diff options
Diffstat (limited to 'alacritty_terminal/src/grid/mod.rs')
-rw-r--r-- | alacritty_terminal/src/grid/mod.rs | 506 |
1 files changed, 175 insertions, 331 deletions
diff --git a/alacritty_terminal/src/grid/mod.rs b/alacritty_terminal/src/grid/mod.rs index 4b3c86dc..7949489a 100644 --- a/alacritty_terminal/src/grid/mod.rs +++ b/alacritty_terminal/src/grid/mod.rs @@ -1,7 +1,8 @@ //! A specialized 2D grid implementation optimized for use in a terminal. use std::cmp::{max, min}; -use std::ops::{Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo}; +use std::iter::{Map, TakeWhile}; +use std::ops::{Bound, Deref, Index, IndexMut, Range, RangeBounds, RangeInclusive}; use serde::{Deserialize, Serialize}; @@ -18,37 +19,6 @@ mod tests; pub use self::row::Row; use self::storage::Storage; -/// Bidirectional iterator. -pub trait BidirectionalIterator: Iterator { - fn prev(&mut self) -> Option<Self::Item>; -} - -/// An item in the grid along with its Line and Column. -pub struct Indexed<T> { - pub inner: T, - pub line: Line, - pub column: Column, -} - -impl<T> Deref for Indexed<T> { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - &self.inner - } -} - -impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> { - fn eq(&self, other: &Self) -> bool { - // Compare struct fields and check result of grid comparison. - self.raw.eq(&other.raw) - && self.cols.eq(&other.cols) - && self.lines.eq(&other.lines) - && self.display_offset.eq(&other.display_offset) - } -} - pub trait GridCell: Sized { /// Check if the cell contains any content. fn is_empty(&self) -> bool; @@ -99,6 +69,15 @@ impl IndexMut<CharsetIndex> for Charsets { } } +#[derive(Debug, Copy, Clone)] +pub enum Scroll { + Delta(isize), + PageUp, + PageDown, + Top, + Bottom, +} + /// Grid based terminal content storage. /// /// ```notrust @@ -157,15 +136,6 @@ pub struct Grid<T> { max_scroll_limit: usize, } -#[derive(Debug, Copy, Clone)] -pub enum Scroll { - Delta(isize), - PageUp, - PageDown, - Top, - Bottom, -} - impl<T: GridCell + Default + PartialEq + Clone> Grid<T> { pub fn new(lines: Line, cols: Column, max_scroll_limit: usize) -> Grid<T> { Grid { @@ -341,15 +311,15 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> { D: PartialEq, { // Determine how many lines to scroll up by. - let end = Point { line: 0, col: self.cols() }; + let end = Point { line: 0, column: self.cols() }; let mut iter = self.iter_from(end); while let Some(cell) = iter.prev() { - if !cell.is_empty() || iter.cur.line >= *self.lines { + if !cell.is_empty() || cell.point.line >= *self.lines { break; } } - debug_assert!(iter.cur.line <= *self.lines); - let positions = self.lines - iter.cur.line; + debug_assert!(iter.point.line <= *self.lines); + let positions = self.lines - iter.point.line; let region = Line(0)..self.screen_lines(); // Reset display offset. @@ -383,8 +353,33 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> { } } -#[allow(clippy::len_without_is_empty)] impl<T> Grid<T> { + /// Reset a visible region within the grid. + pub fn reset_region<D, R: RangeBounds<Line>>(&mut self, bounds: R) + where + T: ResetDiscriminant<D> + GridCell + Clone + Default, + D: PartialEq, + { + let start = match bounds.start_bound() { + Bound::Included(line) => *line, + Bound::Excluded(line) => *line + 1, + Bound::Unbounded => Line(0), + }; + + let end = match bounds.end_bound() { + Bound::Included(line) => *line + 1, + Bound::Excluded(line) => *line, + Bound::Unbounded => self.screen_lines(), + }; + + debug_assert!(start < self.screen_lines()); + debug_assert!(end <= self.screen_lines()); + + for row in start.0..end.0 { + self.raw[Line(row)].reset(&self.cursor.template); + } + } + /// Clamp a buffer point to the visible region. pub fn clamp_buffer_to_visible(&self, point: Point<usize>) -> Point { if point.line < self.display_offset { @@ -424,12 +419,7 @@ impl<T> Grid<T> { /// Convert viewport relative point to global buffer indexing. #[inline] pub fn visible_to_buffer(&self, point: Point) -> Point<usize> { - Point { line: self.lines.0 + self.display_offset - point.line.0 - 1, col: point.col } - } - - #[inline] - pub fn display_iter(&self) -> DisplayIter<'_, T> { - DisplayIter::new(self) + Point { line: self.lines.0 + self.display_offset - point.line.0 - 1, column: point.column } } #[inline] @@ -459,126 +449,51 @@ impl<T> Grid<T> { #[inline] pub fn iter_from(&self, point: Point<usize>) -> GridIterator<'_, T> { - GridIterator { grid: self, cur: point } - } - - #[inline] - pub fn display_offset(&self) -> usize { - self.display_offset + GridIterator { grid: self, point } } + /// Iterator over all visible cells. #[inline] - pub fn cursor_cell(&mut self) -> &mut T { - let point = self.cursor.point; - &mut self[&point] - } -} - -/// Grid dimensions. -pub trait Dimensions { - /// Total number of lines in the buffer, this includes scrollback and visible lines. - fn total_lines(&self) -> usize; + pub fn display_iter(&self) -> DisplayIter<'_, T> { + let start = Point::new(self.display_offset + self.lines.0, self.cols() - 1); + let end = Point::new(self.display_offset, self.cols()); - /// Height of the viewport in lines. - fn screen_lines(&self) -> Line; + let iter = GridIterator { grid: self, point: start }; - /// Width of the terminal in columns. - fn cols(&self) -> Column; + let display_offset = self.display_offset; + let lines = self.lines.0; - /// Number of invisible lines part of the scrollback history. - #[inline] - fn history_size(&self) -> usize { - self.total_lines() - self.screen_lines().0 - } -} - -impl<G> Dimensions for Grid<G> { - #[inline] - fn total_lines(&self) -> usize { - self.raw.len() + let take_while: DisplayIterTakeFun<'_, T> = + Box::new(move |indexed: &Indexed<&T>| indexed.point <= end); + let map: DisplayIterMapFun<'_, T> = Box::new(move |indexed: Indexed<&T>| { + let line = Line(lines + display_offset - indexed.point.line - 1); + Indexed { point: Point::new(line, indexed.point.column), cell: indexed.cell } + }); + iter.take_while(take_while).map(map) } #[inline] - fn screen_lines(&self) -> Line { - self.lines + pub fn display_offset(&self) -> usize { + self.display_offset } #[inline] - fn cols(&self) -> Column { - self.cols - } -} - -#[cfg(test)] -impl Dimensions for (Line, Column) { - fn total_lines(&self) -> usize { - *self.0 - } - - fn screen_lines(&self) -> Line { - self.0 - } - - fn cols(&self) -> Column { - self.1 - } -} - -pub struct GridIterator<'a, T> { - /// Immutable grid reference. - grid: &'a Grid<T>, - - /// Current position of the iterator within the grid. - cur: Point<usize>, -} - -impl<'a, T> GridIterator<'a, T> { - pub fn point(&self) -> Point<usize> { - self.cur - } - - pub fn cell(&self) -> &'a T { - &self.grid[self.cur] - } -} - -impl<'a, T> Iterator for GridIterator<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<Self::Item> { - let last_col = self.grid.cols() - 1; - - match self.cur { - Point { line, col } if line == 0 && col == last_col => return None, - Point { col, .. } if (col == last_col) => { - self.cur.line -= 1; - self.cur.col = Column(0); - }, - _ => self.cur.col += Column(1), - } - - Some(&self.grid[self.cur]) + pub fn cursor_cell(&mut self) -> &mut T { + let point = self.cursor.point; + &mut self[point.line][point.column] } } -impl<'a, T> BidirectionalIterator for GridIterator<'a, T> { - fn prev(&mut self) -> Option<Self::Item> { - let last_col = self.grid.cols() - 1; - - match self.cur { - Point { line, col: Column(0) } if line == self.grid.total_lines() - 1 => return None, - Point { col: Column(0), .. } => { - self.cur.line += 1; - self.cur.col = last_col; - }, - _ => self.cur.col -= Column(1), - } - - Some(&self.grid[self.cur]) +impl<T: PartialEq> PartialEq for Grid<T> { + fn eq(&self, other: &Self) -> bool { + // Compare struct fields and check result of grid comparison. + self.raw.eq(&other.raw) + && self.cols.eq(&other.cols) + && self.lines.eq(&other.lines) + && self.display_offset.eq(&other.display_offset) } } -/// Index active region by line. impl<T> Index<Line> for Grid<T> { type Output = Row<T>; @@ -588,16 +503,6 @@ impl<T> Index<Line> for Grid<T> { } } -/// Index with buffer offset. -impl<T> Index<usize> for Grid<T> { - type Output = Row<T>; - - #[inline] - fn index(&self, index: usize) -> &Row<T> { - &self.raw[index] - } -} - impl<T> IndexMut<Line> for Grid<T> { #[inline] fn index_mut(&mut self, index: Line) -> &mut Row<T> { @@ -605,26 +510,19 @@ impl<T> IndexMut<Line> for Grid<T> { } } -impl<T> IndexMut<usize> for Grid<T> { - #[inline] - fn index_mut(&mut self, index: usize) -> &mut Row<T> { - &mut self.raw[index] - } -} - -impl<'point, T> Index<&'point Point> for Grid<T> { - type Output = T; +impl<T> Index<usize> for Grid<T> { + type Output = Row<T>; #[inline] - fn index<'a>(&'a self, point: &Point) -> &'a T { - &self[point.line][point.col] + fn index(&self, index: usize) -> &Row<T> { + &self.raw[index] } } -impl<'point, T> IndexMut<&'point Point> for Grid<T> { +impl<T> IndexMut<usize> for Grid<T> { #[inline] - fn index_mut<'a, 'b>(&'a mut self, point: &'b Point) -> &'a mut T { - &mut self[point.line][point.col] + fn index_mut(&mut self, index: usize) -> &mut Row<T> { + &mut self.raw[index] } } @@ -633,216 +531,162 @@ impl<T> Index<Point<usize>> for Grid<T> { #[inline] fn index(&self, point: Point<usize>) -> &T { - &self[point.line][point.col] + &self[point.line][point.column] } } impl<T> IndexMut<Point<usize>> for Grid<T> { #[inline] fn index_mut(&mut self, point: Point<usize>) -> &mut T { - &mut self[point.line][point.col] + &mut self[point.line][point.column] } } -/// A subset of lines in the grid. -/// -/// May be constructed using Grid::region(..). -pub struct Region<'a, T> { - start: Line, - end: Line, - raw: &'a Storage<T>, -} +impl<T> Index<Point> for Grid<T> { + type Output = T; -/// A mutable subset of lines in the grid. -/// -/// May be constructed using Grid::region_mut(..). -pub struct RegionMut<'a, T> { - start: Line, - end: Line, - raw: &'a mut Storage<T>, + #[inline] + fn index(&self, point: Point) -> &T { + &self[point.line][point.column] + } } -impl<'a, T> RegionMut<'a, T> { - /// Call the provided function for every item in this region. - pub fn each<F: Fn(&mut T)>(self, func: F) { - for row in self { - for item in row { - func(item) - } - } +impl<T> IndexMut<Point> for Grid<T> { + #[inline] + fn index_mut(&mut self, point: Point) -> &mut T { + &mut self[point.line][point.column] } } -pub trait IndexRegion<I, T> { - /// Get an immutable region of Self. - fn region(&self, _: I) -> Region<'_, T>; +/// Grid dimensions. +pub trait Dimensions { + /// Total number of lines in the buffer, this includes scrollback and visible lines. + fn total_lines(&self) -> usize; - /// Get a mutable region of Self. - fn region_mut(&mut self, _: I) -> RegionMut<'_, T>; -} + /// Height of the viewport in lines. + fn screen_lines(&self) -> Line; -impl<T> IndexRegion<Range<Line>, T> for Grid<T> { - fn region(&self, index: Range<Line>) -> Region<'_, T> { - assert!(index.start < self.screen_lines()); - assert!(index.end <= self.screen_lines()); - assert!(index.start <= index.end); - Region { start: index.start, end: index.end, raw: &self.raw } - } + /// Width of the terminal in columns. + fn cols(&self) -> Column; - fn region_mut(&mut self, index: Range<Line>) -> RegionMut<'_, T> { - assert!(index.start < self.screen_lines()); - assert!(index.end <= self.screen_lines()); - assert!(index.start <= index.end); - RegionMut { start: index.start, end: index.end, raw: &mut self.raw } + /// Number of invisible lines part of the scrollback history. + #[inline] + fn history_size(&self) -> usize { + self.total_lines() - self.screen_lines().0 } } -impl<T> IndexRegion<RangeTo<Line>, T> for Grid<T> { - fn region(&self, index: RangeTo<Line>) -> Region<'_, T> { - assert!(index.end <= self.screen_lines()); - Region { start: Line(0), end: index.end, raw: &self.raw } - } - - fn region_mut(&mut self, index: RangeTo<Line>) -> RegionMut<'_, T> { - assert!(index.end <= self.screen_lines()); - RegionMut { start: Line(0), end: index.end, raw: &mut self.raw } +impl<G> Dimensions for Grid<G> { + #[inline] + fn total_lines(&self) -> usize { + self.raw.len() } -} -impl<T> IndexRegion<RangeFrom<Line>, T> for Grid<T> { - fn region(&self, index: RangeFrom<Line>) -> Region<'_, T> { - assert!(index.start < self.screen_lines()); - Region { start: index.start, end: self.screen_lines(), raw: &self.raw } + #[inline] + fn screen_lines(&self) -> Line { + self.lines } - fn region_mut(&mut self, index: RangeFrom<Line>) -> RegionMut<'_, T> { - assert!(index.start < self.screen_lines()); - RegionMut { start: index.start, end: self.screen_lines(), raw: &mut self.raw } + #[inline] + fn cols(&self) -> Column { + self.cols } } -impl<T> IndexRegion<RangeFull, T> for Grid<T> { - fn region(&self, _: RangeFull) -> Region<'_, T> { - Region { start: Line(0), end: self.screen_lines(), raw: &self.raw } - } - - fn region_mut(&mut self, _: RangeFull) -> RegionMut<'_, T> { - RegionMut { start: Line(0), end: self.screen_lines(), raw: &mut self.raw } +#[cfg(test)] +impl Dimensions for (Line, Column) { + fn total_lines(&self) -> usize { + *self.0 } -} - -pub struct RegionIter<'a, T> { - end: Line, - cur: Line, - raw: &'a Storage<T>, -} -pub struct RegionIterMut<'a, T> { - end: Line, - cur: Line, - raw: &'a mut Storage<T>, -} - -impl<'a, T> IntoIterator for Region<'a, T> { - type IntoIter = RegionIter<'a, T>; - type Item = &'a Row<T>; - - fn into_iter(self) -> Self::IntoIter { - RegionIter { end: self.end, cur: self.start, raw: self.raw } + fn screen_lines(&self) -> Line { + self.0 } -} -impl<'a, T> IntoIterator for RegionMut<'a, T> { - type IntoIter = RegionIterMut<'a, T>; - type Item = &'a mut Row<T>; - - fn into_iter(self) -> Self::IntoIter { - RegionIterMut { end: self.end, cur: self.start, raw: self.raw } + fn cols(&self) -> Column { + self.1 } } -impl<'a, T> Iterator for RegionIter<'a, T> { - type Item = &'a Row<T>; - - fn next(&mut self) -> Option<Self::Item> { - if self.cur < self.end { - let index = self.cur; - self.cur += 1; - Some(&self.raw[index]) - } else { - None - } - } +#[derive(Debug, PartialEq)] +pub struct Indexed<T, L = usize> { + pub point: Point<L>, + pub cell: T, } -impl<'a, T> Iterator for RegionIterMut<'a, T> { - type Item = &'a mut Row<T>; +impl<T, L> Deref for Indexed<T, L> { + type Target = T; - fn next(&mut self) -> Option<Self::Item> { - if self.cur < self.end { - let index = self.cur; - self.cur += 1; - unsafe { Some(&mut *(&mut self.raw[index] as *mut _)) } - } else { - None - } + #[inline] + fn deref(&self) -> &T { + &self.cell } } -/// Iterates over the visible area accounting for buffer transform. -pub struct DisplayIter<'a, T> { +/// Grid cell iterator. +pub struct GridIterator<'a, T> { + /// Immutable grid reference. grid: &'a Grid<T>, - offset: usize, - limit: usize, - col: Column, - line: Line, -} -impl<'a, T: 'a> DisplayIter<'a, T> { - pub fn new(grid: &'a Grid<T>) -> DisplayIter<'a, T> { - let offset = grid.display_offset + *grid.screen_lines() - 1; - let limit = grid.display_offset; - let col = Column(0); - let line = Line(0); - - DisplayIter { grid, offset, col, limit, line } - } + /// Current position of the iterator within the grid. + point: Point<usize>, +} - pub fn offset(&self) -> usize { - self.offset +impl<'a, T> GridIterator<'a, T> { + /// Current iteratior position. + pub fn point(&self) -> Point<usize> { + self.point } - pub fn point(&self) -> Point { - Point::new(self.line, self.col) + /// Cell at the current iteratior position. + pub fn cell(&self) -> &'a T { + &self.grid[self.point] } } -impl<'a, T: 'a> Iterator for DisplayIter<'a, T> { +impl<'a, T> Iterator for GridIterator<'a, T> { type Item = Indexed<&'a T>; - #[inline] fn next(&mut self) -> Option<Self::Item> { - // Return None if we've reached the end. - if self.offset == self.limit && self.grid.cols() == self.col { - return None; + let last_col = self.grid.cols() - 1; + match self.point { + Point { line, column: col } if line == 0 && col == last_col => return None, + Point { column: col, .. } if (col == last_col) => { + self.point.line -= 1; + self.point.column = Column(0); + }, + _ => self.point.column += Column(1), } - // Get the next item. - let item = Some(Indexed { - inner: &self.grid.raw[self.offset][self.col], - line: self.line, - column: self.col, - }); + Some(Indexed { cell: &self.grid[self.point], point: self.point }) + } +} + +/// Bidirectional iterator. +pub trait BidirectionalIterator: Iterator { + fn prev(&mut self) -> Option<Self::Item>; +} - // Update line/col to point to next item. - self.col += 1; - if self.col == self.grid.cols() && self.offset != self.limit { - self.offset -= 1; +impl<'a, T> BidirectionalIterator for GridIterator<'a, T> { + fn prev(&mut self) -> Option<Self::Item> { + let last_col = self.grid.cols() - 1; - self.col = Column(0); - self.line = Line(*self.grid.lines - 1 - (self.offset - self.limit)); + match self.point { + Point { line, column: Column(0) } if line == self.grid.total_lines() - 1 => { + return None + }, + Point { column: Column(0), .. } => { + self.point.line += 1; + self.point.column = last_col; + }, + _ => self.point.column -= Column(1), } - item + Some(Indexed { cell: &self.grid[self.point], point: self.point }) } } + +pub type DisplayIter<'a, T> = + Map<TakeWhile<GridIterator<'a, T>, DisplayIterTakeFun<'a, T>>, DisplayIterMapFun<'a, T>>; +type DisplayIterTakeFun<'a, T> = Box<dyn Fn(&Indexed<&'a T>) -> bool>; +type DisplayIterMapFun<'a, T> = Box<dyn FnMut(Indexed<&'a T>) -> Indexed<&'a T, Line>>; |