summaryrefslogtreecommitdiff
path: root/alacritty_terminal/src/grid/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty_terminal/src/grid/mod.rs')
-rw-r--r--alacritty_terminal/src/grid/mod.rs506
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>>;