summaryrefslogtreecommitdiff
path: root/alacritty_terminal/src/grid/mod.rs
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2021-01-24 21:45:36 +0000
committerGitHub <noreply@github.com>2021-01-24 21:45:36 +0000
commit530de00049c2afcc562d36ccdb3e6afa2fe396a5 (patch)
tree3dabbcef3fc4a2041f9027d82243aa0d70928153 /alacritty_terminal/src/grid/mod.rs
parent7291702f6b4fff10f2470f084abe0785b95659a0 (diff)
downloadalacritty-530de00049c2afcc562d36ccdb3e6afa2fe396a5.tar.gz
alacritty-530de00049c2afcc562d36ccdb3e6afa2fe396a5.zip
Move renderable cell transformation to alacritty
This refactors a large chunk of the alacritty_terminal API to expose all data necessary for rendering uniformly through the `renderable_content` call. This also no longer transforms the cells for rendering by a GUI but instead just reports the content from a terminal emulation perspective. The transformation into renderable cells is now done inside the alacritty crate. Since the terminal itself only ever needs to know about modified color RGB values, the configuration for colors was moved to the alacritty UI code.
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>>;