use crate::cell::Cell;
use crate::grid::{Grid, Row};
use crate::selection::BufferPoint;
use super::Term;
impl Term {
pub(super) fn line_in<'a>(&'a self, grid: &'a Grid, line: usize) -> &'a [Cell] {
if line < self.scrollback.len() {
&self.scrollback[line]
} else {
grid.row(line - self.scrollback.len())
}
}
pub(super) fn row_in<'a>(&'a self, grid: &'a Grid, line: usize) -> &'a Row {
if line < self.scrollback.len() {
&self.scrollback[line]
} else {
grid.row_ref(line - self.scrollback.len())
}
}
pub(super) fn abs_line(&self, line: usize) -> &[Cell] {
self.line_in(&self.grid, line)
}
pub(super) fn abs_row(&self, line: usize) -> &Row {
self.row_in(&self.grid, line)
}
fn combining_in<'a>(&'a self, grid: &'a Grid, line: usize, col: usize) -> Option<&'a [char]> {
self.row_in(grid, line).combining_at(col)
}
pub(super) fn combining_at(&self, line: usize, col: usize) -> Option<&[char]> {
self.combining_in(&self.grid, line, col)
}
pub(super) fn append_cell(&self, grid: &Grid, out: &mut String, line: usize, col: usize) {
let cell = &self.line_in(grid, line)[col];
if cell.is_spacer() {
return;
}
out.push(cell.c());
if let Some(marks) = self.combining_in(grid, line, col) {
out.extend(marks);
}
}
pub(super) fn extract_lines(
&self,
grid: &Grid,
start_line: usize,
from: usize,
end_line: usize,
to_end: usize,
) -> String {
let (start_line, from) =
if from >= self.line_in(grid, start_line).len() && start_line < end_line {
(start_line + 1, 0)
} else {
(start_line, from)
};
let mut out = String::new();
let mut current = String::new();
for line in start_line..=end_line {
let cells = self.line_in(grid, line);
let left = if line == start_line { from } else { 0 };
let right = if line == end_line {
to_end.min(cells.len())
} else {
cells.len()
};
let right = right.max(left);
for col in left..right {
self.append_cell(grid, &mut current, line, col);
}
let is_last = line == end_line;
let soft = self.row_in(grid, line).is_wrapped();
if is_last || !soft {
out.push_str(current.trim_end());
current.clear();
if !is_last {
out.push('\n');
}
}
}
out
}
pub(super) fn abs_floor(&self) -> usize {
if self.on_alt {
self.scrollback.len()
} else {
0
}
}
fn prev_pos(&self, line: usize, col: usize) -> Option<(usize, usize)> {
if col > 0 {
return Some((line, col - 1));
}
if line > self.abs_floor() {
let prev = self.abs_line(line - 1);
if self.abs_row(line - 1).is_wrapped() {
return Some((line - 1, prev.len() - 1));
}
}
None
}
fn next_pos(&self, line: usize, col: usize) -> Option<(usize, usize)> {
let cells = self.abs_line(line);
if col + 1 < cells.len() {
return Some((line, col + 1));
}
let total = self.scrollback.len() + self.grid.rows();
if line >= self.abs_floor() && line + 1 < total && self.abs_row(line).is_wrapped() {
return Some((line + 1, 0));
}
None
}
fn is_wrap_artefact(&self, line: usize, col: usize) -> bool {
let cells = self.abs_line(line);
cells[col].is_leading_spacer() && col + 1 == cells.len()
}
fn is_walk_transparent_spacer(&self, line: usize, col: usize) -> bool {
if self.is_wrap_artefact(line, col) {
return true;
}
let cells = self.abs_line(line);
cells[col].is_wide_spacer()
&& col > 0
&& cells[col - 1].is_wide()
&& !is_word_boundary(cells[col - 1].c())
}
pub(super) fn word_start(&self, p: BufferPoint) -> BufferPoint {
let cells = self.abs_line(p.line);
let (mut line, mut col) = (p.line, p.col.min(cells.len().saturating_sub(1)));
while let Some((pl, pc)) = self.prev_pos(line, col) {
if !self.is_walk_transparent_spacer(pl, pc)
&& is_word_boundary(self.abs_line(pl)[pc].c())
{
break;
}
line = pl;
col = pc;
}
BufferPoint { line, col }
}
pub(super) fn word_end(&self, p: BufferPoint) -> BufferPoint {
let cells = self.abs_line(p.line);
let (mut line, mut col) = (p.line, p.col.min(cells.len().saturating_sub(1)));
while let Some((nl, nc)) = self.next_pos(line, col) {
if !self.is_walk_transparent_spacer(nl, nc)
&& is_word_boundary(self.abs_line(nl)[nc].c())
{
break; }
line = nl;
col = nc;
}
BufferPoint { line, col }
}
}
fn is_word_boundary(c: char) -> bool {
c.is_whitespace() || ",│`|:\"'()[]{}<>".contains(c)
}