summaryrefslogtreecommitdiff
path: root/alacritty_terminal/src/grid
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2021-03-30 23:25:38 +0000
committerGitHub <noreply@github.com>2021-03-30 23:25:38 +0000
commit3bd5ac221ab3b122962063edd1f4c10f9f2d117f (patch)
treeb0a367b91611e911344aec9ff35354e5a473b6aa /alacritty_terminal/src/grid
parent974392cdc6fdf1ba0d213145ae578a9316e9d404 (diff)
downloadalacritty-3bd5ac221ab3b122962063edd1f4c10f9f2d117f.tar.gz
alacritty-3bd5ac221ab3b122962063edd1f4c10f9f2d117f.zip
Unify the grid line indexing types
Previously Alacritty was using two different ways to reference lines in the terminal. Either a `usize`, or a `Line(usize)`. These indexing systems both served different purposes, but made it difficult to reason about logic involving these systems because of its inconsistency. To resolve this issue, a single new `Line(i32)` type has been introduced. All existing references to lines and points now rely on this definition of a line. The indexing starts at the top of the terminal region with the line 0, which matches the line 1 used by escape sequences. Each line in the history becomes increasingly negative and the bottommost line is equal to the number of visible lines minus one. Having a system which goes into the negatives allows following the escape sequence's indexing system closely, while at the same time making it trivial to implement `Ord` for points. The Alacritty UI crate is the only place which has a different indexing system, since rendering and input puts the zero line at the top of the viewport, rather than the top of the terminal region. All instances which refer to a number of lines/columns instead of just a single Line/Column have also been changed to use a `usize` instead. This way a Line/Column will always refer to a specific place in the grid and no confusion is created by having a count of lines as a possible index into the grid storage.
Diffstat (limited to 'alacritty_terminal/src/grid')
-rw-r--r--alacritty_terminal/src/grid/mod.rs302
-rw-r--r--alacritty_terminal/src/grid/resize.rs134
-rw-r--r--alacritty_terminal/src/grid/row.rs24
-rw-r--r--alacritty_terminal/src/grid/storage.rs156
-rw-r--r--alacritty_terminal/src/grid/tests.rs242
5 files changed, 384 insertions, 474 deletions
diff --git a/alacritty_terminal/src/grid/mod.rs b/alacritty_terminal/src/grid/mod.rs
index 7949489a..169067af 100644
--- a/alacritty_terminal/src/grid/mod.rs
+++ b/alacritty_terminal/src/grid/mod.rs
@@ -1,13 +1,13 @@
//! A specialized 2D grid implementation optimized for use in a terminal.
use std::cmp::{max, min};
-use std::iter::{Map, TakeWhile};
-use std::ops::{Bound, Deref, Index, IndexMut, Range, RangeBounds, RangeInclusive};
+use std::iter::TakeWhile;
+use std::ops::{Bound, Deref, Index, IndexMut, Range, RangeBounds};
use serde::{Deserialize, Serialize};
use crate::ansi::{CharsetIndex, StandardCharset};
-use crate::index::{Column, IndexRange, Line, Point};
+use crate::index::{Column, Line, Point};
use crate::term::cell::{Flags, ResetDiscriminant};
pub mod resize;
@@ -71,7 +71,7 @@ impl IndexMut<CharsetIndex> for Charsets {
#[derive(Debug, Copy, Clone)]
pub enum Scroll {
- Delta(isize),
+ Delta(i32),
PageUp,
PageDown,
Top,
@@ -103,7 +103,7 @@ pub enum Scroll {
/// │ │
/// └─────────────────────────┘ <-- zero
/// ^
-/// cols
+/// columns
/// ```
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Grid<T> {
@@ -120,10 +120,10 @@ pub struct Grid<T> {
raw: Storage<T>,
/// Number of columns.
- cols: Column,
+ columns: usize,
/// Number of visible lines.
- lines: Line,
+ lines: usize,
/// Offset of displayed area.
///
@@ -137,15 +137,15 @@ pub struct Grid<T> {
}
impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
- pub fn new(lines: Line, cols: Column, max_scroll_limit: usize) -> Grid<T> {
+ pub fn new(lines: usize, columns: usize, max_scroll_limit: usize) -> Grid<T> {
Grid {
- raw: Storage::with_capacity(lines, cols),
+ raw: Storage::with_capacity(lines, columns),
max_scroll_limit,
display_offset: 0,
saved_cursor: Cursor::default(),
cursor: Cursor::default(),
lines,
- cols,
+ columns,
}
}
@@ -161,12 +161,11 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
pub fn scroll_display(&mut self, scroll: Scroll) {
self.display_offset = match scroll {
- Scroll::Delta(count) => min(
- max((self.display_offset as isize) + count, 0isize) as usize,
- self.history_size(),
- ),
- Scroll::PageUp => min(self.display_offset + self.lines.0, self.history_size()),
- Scroll::PageDown => self.display_offset.saturating_sub(self.lines.0),
+ Scroll::Delta(count) => {
+ min(max((self.display_offset as i32) + count, 0) as usize, self.history_size())
+ },
+ Scroll::PageUp => min(self.display_offset + self.lines, self.history_size()),
+ Scroll::PageDown => self.display_offset.saturating_sub(self.lines),
Scroll::Top => self.history_size(),
Scroll::Bottom => 0,
};
@@ -175,7 +174,7 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
fn increase_scroll_limit(&mut self, count: usize) {
let count = min(count, self.max_scroll_limit - self.history_size());
if count != 0 {
- self.raw.initialize(count, self.cols);
+ self.raw.initialize(count, self.columns);
}
}
@@ -188,18 +187,15 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
}
#[inline]
- pub fn scroll_down<D>(&mut self, region: &Range<Line>, positions: Line)
+ pub fn scroll_down<D>(&mut self, region: &Range<Line>, positions: usize)
where
T: ResetDiscriminant<D>,
D: PartialEq,
{
- let screen_lines = self.screen_lines().0;
-
// When rotating the entire region, just reset everything.
- if positions >= region.end - region.start {
- for i in region.start.0..region.end.0 {
- let index = screen_lines - i - 1;
- self.raw[index].reset(&self.cursor.template);
+ if region.end - region.start <= positions {
+ for i in (region.start.0..region.end.0).map(Line::from) {
+ self.raw[i].reset(&self.cursor.template);
}
return;
@@ -218,32 +214,32 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
//
// We need to start from the top, to make sure the fixed lines aren't swapped with each
// other.
- let fixed_lines = screen_lines - region.end.0;
- for i in (0..fixed_lines).rev() {
- self.raw.swap(i, i + positions.0);
+ let screen_lines = self.screen_lines() as i32;
+ for i in (region.end.0..screen_lines).map(Line::from) {
+ self.raw.swap(i, i - positions as i32);
}
// Rotate the entire line buffer downward.
- self.raw.rotate_down(*positions);
+ self.raw.rotate_down(positions);
// Ensure all new lines are fully cleared.
- for i in 0..positions.0 {
- let index = screen_lines - i - 1;
- self.raw[index].reset(&self.cursor.template);
+ for i in (0..positions).map(Line::from) {
+ self.raw[i].reset(&self.cursor.template);
}
// Swap the fixed lines at the top back into position.
- for i in 0..region.start.0 {
- let index = screen_lines - i - 1;
- self.raw.swap(index, index - positions.0);
+ for i in (0..region.start.0).map(Line::from) {
+ self.raw.swap(i, i + positions);
}
} else {
// Subregion rotation.
- for line in IndexRange((region.start + positions)..region.end).rev() {
- self.raw.swap_lines(line, line - positions);
+ let range = (region.start + positions).0..region.end.0;
+ for line in range.rev().map(Line::from) {
+ self.raw.swap(line, line - positions);
}
- for line in IndexRange(region.start..(region.start + positions)) {
+ let range = region.start.0..(region.start + positions).0;
+ for line in range.rev().map(Line::from) {
self.raw[line].reset(&self.cursor.template);
}
}
@@ -252,18 +248,15 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
/// Move lines at the bottom toward the top.
///
/// This is the performance-sensitive part of scrolling.
- pub fn scroll_up<D>(&mut self, region: &Range<Line>, positions: Line)
+ pub fn scroll_up<D>(&mut self, region: &Range<Line>, positions: usize)
where
T: ResetDiscriminant<D>,
D: PartialEq,
{
- let screen_lines = self.screen_lines().0;
-
// When rotating the entire region with fixed lines at the top, just reset everything.
- if positions >= region.end - region.start && region.start != Line(0) {
- for i in region.start.0..region.end.0 {
- let index = screen_lines - i - 1;
- self.raw[index].reset(&self.cursor.template);
+ if region.end - region.start <= positions && region.start != 0 {
+ for i in (region.start.0..region.end.0).map(Line::from) {
+ self.raw[i].reset(&self.cursor.template);
}
return;
@@ -271,11 +264,11 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
// Update display offset when not pinned to active area.
if self.display_offset != 0 {
- self.display_offset = min(self.display_offset + *positions, self.max_scroll_limit);
+ self.display_offset = min(self.display_offset + positions, self.max_scroll_limit);
}
// Create scrollback for the new lines.
- self.increase_scroll_limit(*positions);
+ self.increase_scroll_limit(positions);
// Swap the lines fixed at the top to their target positions after rotation.
//
@@ -285,23 +278,22 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
//
// We need to start from the bottom, to make sure the fixed lines aren't swapped with each
// other.
- for i in (0..region.start.0).rev() {
- let index = screen_lines - i - 1;
- self.raw.swap(index, index - positions.0);
+ for i in (0..region.start.0).rev().map(Line::from) {
+ self.raw.swap(i, i + positions);
}
// Rotate the entire line buffer upward.
- self.raw.rotate(-(positions.0 as isize));
+ self.raw.rotate(-(positions as isize));
// Ensure all new lines are fully cleared.
- for i in 0..positions.0 {
+ let screen_lines = self.screen_lines();
+ for i in ((screen_lines - positions)..screen_lines).map(Line::from) {
self.raw[i].reset(&self.cursor.template);
}
// Swap the fixed lines at the bottom back into position.
- let fixed_lines = screen_lines - region.end.0;
- for i in 0..fixed_lines {
- self.raw.swap(i, i + positions.0);
+ for i in (region.end.0..(screen_lines as i32)).rev().map(Line::from) {
+ self.raw.swap(i, i - positions);
}
}
@@ -311,16 +303,16 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
D: PartialEq,
{
// Determine how many lines to scroll up by.
- let end = Point { line: 0, column: self.cols() };
+ let end = Point::new(Line(self.lines as i32 - 1), Column(self.columns()));
let mut iter = self.iter_from(end);
while let Some(cell) = iter.prev() {
- if !cell.is_empty() || cell.point.line >= *self.lines {
+ if !cell.is_empty() || cell.point.line < 0 {
break;
}
}
- debug_assert!(iter.point.line <= *self.lines);
- let positions = self.lines - iter.point.line;
- let region = Line(0)..self.screen_lines();
+ debug_assert!(iter.point.line >= -1);
+ let positions = (iter.point.line.0 + 1) as usize;
+ let region = Line(0)..Line(self.lines as i32);
// Reset display offset.
self.display_offset = 0;
@@ -329,8 +321,8 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
self.scroll_up(&region, positions);
// Reset rotated lines.
- for i in positions.0..self.lines.0 {
- self.raw[i].reset(&self.cursor.template);
+ for line in (0..(self.lines - positions)).map(Line::from) {
+ self.raw[line].reset(&self.cursor.template);
}
}
@@ -347,8 +339,9 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
self.display_offset = 0;
// Reset all visible lines.
- for row in 0..self.raw.len() {
- self.raw[row].reset(&self.cursor.template);
+ let range = self.topmost_line().0..(self.screen_lines() as i32);
+ for line in range.map(Line::from) {
+ self.raw[line].reset(&self.cursor.template);
}
}
}
@@ -369,59 +362,17 @@ impl<T> Grid<T> {
let end = match bounds.end_bound() {
Bound::Included(line) => *line + 1,
Bound::Excluded(line) => *line,
- Bound::Unbounded => self.screen_lines(),
+ Bound::Unbounded => Line(self.screen_lines() as i32),
};
- debug_assert!(start < self.screen_lines());
- debug_assert!(end <= self.screen_lines());
+ debug_assert!(start < self.screen_lines() as i32);
+ debug_assert!(end <= self.screen_lines() as i32);
- for row in start.0..end.0 {
- self.raw[Line(row)].reset(&self.cursor.template);
+ for line in (start.0..end.0).map(Line::from) {
+ self.raw[line].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 {
- Point::new(self.lines - 1, self.cols - 1)
- } else if point.line >= self.display_offset + self.lines.0 {
- Point::new(Line(0), Column(0))
- } else {
- // Since edgecases are handled, conversion is identical as visible to buffer.
- self.visible_to_buffer(point.into()).into()
- }
- }
-
- // Clamp a buffer point based range to the viewport.
- //
- // This will make sure the content within the range is visible and return `None` whenever the
- // entire range is outside the visible region.
- pub fn clamp_buffer_range_to_visible(
- &self,
- range: &RangeInclusive<Point<usize>>,
- ) -> Option<RangeInclusive<Point>> {
- let start = range.start();
- let end = range.end();
-
- // Check if the range is completely offscreen
- let viewport_end = self.display_offset;
- let viewport_start = viewport_end + self.lines.0 - 1;
- if end.line > viewport_start || start.line < viewport_end {
- return None;
- }
-
- let start = self.clamp_buffer_to_visible(*start);
- let end = self.clamp_buffer_to_visible(*end);
-
- Some(start..=end)
- }
-
- /// 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, column: point.column }
- }
-
#[inline]
pub fn clear_history(&mut self) {
// Explicitly purge all lines from history.
@@ -438,7 +389,7 @@ impl<T> Grid<T> {
self.truncate();
// Initialize everything with empty new lines.
- self.raw.initialize(self.max_scroll_limit - self.history_size(), self.cols);
+ self.raw.initialize(self.max_scroll_limit - self.history_size(), self.columns);
}
/// This is used only for truncating before saving ref-tests.
@@ -448,28 +399,21 @@ impl<T> Grid<T> {
}
#[inline]
- pub fn iter_from(&self, point: Point<usize>) -> GridIterator<'_, T> {
+ pub fn iter_from(&self, point: Point) -> GridIterator<'_, T> {
GridIterator { grid: self, point }
}
/// Iterator over all visible cells.
#[inline]
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());
+ let start = Point::new(Line(-(self.display_offset as i32) - 1), self.last_column());
+ let end = Point::new(start.line + self.lines, Column(self.columns));
let iter = GridIterator { grid: self, point: start };
- let display_offset = self.display_offset;
- let lines = self.lines.0;
-
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)
+ iter.take_while(take_while)
}
#[inline]
@@ -488,7 +432,7 @@ 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.columns.eq(&other.columns)
&& self.lines.eq(&other.lines)
&& self.display_offset.eq(&other.display_offset)
}
@@ -510,38 +454,6 @@ impl<T> IndexMut<Line> for Grid<T> {
}
}
-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<usize> for Grid<T> {
- #[inline]
- fn index_mut(&mut self, index: usize) -> &mut Row<T> {
- &mut self.raw[index]
- }
-}
-
-impl<T> Index<Point<usize>> for Grid<T> {
- type Output = T;
-
- #[inline]
- fn index(&self, point: Point<usize>) -> &T {
- &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.column]
- }
-}
-
impl<T> Index<Point> for Grid<T> {
type Output = T;
@@ -564,15 +476,33 @@ pub trait Dimensions {
fn total_lines(&self) -> usize;
/// Height of the viewport in lines.
- fn screen_lines(&self) -> Line;
+ fn screen_lines(&self) -> usize;
/// Width of the terminal in columns.
- fn cols(&self) -> Column;
+ fn columns(&self) -> usize;
+
+ /// Index for the last column.
+ #[inline]
+ fn last_column(&self) -> Column {
+ Column(self.columns() - 1)
+ }
+
+ /// Line farthest up in the grid history.
+ #[inline]
+ fn topmost_line(&self) -> Line {
+ Line(-(self.history_size() as i32))
+ }
+
+ /// Line farthest down in the grid history.
+ #[inline]
+ fn bottommost_line(&self) -> Line {
+ Line(self.screen_lines() as i32 - 1)
+ }
/// Number of invisible lines part of the scrollback history.
#[inline]
fn history_size(&self) -> usize {
- self.total_lines() - self.screen_lines().0
+ self.total_lines() - self.screen_lines()
}
}
@@ -583,38 +513,38 @@ impl<G> Dimensions for Grid<G> {
}
#[inline]
- fn screen_lines(&self) -> Line {
+ fn screen_lines(&self) -> usize {
self.lines
}
#[inline]
- fn cols(&self) -> Column {
- self.cols
+ fn columns(&self) -> usize {
+ self.columns
}
}
#[cfg(test)]
-impl Dimensions for (Line, Column) {
+impl Dimensions for (usize, usize) {
fn total_lines(&self) -> usize {
- *self.0
+ self.0
}
- fn screen_lines(&self) -> Line {
+ fn screen_lines(&self) -> usize {
self.0
}
- fn cols(&self) -> Column {
+ fn columns(&self) -> usize {
self.1
}
}
#[derive(Debug, PartialEq)]
-pub struct Indexed<T, L = usize> {
- pub point: Point<L>,
+pub struct Indexed<T> {
+ pub point: Point,
pub cell: T,
}
-impl<T, L> Deref for Indexed<T, L> {
+impl<T> Deref for Indexed<T> {
type Target = T;
#[inline]
@@ -629,12 +559,12 @@ pub struct GridIterator<'a, T> {
grid: &'a Grid<T>,
/// Current position of the iterator within the grid.
- point: Point<usize>,
+ point: Point,
}
impl<'a, T> GridIterator<'a, T> {
/// Current iteratior position.
- pub fn point(&self) -> Point<usize> {
+ pub fn point(&self) -> Point {
self.point
}
@@ -648,12 +578,17 @@ impl<'a, T> Iterator for GridIterator<'a, T> {
type Item = Indexed<&'a T>;
fn next(&mut self) -> Option<Self::Item> {
- let last_col = self.grid.cols() - 1;
+ let last_column = self.grid.last_column();
+
+ // Stop once we've reached the end of the grid.
+ if self.point == Point::new(self.grid.bottommost_line(), last_column) {
+ return None;
+ }
+
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;
+ Point { column, .. } if column == last_column => {
self.point.column = Column(0);
+ self.point.line += 1;
},
_ => self.point.column += Column(1),
}
@@ -669,15 +604,18 @@ pub trait BidirectionalIterator: Iterator {
impl<'a, T> BidirectionalIterator for GridIterator<'a, T> {
fn prev(&mut self) -> Option<Self::Item> {
- let last_col = self.grid.cols() - 1;
+ let topmost_line = self.grid.topmost_line();
+ let last_column = self.grid.last_column();
+
+ // Stop once we've reached the end of the grid.
+ if self.point == Point::new(topmost_line, Column(0)) {
+ return None;
+ }
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 = last_column;
+ self.point.line -= 1;
},
_ => self.point.column -= Column(1),
}
@@ -686,7 +624,5 @@ impl<'a, T> BidirectionalIterator for GridIterator<'a, T> {
}
}
-pub type DisplayIter<'a, T> =
- Map<TakeWhile<GridIterator<'a, T>, DisplayIterTakeFun<'a, T>>, DisplayIterMapFun<'a, T>>;
+pub type DisplayIter<'a, T> = TakeWhile<GridIterator<'a, T>, DisplayIterTakeFun<'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>>;
diff --git a/alacritty_terminal/src/grid/resize.rs b/alacritty_terminal/src/grid/resize.rs
index 40492c3a..eb8bef0c 100644
--- a/alacritty_terminal/src/grid/resize.rs
+++ b/alacritty_terminal/src/grid/resize.rs
@@ -1,9 +1,9 @@
//! Grid resize and reflow.
-use std::cmp::{min, Ordering};
+use std::cmp::{max, min, Ordering};
use std::mem;
-use crate::index::{Column, Line};
+use crate::index::{Boundary, Column, Line};
use crate::term::cell::{Flags, ResetDiscriminant};
use crate::grid::row::Row;
@@ -11,7 +11,7 @@ use crate::grid::{Dimensions, Grid, GridCell};
impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
/// Resize the grid's width and/or height.
- pub fn resize<D>(&mut self, reflow: bool, lines: Line, cols: Column)
+ pub fn resize<D>(&mut self, reflow: bool, lines: usize, columns: usize)
where
T: ResetDiscriminant<D>,
D: PartialEq,
@@ -25,9 +25,9 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
Ordering::Equal => (),
}
- match self.cols.cmp(&cols) {
- Ordering::Less => self.grow_cols(reflow, cols),
- Ordering::Greater => self.shrink_cols(reflow, cols),
+ match self.columns.cmp(&columns) {
+ Ordering::Less => self.grow_columns(reflow, columns),
+ Ordering::Greater => self.shrink_columns(reflow, columns),
Ordering::Equal => (),
}
@@ -40,32 +40,32 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
/// Alacritty keeps the cursor at the bottom of the terminal as long as there
/// is scrollback available. Once scrollback is exhausted, new lines are
/// simply added to the bottom of the screen.
- fn grow_lines<D>(&mut self, new_line_count: Line)
+ fn grow_lines<D>(&mut self, target: usize)
where
T: ResetDiscriminant<D>,
D: PartialEq,
{
- let lines_added = new_line_count - self.lines;
+ let lines_added = target - self.lines;
// Need to resize before updating buffer.
- self.raw.grow_visible_lines(new_line_count);
- self.lines = new_line_count;
+ self.raw.grow_visible_lines(target);
+ self.lines = target;
let history_size = self.history_size();
- let from_history = min(history_size, lines_added.0);
+ let from_history = min(history_size, lines_added);
// Move existing lines up for every line that couldn't be pulled from history.
- if from_history != lines_added.0 {
+ if from_history != lines_added {
let delta = lines_added - from_history;
- self.scroll_up(&(Line(0)..new_line_count), delta);
+ self.scroll_up(&(Line(0)..Line(target as i32)), delta);
}
// Move cursor down for every line pulled from history.
self.saved_cursor.point.line += from_history;
self.cursor.point.line += from_history;
- self.display_offset = self.display_offset.saturating_sub(*lines_added);
- self.decrease_scroll_limit(*lines_added);
+ self.display_offset = self.display_offset.saturating_sub(lines_added);
+ self.decrease_scroll_limit(lines_added);
}
/// Remove lines from the visible area.
@@ -75,37 +75,37 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
/// of the terminal window.
///
/// Alacritty takes the same approach.
- fn shrink_lines<D>(&mut self, target: Line)
+ fn shrink_lines<D>(&mut self, target: usize)
where
T: ResetDiscriminant<D>,
D: PartialEq,
{
// Scroll up to keep content inside the window.
- let required_scrolling = (self.cursor.point.line + 1).saturating_sub(target.0);
+ let required_scrolling = (self.cursor.point.line.0 as usize + 1).saturating_sub(target);
if required_scrolling > 0 {
- self.scroll_up(&(Line(0)..self.lines), Line(required_scrolling));
+ self.scroll_up(&(Line(0)..Line(self.lines as i32)), required_scrolling);
// Clamp cursors to the new viewport size.
- self.cursor.point.line = min(self.cursor.point.line, target - 1);
+ self.cursor.point.line = min(self.cursor.point.line, Line(target as i32 - 1));
}
// Clamp saved cursor, since only primary cursor is scrolled into viewport.
- self.saved_cursor.point.line = min(self.saved_cursor.point.line, target - 1);
+ self.saved_cursor.point.line = min(self.saved_cursor.point.line, Line(target as i32 - 1));
- self.raw.rotate((self.lines - target).0 as isize);
+ self.raw.rotate((self.lines - target) as isize);
self.raw.shrink_visible_lines(target);
self.lines = target;
}
/// Grow number of columns in each row, reflowing if necessary.
- fn grow_cols(&mut self, reflow: bool, cols: Column) {
+ fn grow_columns(&mut self, reflow: bool, columns: usize) {
// Check if a row needs to be wrapped.
let should_reflow = |row: &Row<T>| -> bool {
let len = Column(row.len());
- reflow && len.0 > 0 && len < cols && row[len - 1].flags().contains(Flags::WRAPLINE)
+ reflow && len.0 > 0 && len < columns && row[len - 1].flags().contains(Flags::WRAPLINE)
};
- self.cols = cols;
+ self.columns = columns;
let mut reversed: Vec<Row<T>> = Vec::with_capacity(self.raw.len());
let mut cursor_line_delta = 0;
@@ -138,12 +138,12 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
if last_len >= 1
&& last_row[Column(last_len - 1)].flags().contains(Flags::LEADING_WIDE_CHAR_SPACER)
{
- last_row.shrink(Column(last_len - 1));
+ last_row.shrink(last_len - 1);
last_len -= 1;
}
// Don't try to pull more cells from the next line than available.
- let mut num_wrapped = cols.0 - last_len;
+ let mut num_wrapped = columns - last_len;
let len = min(row.len(), num_wrapped);
// Insert leading spacer when there's not enough room for reflowing wide char.
@@ -164,30 +164,30 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
// Add removed cells to previous row and reflow content.
last_row.append(&mut cells);
- let cursor_buffer_line = (self.lines - self.cursor.point.line - 1).0;
+ let cursor_buffer_line = self.lines - self.cursor.point.line.0 as usize - 1;
if i == cursor_buffer_line && reflow {
// Resize cursor's line and reflow the cursor if necessary.
- let mut target = self.cursor.point.sub(cols, num_wrapped);
+ let mut target = self.cursor.point.sub(self, Boundary::Cursor, num_wrapped);
// Clamp to the last column, if no content was reflown with the cursor.
if target.column.0 == 0 && row.is_clear() {
self.cursor.input_needs_wrap = true;
- target = target.sub(cols, 1);
+ target = target.sub(self, Boundary::Cursor, 1);
}
self.cursor.point.column = target.column;
- // Get required cursor line changes. Since `num_wrapped` is smaller than `cols`
+ // Get required cursor line changes. Since `num_wrapped` is smaller than `columns`
// this will always be either `0` or `1`.
- let line_delta = (self.cursor.point.line - target.line).0;
+ let line_delta = self.cursor.point.line - target.line;
if line_delta != 0 && row.is_clear() {
continue;
}
- cursor_line_delta += line_delta;
+ cursor_line_delta += line_delta.0 as usize;
} else if row.is_clear() {
- if i + reversed.len() >= self.lines.0 {
+ if i + reversed.len() >= self.lines {
// Since we removed a line, rotate down the viewport.
self.display_offset = self.display_offset.saturating_sub(1);
}
@@ -210,27 +210,27 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
}
// Make sure we have at least the viewport filled.
- if reversed.len() < self.lines.0 {
- let delta = self.lines.0 - reversed.len();
- self.cursor.point.line.0 = self.cursor.point.line.saturating_sub(delta);
- reversed.resize_with(self.lines.0, || Row::new(cols));
+ if reversed.len() < self.lines {
+ let delta = (self.lines - reversed.len()) as i32;
+ self.cursor.point.line = max(self.cursor.point.line - delta, Line(0));
+ reversed.resize_with(self.lines, || Row::new(columns));
}
// Pull content down to put cursor in correct position, or move cursor up if there's no
// more lines to delete below the cursor.
if cursor_line_delta != 0 {
- let cursor_buffer_line = (self.lines - self.cursor.point.line - 1).0;
- let available = min(cursor_buffer_line, reversed.len() - self.lines.0);
+ let cursor_buffer_line = self.lines - self.cursor.point.line.0 as usize - 1;
+ let available = min(cursor_buffer_line, reversed.len() - self.lines);
let overflow = cursor_line_delta.saturating_sub(available);
reversed.truncate(reversed.len() + overflow - cursor_line_delta);
- self.cursor.point.line.0 = self.cursor.point.line.saturating_sub(overflow);
+ self.cursor.point.line = max(self.cursor.point.line - overflow, Line(0));
}
// Reverse iterator and fill all rows that are still too short.
let mut new_raw = Vec::with_capacity(reversed.len());
for mut row in reversed.drain(..).rev() {
- if row.len() < cols.0 {
- row.grow(cols);
+ if row.len() < columns {
+ row.grow(columns);
}
new_raw.push(row);
}
@@ -242,8 +242,8 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
}
/// Shrink number of columns in each row, reflowing if necessary.
- fn shrink_cols(&mut self, reflow: bool, cols: Column) {
- self.cols = cols;
+ fn shrink_columns(&mut self, reflow: bool, columns: usize) {
+ self.columns = columns;
// Remove the linewrap special case, by moving the cursor outside of the grid.
if self.cursor.input_needs_wrap && reflow {
@@ -260,7 +260,7 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
if let Some(buffered) = buffered.take() {
// Add a column for every cell added before the cursor, if it goes beyond the new
// width it is then later reflown.
- let cursor_buffer_line = (self.lines - self.cursor.point.line - 1).0;
+ let cursor_buffer_line = self.lines - self.cursor.point.line.0 as usize - 1;
if i == cursor_buffer_line {
self.cursor.point.column += buffered.len();
}
@@ -270,11 +270,11 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
loop {
// Remove all cells which require reflowing.
- let mut wrapped = match row.shrink(cols) {
+ let mut wrapped = match row.shrink(columns) {
Some(wrapped) if reflow => wrapped,
_ => {
- let cursor_buffer_line = (self.lines - self.cursor.point.line - 1).0;
- if reflow && i == cursor_buffer_line && self.cursor.point.column > cols {
+ let cursor_buffer_line = self.lines - self.cursor.point.line.0 as usize - 1;
+ if reflow && i == cursor_buffer_line && self.cursor.point.column > columns {
// If there are empty cells before the cursor, we assume it is explicit
// whitespace and need to wrap it like normal content.
Vec::new()
@@ -287,11 +287,13 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
};
// Insert spacer if a wide char would be wrapped into the last column.
- if row.len() >= cols.0 && row[cols - 1].flags().contains(Flags::WIDE_CHAR) {
+ if row.len() >= columns
+ && row[Column(columns - 1)].flags().contains(Flags::WIDE_CHAR)
+ {
let mut spacer = T::default();
spacer.flags_mut().insert(Flags::LEADING_WIDE_CHAR_SPACER);
- let wide_char = mem::replace(&mut row[cols - 1], spacer);
+ let wide_char = mem::replace(&mut row[Column(columns - 1)], spacer);
wrapped.insert(0, wide_char);
}
@@ -299,7 +301,7 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
let len = wrapped.len();
if len > 0 && wrapped[len - 1].flags().contains(Flags::LEADING_WIDE_CHAR_SPACER) {
if len == 1 {
- row[cols - 1].flags_mut().insert(Flags::WRAPLINE);
+ row[Column(columns - 1)].flags_mut().insert(Flags::WRAPLINE);
new_raw.push(row);
break;
} else {
@@ -320,7 +322,7 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
.last()
.map(|c| c.flags().contains(Flags::WRAPLINE) && i >= 1)
.unwrap_or(false)
- && wrapped.len() < cols.0
+ && wrapped.len() < columns
{
// Make sure previous wrap flag doesn't linger around.
if let Some(cell) = wrapped.last_mut() {
@@ -332,24 +334,24 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
break;
} else {
// Reflow cursor if a line below it is deleted.
- let cursor_buffer_line = (self.lines - self.cursor.point.line - 1).0;
- if (i == cursor_buffer_line && self.cursor.point.column < cols)
+ let cursor_buffer_line = self.lines - self.cursor.point.line.0 as usize - 1;
+ if (i == cursor_buffer_line && self.cursor.point.column < columns)
|| i < cursor_buffer_line
{
- self.cursor.point.line.0 = self.cursor.point.line.saturating_sub(1);
+ self.cursor.point.line = max(self.cursor.point.line - 1, Line(0));
}
// Reflow the cursor if it is on this line beyond the width.
- if i == cursor_buffer_line && self.cursor.point.column >= cols {
- // Since only a single new line is created, we subtract only `cols`
+ if i == cursor_buffer_line && self.cursor.point.column >= columns {
+ // Since only a single new line is created, we subtract only `columns`
// from the cursor instead of reflowing it completely.
- self.cursor.point.column -= cols;
+ self.cursor.point.column -= columns;
}
// Make sure new row is at least as long as new width.
let occ = wrapped.len();
- if occ < cols.0 {
- wrapped.resize_with(cols.0, T::default);
+ if occ < columns {
+ wrapped.resize_with(columns, T::default);
}
row = Row::from_vec(wrapped, occ);
}
@@ -358,22 +360,22 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
// Reverse iterator and use it as the new grid storage.
let mut reversed: Vec<Row<T>> = new_raw.drain(..).rev().collect();
- reversed.truncate(self.max_scroll_limit + self.lines.0);
+ reversed.truncate(self.max_scroll_limit + self.lines);
self.raw.replace_inner(reversed);
// Reflow the primary cursor, or clamp it if reflow is disabled.
if !reflow {
- self.cursor.point.column = min(self.cursor.point.column, cols - 1);
- } else if self.cursor.point.column == cols
- && !self[self.cursor.point.line][cols - 1].flags().contains(Flags::WRAPLINE)
+ self.cursor.point.column = min(self.cursor.point.column, Column(columns - 1));
+ } else if self.cursor.point.column == columns
+ && !self[self.cursor.point.line][Column(columns - 1)].flags().contains(Flags::WRAPLINE)
{
self.cursor.input_needs_wrap = true;
self.cursor.point.column -= 1;
} else {
- self.cursor.point = self.cursor.point.add(cols, 0);
+ self.cursor.point = self.cursor.point.grid_clamp(self, Boundary::Cursor);
}
// Clamp the saved cursor to the grid.
- self.saved_cursor.point.column = min(self.saved_cursor.point.column, cols - 1);
+ self.saved_cursor.point.column = min(self.saved_cursor.point.column, Column(columns - 1));
}
}
diff --git a/alacritty_terminal/src/grid/row.rs b/alacritty_terminal/src/grid/row.rs
index e48103a6..c932e83f 100644
--- a/alacritty_terminal/src/grid/row.rs
+++ b/alacritty_terminal/src/grid/row.rs
@@ -34,22 +34,22 @@ impl<T: Clone + Default> Row<T> {
/// Create a new terminal row.
///
/// Ideally the `template` should be `Copy` in all performance sensitive scenarios.
- pub fn new(columns: Column) -> Row<T> {
- debug_assert!(columns.0 >= 1);
+ pub fn new(columns: usize) -> Row<T> {
+ debug_assert!(columns >= 1);
- let mut inner: Vec<T> = Vec::with_capacity(columns.0);
+ let mut inner: Vec<T> = Vec::with_capacity(columns);
// This is a slightly optimized version of `std::vec::Vec::resize`.
unsafe {
let mut ptr = inner.as_mut_ptr();
- for _ in 1..columns.0 {
+ for _ in 1..columns {
ptr::write(ptr, T::default());
ptr = ptr.offset(1);
}
ptr::write(ptr, T::default());
- inner.set_len(columns.0);
+ inner.set_len(columns);
}
Row { inner, occ: 0 }
@@ -57,31 +57,31 @@ impl<T: Clone + Default> Row<T> {
/// Increase the number of columns in the row.
#[inline]
- pub fn grow(&mut self, cols: Column) {
- if self.inner.len() >= cols.0 {
+ pub fn grow(&mut self, columns: usize) {
+ if self.inner.len() >= columns {
return;
}
- self.inner.resize_with(cols.0, T::default);
+ self.inner.resize_with(columns, T::default);
}
/// Reduce the number of columns in the row.
///
/// This will return all non-empty cells that were removed.
- pub fn shrink(&mut self, cols: Column) -> Option<Vec<T>>
+ pub fn shrink(&mut self, columns: usize) -> Option<Vec<T>>
where
T: GridCell,
{
- if self.inner.len() <= cols.0 {
+ if self.inner.len() <= columns {
return None;
}
// Split off cells for a new row.
- let mut new_row = self.inner.split_off(cols.0);
+ let mut new_row = self.inner.split_off(columns);
let index = new_row.iter().rposition(|c| !c.is_empty()).map(|i| i + 1).unwrap_or(0);
new_row.truncate(index);
- self.occ = min(self.occ, cols.0);
+ self.occ = min(self.occ, columns);
if new_row.is_empty() {
None
diff --git a/alacritty_terminal/src/grid/storage.rs b/alacritty_terminal/src/grid/storage.rs
index f9c4b2cf..ad6358f0 100644
--- a/alacritty_terminal/src/grid/storage.rs
+++ b/alacritty_terminal/src/grid/storage.rs
@@ -5,7 +5,7 @@ use std::ops::{Index, IndexMut};
use serde::{Deserialize, Serialize};
use super::Row;
-use crate::index::{Column, Line};
+use crate::index::Line;
/// Maximum number of buffered lines outside of the grid for performance optimization.
const MAX_CACHE_SIZE: usize = 1_000;
@@ -38,7 +38,7 @@ pub struct Storage<T> {
zero: usize,
/// Number of visible lines.
- visible_lines: Line,
+ visible_lines: usize,
/// Total number of lines currently active in the terminal (scrollback + visible)
///
@@ -61,28 +61,28 @@ impl<T: PartialEq> PartialEq for Storage<T> {
impl<T> Storage<T> {
#[inline]
- pub fn with_capacity(visible_lines: Line, cols: Column) -> Storage<T>
+ pub fn with_capacity(visible_lines: usize, columns: usize) -> Storage<T>
where
T: Clone + Default,
{
// Initialize visible lines; the scrollback buffer is initialized dynamically.
- let mut inner = Vec::with_capacity(visible_lines.0);
- inner.resize_with(visible_lines.0, || Row::new(cols));
+ let mut inner = Vec::with_capacity(visible_lines);
+ inner.resize_with(visible_lines, || Row::new(columns));
- Storage { inner, zero: 0, visible_lines, len: visible_lines.0 }
+ Storage { inner, zero: 0, visible_lines, len: visible_lines }
}
/// Increase the number of lines in the buffer.
#[inline]
- pub fn grow_visible_lines(&mut self, next: Line)
+ pub fn grow_visible_lines(&mut self, next: usize)
where
T: Clone + Default,
{
// Number of lines the buffer needs to grow.
let growage = next - self.visible_lines;
- let cols = self[0].len();
- self.initialize(growage.0, Column(cols));
+ let columns = self[Line(0)].len();
+ self.initialize(growage, columns);
// Update visible lines.
self.visible_lines = next;
@@ -90,10 +90,10 @@ impl<T> Storage<T> {
/// Decrease the number of lines in the buffer.
#[inline]
- pub fn shrink_visible_lines(&mut self, next: Line) {
+ pub fn shrink_visible_lines(&mut self, next: usize) {
// Shrink the size without removing any lines.
let shrinkage = self.visible_lines - next;
- self.shrink_lines(shrinkage.0);
+ self.shrink_lines(shrinkage);
// Update visible lines.
self.visible_lines = next;
@@ -120,7 +120,7 @@ impl<T> Storage<T> {
/// Dynamically grow the storage buffer at runtime.
#[inline]
- pub fn initialize(&mut self, additional_rows: usize, cols: Column)
+ pub fn initialize(&mut self, additional_rows: usize, columns: usize)
where
T: Clone + Default,
{
@@ -128,7 +128,7 @@ impl<T> Storage<T> {
self.rezero();
let realloc_size = self.inner.len() + max(additional_rows, MAX_CACHE_SIZE);
- self.inner.resize_with(realloc_size, || Row::new(cols));
+ self.inner.resize_with(realloc_size, || Row::new(columns));
}
self.len += additional_rows;
@@ -139,14 +139,6 @@ impl<T> Storage<T> {
self.len
}
- #[inline]
- pub fn swap_lines(&mut self, a: Line, b: Line) {
- let offset = self.inner.len() + self.zero + *self.visible_lines - 1;
- let a = (offset - *a) % self.inner.len();
- let b = (offset - *b) % self.inner.len();
- self.inner.swap(a, b);
- }
-
/// Swap implementation for Row<T>.
///
/// Exploits the known size of Row<T> to produce a slightly more efficient
@@ -155,7 +147,7 @@ impl<T> Storage<T> {
/// The default implementation from swap generates 8 movups and 4 movaps
/// instructions. This implementation achieves the swap in only 8 movups
/// instructions.
- pub fn swap(&mut self, a: usize, b: usize) {
+ pub fn swap(&mut self, a: Line, b: Line) {
debug_assert_eq!(mem::size_of::<Row<T>>(), mem::size_of::<usize>() * 4);
let a = self.compute_index(a);
@@ -222,10 +214,14 @@ impl<T> Storage<T> {
/// Compute actual index in underlying storage given the requested index.
#[inline]
- fn compute_index(&self, requested: usize) -> usize {
- debug_assert!(requested < self.len);
+ fn compute_index(&self, requested: Line) -> usize {
+ debug_assert!(requested.0 < self.visible_lines as i32);
+
+ let positive = -(requested - self.visible_lines).0 as usize - 1;
+
+ debug_assert!(positive < self.len);
- let zeroed = self.zero + requested;
+ let zeroed = self.zero + positive;
// Use if/else instead of remainder here to improve performance.
//
@@ -250,38 +246,21 @@ impl<T> Storage<T> {
}
}
-impl<T> Index<usize> for Storage<T> {
- type Output = Row<T>;
-
- #[inline]
- fn index(&self, index: usize) -> &Self::Output {
- &self.inner[self.compute_index(index)]
- }
-}
-
-impl<T> IndexMut<usize> for Storage<T> {
- #[inline]
- fn index_mut(&mut self, index: usize) -> &mut Self::Output {
- let index = self.compute_index(index); // borrowck
- &mut self.inner[index]
- }
-}
-
impl<T> Index<Line> for Storage<T> {
type Output = Row<T>;
#[inline]
fn index(&self, index: Line) -> &Self::Output {
- let index = self.visible_lines - 1 - index;
- &self[*index]
+ let index = self.compute_index(index);
+ &self.inner[index]
}
}
impl<T> IndexMut<Line> for Storage<T> {
#[inline]
fn index_mut(&mut self, index: Line) -> &mut Self::Output {
- let index = self.visible_lines - 1 - index;
- &mut self[*index]
+ let index = self.compute_index(index);
+ &mut self.inner[index]
}
}
@@ -313,43 +292,39 @@ mod tests {
#[test]
fn with_capacity() {
- let storage = Storage::<char>::with_capacity(Line(3), Column(1));
+ let storage = Storage::<char>::with_capacity(3, 1);
assert_eq!(storage.inner.len(), 3);
assert_eq!(storage.len, 3);
assert_eq!(storage.zero, 0);
- assert_eq!(storage.visible_lines, Line(3));
+ assert_eq!(storage.visible_lines, 3);
}
#[test]
fn indexing() {
- let mut storage = Storage::<char>::with_capacity(Line(3), Column(1));
-
- storage[0] = filled_row('0');
- storage[1] = filled_row('1');
- storage[2] = filled_row('2');
+ let mut storage = Storage::<char>::with_capacity(3, 1);
- assert_eq!(storage[0], filled_row('0'));
- assert_eq!(storage[1], filled_row('1'));
- assert_eq!(storage[2], filled_row('2'));
+ storage[Line(0)] = filled_row('0');
+ storage[Line(1)] = filled_row('1');
+ storage[Line(2)] = filled_row('2');
storage.zero += 1;
- assert_eq!(storage[0], filled_row('1'));
- assert_eq!(storage[1], filled_row('2'));
- assert_eq!(storage[2], filled_row('0'));
+ assert_eq!(storage[Line(0)], filled_row('2'));
+ assert_eq!(storage[Line(1)], filled_row('0'));
+ assert_eq!(storage[Line(2)], filled_row('1'));
}
#[test]
#[should_panic]
fn indexing_above_inner_len() {
- let storage = Storage::<char>::with_capacity(Line(1), Column(1));
- let _ = &storage[2];
+ let storage = Storage::<char>::with_capacity(1, 1);
+ let _ = &storage[Line(-1)];
}
#[test]
fn rotate() {
- let mut storage = Storage::<char>::with_capacity(Line(3), Column(1));
+ let mut storage = Storage::<char>::with_capacity(3, 1);
storage.rotate(2);
assert_eq!(storage.zero, 2);
storage.shrink_lines(2);
@@ -377,18 +352,18 @@ mod tests {
let mut storage: Storage<char> = Storage {
inner: vec![filled_row('0'), filled_row('1'), filled_row('-')],
zero: 0,
- visible_lines: Line(3),
+ visible_lines: 3,
len: 3,
};
// Grow buffer.
- storage.grow_visible_lines(Line(4));
+ storage.grow_visible_lines(4);
// Make sure the result is correct.
let mut expected = Storage {
inner: vec![filled_row('0'), filled_row('1'), filled_row('-')],
zero: 0,
- visible_lines: Line(4),
+ visible_lines: 4,
len: 4,
};
expected.inner.append(&mut vec![filled_row('\0'); MAX_CACHE_SIZE]);
@@ -418,18 +393,18 @@ mod tests {
let mut storage: Storage<char> = Storage {
inner: vec![filled_row('-'), filled_row('0'), filled_row('1')],
zero: 1,
- visible_lines: Line(3),
+ visible_lines: 3,
len: 3,
};
// Grow buffer.
- storage.grow_visible_lines(Line(4));
+ storage.grow_visible_lines(4);
// Make sure the result is correct.
let mut expected = Storage {
inner: vec![filled_row('0'), filled_row('1'), filled_row('-')],
zero: 0,
- visible_lines: Line(4),
+ visible_lines: 4,
len: 4,
};
expected.inner.append(&mut vec![filled_row('\0'); MAX_CACHE_SIZE]);
@@ -456,18 +431,18 @@ mod tests {
let mut storage: Storage<char> = Storage {
inner: vec![filled_row('2'), filled_row('0'), filled_row('1')],
zero: 1,
- visible_lines: Line(3),
+ visible_lines: 3,
len: 3,
};
// Shrink buffer.
- storage.shrink_visible_lines(Line(2));
+ storage.shrink_visible_lines(2);
// Make sure the result is correct.
let expected = Storage {
inner: vec![filled_row('2'), filled_row('0'), filled_row('1')],
zero: 1,
- visible_lines: Line(2),
+ visible_lines: 2,
len: 2,
};
assert_eq!(storage.visible_lines, expected.visible_lines);
@@ -492,18 +467,18 @@ mod tests {
let mut storage: Storage<char> = Storage {
inner: vec![filled_row('0'), filled_row('1'), filled_row('2')],
zero: 0,
- visible_lines: Line(3),
+ visible_lines: 3,
len: 3,
};
// Shrink buffer.
- storage.shrink_visible_lines(Line(2));
+ storage.shrink_visible_lines(2);
// Make sure the result is correct.
let expected = Storage {
inner: vec![filled_row('0'), filled_row('1'), filled_row('2')],
zero: 0,
- visible_lines: Line(2),
+ visible_lines: 2,
len: 2,
};
assert_eq!(storage.visible_lines, expected.visible_lines);
@@ -541,12 +516,12 @@ mod tests {
filled_row('3'),
],
zero: 2,
- visible_lines: Line(6),
+ visible_lines: 6,
len: 6,
};
// Shrink buffer.
- storage.shrink_visible_lines(Line(2));
+ storage.shrink_visible_lines(2);
// Make sure the result is correct.
let expected = Storage {
@@ -559,7 +534,7 @@ mod tests {
filled_row('3'),
],
zero: 2,
- visible_lines: Line(2),
+ visible_lines: 2,
len: 2,
};
assert_eq!(storage.visible_lines, expected.visible_lines);
@@ -593,7 +568,7 @@ mod tests {
filled_row('3'),
],
zero: 2,
- visible_lines: Line(1),
+ visible_lines: 1,
len: 2,
};
@@ -604,7 +579,7 @@ mod tests {
let expected = Storage {
inner: vec![filled_row('0'), filled_row('1')],
zero: 0,
- visible_lines: Line(1),
+ visible_lines: 1,
len: 2,
};
assert_eq!(storage.visible_lines, expected.visible_lines);
@@ -628,7 +603,7 @@ mod tests {
let mut storage: Storage<char> = Storage {
inner: vec![filled_row('1'), filled_row('2'), filled_row('0')],
zero: 2,
- visible_lines: Line(1),
+ visible_lines: 1,
len: 2,
};
@@ -639,7 +614,7 @@ mod tests {
let expected = Storage {
inner: vec![filled_row('0'), filled_row('1')],
zero: 0,
- visible_lines: Line(1),
+ visible_lines: 1,
len: 2,
};
assert_eq!(storage.visible_lines, expected.visible_lines);
@@ -685,7 +660,7 @@ mod tests {
filled_row('3'),
],
zero: 2,
- visible_lines: Line(0),
+ visible_lines: 0,
len: 6,
};
@@ -703,7 +678,7 @@ mod tests {
filled_row('3'),
],
zero: 2,
- visible_lines: Line(0),
+ visible_lines: 0,
len: 3,
};
assert_eq!(storage.inner, shrinking_expected.inner);
@@ -711,7 +686,7 @@ mod tests {
assert_eq!(storage.len, shrinking_expected.len);
// Grow buffer.
- storage.initialize(1, Column(1));
+ storage.initialize(1, 1);
// Make sure the previously freed elements are reused.
let growing_expected = Storage {
@@ -724,7 +699,7 @@ mod tests {
filled_row('3'),
],
zero: 2,
- visible_lines: Line(0),
+ visible_lines: 0,
len: 4,
};
@@ -746,13 +721,13 @@ mod tests {
filled_row('3'),
],
zero: 2,
- visible_lines: Line(0),
+ visible_lines: 0,
len: 6,
};
// Initialize additional lines.
let init_size = 3;
- storage.initialize(init_size, Column(1));
+ storage.initialize(init_size, 1);
// Generate expected grid.
let mut expected_inner = vec![
@@ -765,8 +740,7 @@ mod tests {
];
let expected_init_size = std::cmp::max(init_size, MAX_CACHE_SIZE);
expected_inner.append(&mut vec![filled_row('\0'); expected_init_size]);
- let expected_storage =
- Storage { inner: expected_inner, zero: 0, visible_lines: Line(0), len: 9 };
+ let expected_storage = Storage { inner: expected_inner, zero: 0, visible_lines: 0, len: 9 };
assert_eq!(storage.len, expected_storage.len);
assert_eq!(storage.zero, expected_storage.zero);
@@ -778,7 +752,7 @@ mod tests {
let mut storage: Storage<char> = Storage {
inner: vec![filled_row('-'), filled_row('-'), filled_row('-')],
zero: 2,
- visible_lines: Line(0),
+ visible_lines: 0,
len: 3,
};
@@ -788,7 +762,7 @@ mod tests {
}
fn filled_row(content: char) -> Row<char> {
- let mut row = Row::new(Column(1));
+ let mut row = Row::new(1);
row[Column(0)] = content;
row
}
diff --git a/alacritty_terminal/src/grid/tests.rs b/alacritty_terminal/src/grid/tests.rs
index 269ab636..49cf1a76 100644
--- a/alacritty_terminal/src/grid/tests.rs
+++ b/alacritty_terminal/src/grid/tests.rs
@@ -22,49 +22,15 @@ impl GridCell for usize {
}
}
-#[test]
-fn grid_clamp_buffer_point() {
- let mut grid = Grid::<usize>::new(Line(10), Column(10), 1_000);
- grid.display_offset = 5;
-
- let point = grid.clamp_buffer_to_visible(Point::new(10, Column(3)));
- assert_eq!(point, Point::new(Line(4), Column(3)));
-
- let point = grid.clamp_buffer_to_visible(Point::new(15, Column(3)));
- assert_eq!(point, Point::new(Line(0), Column(0)));
-
- let point = grid.clamp_buffer_to_visible(Point::new(4, Column(3)));
- assert_eq!(point, Point::new(Line(9), Column(9)));
-
- grid.display_offset = 0;
-
- let point = grid.clamp_buffer_to_visible(Point::new(4, Column(3)));
- assert_eq!(point, Point::new(Line(5), Column(3)));
-}
-
-#[test]
-fn visible_to_buffer() {
- let mut grid = Grid::<usize>::new(Line(10), Column(10), 1_000);
- grid.display_offset = 5;
-
- let point = grid.visible_to_buffer(Point::new(Line(4), Column(3)));
- assert_eq!(point, Point::new(10, Column(3)));
-
- grid.display_offset = 0;
-
- let point = grid.visible_to_buffer(Point::new(Line(5), Column(3)));
- assert_eq!(point, Point::new(4, Column(3)));
-}
-
// Scroll up moves lines upward.
#[test]
fn scroll_up() {
- let mut grid = Grid::<usize>::new(Line(10), Column(1), 0);
+ let mut grid = Grid::<usize>::new(10, 1, 0);
for i in 0..10 {
- grid[Line(i)][Column(0)] = i;
+ grid[Line(i as i32)][Column(0)] = i;
}
- grid.scroll_up::<usize>(&(Line(0)..Line(10)), Line(2));
+ grid.scroll_up::<usize>(&(Line(0)..Line(10)), 2);
assert_eq!(grid[Line(0)][Column(0)], 2);
assert_eq!(grid[Line(0)].occ, 1);
@@ -91,12 +57,44 @@ fn scroll_up() {
// Scroll down moves lines downward.
#[test]
fn scroll_down() {
- let mut grid = Grid::<usize>::new(Line(10), Column(1), 0);
+ let mut grid = Grid::<usize>::new(10, 1, 0);
+ for i in 0..10 {
+ grid[Line(i as i32)][Column(0)] = i;
+ }
+
+ grid.scroll_down::<usize>(&(Line(0)..Line(10)), 2);
+
+ assert_eq!(grid[Line(0)][Column(0)], 0); // was 8.
+ assert_eq!(grid[Line(0)].occ, 0);
+ assert_eq!(grid[Line(1)][Column(0)], 0); // was 9.
+ assert_eq!(grid[Line(1)].occ, 0);
+ assert_eq!(grid[Line(2)][Column(0)], 0);
+ assert_eq!(grid[Line(2)].occ, 1);
+ assert_eq!(grid[Line(3)][Column(0)], 1);
+ assert_eq!(grid[Line(3)].occ, 1);
+ assert_eq!(grid[Line(4)][Column(0)], 2);
+ assert_eq!(grid[Line(4)].occ, 1);
+ assert_eq!(grid[Line(5)][Column(0)], 3);
+ assert_eq!(grid[Line(5)].occ, 1);
+ assert_eq!(grid[Line(6)][Column(0)], 4);
+ assert_eq!(grid[Line(6)].occ, 1);
+ assert_eq!(grid[Line(7)][Column(0)], 5);
+ assert_eq!(grid[Line(7)].occ, 1);
+ assert_eq!(grid[Line(8)][Column(0)], 6);
+ assert_eq!(grid[Line(8)].occ, 1);
+ assert_eq!(grid[Line(9)][Column(0)], 7);
+ assert_eq!(grid[Line(9)].occ, 1);
+}
+
+#[test]
+fn scroll_down_with_history() {
+ let mut grid = Grid::<usize>::new(10, 1, 1);
+ grid.increase_scroll_limit(1);
for i in 0..10 {
- grid[Line(i)][Column(0)] = i;
+ grid[Line(i as i32)][Column(0)] = i;
}
- grid.scroll_down::<usize>(&(Line(0)..Line(10)), Line(2));
+ grid.scroll_down::<usize>(&(Line(0)..Line(10)), 2);
assert_eq!(grid[Line(0)][Column(0)], 0); // was 8.
assert_eq!(grid[Line(0)].occ, 0);
@@ -127,19 +125,19 @@ fn test_iter() {
assert_eq!(Some(&value), indexed.map(|indexed| indexed.cell));
};
- let mut grid = Grid::<usize>::new(Line(5), Column(5), 0);
+ let mut grid = Grid::<usize>::new(5, 5, 0);
for i in 0..5 {
for j in 0..5 {
- grid[Line(i)][Column(j)] = i * 5 + j;
+ grid[Line(i)][Column(j)] = i as usize * 5 + j;
}
}
- let mut iter = grid.iter_from(Point::new(4, Column(0)));
+ let mut iter = grid.iter_from(Point::new(Line(0), Column(0)));
assert_eq!(None, iter.prev());
assert_indexed(1, iter.next());
assert_eq!(Column(1), iter.point().column);
- assert_eq!(4, iter.point().line);
+ assert_eq!(0, iter.point().line);
assert_indexed(2, iter.next());
assert_indexed(3, iter.next());
@@ -148,139 +146,139 @@ fn test_iter() {
// Test line-wrapping.
assert_indexed(5, iter.next());
assert_eq!(Column(0), iter.point().column);
- assert_eq!(3, iter.point().line);
+ assert_eq!(1, iter.point().line);
assert_indexed(4, iter.prev());
assert_eq!(Column(4), iter.point().column);
- assert_eq!(4, iter.point().line);
+ assert_eq!(0, iter.point().line);
// Make sure iter.cell() returns the current iterator position.
assert_eq!(&4, iter.cell());
// Test that iter ends at end of grid.
- let mut final_iter = grid.iter_from(Point { line: 0, column: Column(4) });
+ let mut final_iter = grid.iter_from(Point { line: Line(4), column: Column(4) });
assert_eq!(None, final_iter.next());
assert_indexed(23, final_iter.prev());
}
#[test]
fn shrink_reflow() {
- let mut grid = Grid::<Cell>::new(Line(1), Column(5), 2);
+ let mut grid = Grid::<Cell>::new(1, 5, 2);
grid[Line(0)][Column(0)] = cell('1');
grid[Line(0)][Column(1)] = cell('2');
grid[Line(0)][Column(2)] = cell('3');
grid[Line(0)][Column(3)] = cell('4');
grid[Line(0)][Column(4)] = cell('5');
- grid.resize(true, Line(1), Column(2));
+ grid.resize(true, 1, 2);
assert_eq!(grid.total_lines(), 3);
- assert_eq!(grid[2].len(), 2);
- assert_eq!(grid[2][Column(0)], cell('1'));
- assert_eq!(grid[2][Column(1)], wrap_cell('2'));
+ assert_eq!(grid[Line(-2)].len(), 2);
+ assert_eq!(grid[Line(-2)][Column(0)], cell('1'));
+ assert_eq!(grid[Line(-2)][Column(1)], wrap_cell('2'));
- assert_eq!(grid[1].len(), 2);
- assert_eq!(grid[1][Column(0)], cell('3'));
- assert_eq!(grid[1][Column(1)], wrap_cell('4'));
+ assert_eq!(grid[Line(-1)].len(), 2);
+ assert_eq!(grid[Line(-1)][Column(0)], cell('3'));
+ assert_eq!(grid[Line(-1)][Column(1)], wrap_cell('4'));
- assert_eq!(grid[0].len(), 2);
- assert_eq!(grid[0][Column(0)], cell('5'));
- assert_eq!(grid[0][Column(1)], Cell::default());
+ assert_eq!(grid[Line(0)].len(), 2);
+ assert_eq!(grid[Line(0)][Column(0)], cell('5'));
+ assert_eq!(grid[Line(0)][Column(1)], Cell::default());
}
#[test]
fn shrink_reflow_twice() {
- let mut grid = Grid::<Cell>::new(Line(1), Column(5), 2);
+ let mut grid = Grid::<Cell>::new(1, 5, 2);
grid[Line(0)][Column(0)] = cell('1');
grid[Line(0)][Column(1)] = cell('2');
grid[Line(0)][Column(2)] = cell('3');
grid[Line(0)][Column(3)] = cell('4');
grid[Line(0)][Column(4)] = cell('5');
- grid.resize(true, Line(1), Column(4));
- grid.resize(true, Line(1), Column(2));
+ grid.resize(true, 1, 4);
+ grid.resize(true, 1, 2);
assert_eq!(grid.total_lines(), 3);
- assert_eq!(grid[2].len(), 2);
- assert_eq!(grid[2][Column(0)], cell('1'));
- assert_eq!(grid[2][Column(1)], wrap_cell('2'));
+ assert_eq!(grid[Line(-2)].len(), 2);
+ assert_eq!(grid[Line(-2)][Column(0)], cell('1'));
+ assert_eq!(grid[Line(-2)][Column(1)], wrap_cell('2'));
- assert_eq!(grid[1].len(), 2);
- assert_eq!(grid[1][Column(0)], cell('3'));
- assert_eq!(grid[1][Column(1)], wrap_cell('4'));
+ assert_eq!(grid[Line(-1)].len(), 2);
+ assert_eq!(grid[Line(-1)][Column(0)], cell('3'));
+ assert_eq!(grid[Line(-1)][Column(1)], wrap_cell('4'));
- assert_eq!(grid[0].len(), 2);
- assert_eq!(grid[0][Column(0)], cell('5'));
- assert_eq!(grid[0][Column(1)], Cell::default());
+ assert_eq!(grid[Line(0)].len(), 2);
+ assert_eq!(grid[Line(0)][Column(0)], cell('5'));
+ assert_eq!(grid[Line(0)][Column(1)], Cell::default());
}
#[test]
fn shrink_reflow_empty_cell_inside_line() {
- let mut grid = Grid::<Cell>::new(Line(1), Column(5), 3);
+ let mut grid = Grid::<Cell>::new(1, 5, 3);
grid[Line(0)][Column(0)] = cell('1');
grid[Line(0)][Column(1)] = Cell::default();
grid[Line(0)][Column(2)] = cell('3');
grid[Line(0)][Column(3)] = cell('4');
grid[Line(0)][Column(4)] = Cell::default();
- grid.resize(true, Line(1), Column(2));
+ grid.resize(true, 1, 2);
assert_eq!(grid.total_lines(), 2);
- assert_eq!(grid[1].len(), 2);
- assert_eq!(grid[1][Column(0)], cell('1'));
- assert_eq!(grid[1][Column(1)], wrap_cell(' '));
+ assert_eq!(grid[Line(-1)].len(), 2);
+ assert_eq!(grid[Line(-1)][Column(0)], cell('1'));
+ assert_eq!(grid[Line(-1)][Column(1)], wrap_cell(' '));
- assert_eq!(grid[0].len(), 2);
- assert_eq!(grid[0][Column(0)], cell('3'));
- assert_eq!(grid[0][Column(1)], cell('4'));
+ assert_eq!(grid[Line(0)].len(), 2);
+ assert_eq!(grid[Line(0)][Column(0)], cell('3'));
+ assert_eq!(grid[Line(0)][Column(1)], cell('4'));
- grid.resize(true, Line(1), Column(1));
+ grid.resize(true, 1, 1);
assert_eq!(grid.total_lines(), 4);
- assert_eq!(grid[3].len(), 1);
- assert_eq!(grid[3][Column(0)], wrap_cell('1'));
+ assert_eq!(grid[Line(-3)].len(), 1);
+ assert_eq!(grid[Line(-3)][Column(0)], wrap_cell('1'));
- assert_eq!(grid[2].len(), 1);
- assert_eq!(grid[2][Column(0)], wrap_cell(' '));
+ assert_eq!(grid[Line(-2)].len(), 1);
+ assert_eq!(grid[Line(-2)][Column(0)], wrap_cell(' '));
- assert_eq!(grid[1].len(), 1);
- assert_eq!(grid[1][Column(0)], wrap_cell('3'));
+ assert_eq!(grid[Line(-1)].len(), 1);
+ assert_eq!(grid[Line(-1)][Column(0)], wrap_cell('3'));
- assert_eq!(grid[0].len(), 1);
- assert_eq!(grid[0][Column(0)], cell('4'));
+ assert_eq!(grid[Line(0)].len(), 1);
+ assert_eq!(grid[Line(0)][Column(0)], cell('4'));
}
#[test]
fn grow_reflow() {
- let mut grid = Grid::<Cell>::new(Line(2), Column(2), 0);
+ let mut grid = Grid::<Cell>::new(2, 2, 0);
grid[Line(0)][Column(0)] = cell('1');
grid[Line(0)][Column(1)] = wrap_cell('2');
grid[Line(1)][Column(0)] = cell('3');
grid[Line(1)][Column(1)] = Cell::default();
- grid.resize(true, Line(2), Column(3));
+ grid.resize(true, 2, 3);
assert_eq!(grid.total_lines(), 2);
- assert_eq!(grid[1].len(), 3);
- assert_eq!(grid[1][Column(0)], cell('1'));
- assert_eq!(grid[1][Column(1)], cell('2'));
- assert_eq!(grid[1][Column(2)], cell('3'));
+ assert_eq!(grid[Line(0)].len(), 3);
+ assert_eq!(grid[Line(0)][Column(0)], cell('1'));
+ assert_eq!(grid[Line(0)][Column(1)], cell('2'));
+ assert_eq!(grid[Line(0)][Column(2)], cell('3'));
// Make sure rest of grid is empty.
- assert_eq!(grid[0].len(), 3);
- assert_eq!(grid[0][Column(0)], Cell::default());
- assert_eq!(grid[0][Column(1)], Cell::default());
- assert_eq!(grid[0][Column(2)], Cell::default());
+ assert_eq!(grid[Line(1)].len(), 3);
+ assert_eq!(grid[Line(1)][Column(0)], Cell::default());
+ assert_eq!(grid[Line(1)][Column(1)], Cell::default());
+ assert_eq!(grid[Line(1)][Column(2)], Cell::default());
}
#[test]
fn grow_reflow_multiline() {
- let mut grid = Grid::<Cell>::new(Line(3), Column(2), 0);
+ let mut grid = Grid::<Cell>::new(3, 2, 0);
grid[Line(0)][Column(0)] = cell('1');
grid[Line(0)][Column(1)] = wrap_cell('2');
grid[Line(1)][Column(0)] = cell('3');
@@ -288,20 +286,20 @@ fn grow_reflow_multiline() {
grid[Line(2)][Column(0)] = cell('5');
grid[Line(2)][Column(1)] = cell('6');
- grid.resize(true, Line(3), Column(6));
+ grid.resize(true, 3, 6);
assert_eq!(grid.total_lines(), 3);
- assert_eq!(grid[2].len(), 6);
- assert_eq!(grid[2][Column(0)], cell('1'));
- assert_eq!(grid[2][Column(1)], cell('2'));
- assert_eq!(grid[2][Column(2)], cell('3'));
- assert_eq!(grid[2][Column(3)], cell('4'));
- assert_eq!(grid[2][Column(4)], cell('5'));
- assert_eq!(grid[2][Column(5)], cell('6'));
+ assert_eq!(grid[Line(0)].len(), 6);
+ assert_eq!(grid[Line(0)][Column(0)], cell('1'));
+ assert_eq!(grid[Line(0)][Column(1)], cell('2'));
+ assert_eq!(grid[Line(0)][Column(2)], cell('3'));
+ assert_eq!(grid[Line(0)][Column(3)], cell('4'));
+ assert_eq!(grid[Line(0)][Column(4)], cell('5'));
+ assert_eq!(grid[Line(0)][Column(5)], cell('6'));
// Make sure rest of grid is empty.
- for r in 0..2 {
+ for r in (1..3).map(Line::from) {
assert_eq!(grid[r].len(), 6);
for c in 0..6 {
assert_eq!(grid[r][Column(c)], Cell::default());
@@ -311,43 +309,43 @@ fn grow_reflow_multiline() {
#[test]
fn grow_reflow_disabled() {
- let mut grid = Grid::<Cell>::new(Line(2), Column(2), 0);
+ let mut grid = Grid::<Cell>::new(2, 2, 0);
grid[Line(0)][Column(0)] = cell('1');
grid[Line(0)][Column(1)] = wrap_cell('2');
grid[Line(1)][Column(0)] = cell('3');
grid[Line(1)][Column(1)] = Cell::default();
- grid.resize(false, Line(2), Column(3));
+ grid.resize(false, 2, 3);
assert_eq!(grid.total_lines(), 2);
- assert_eq!(grid[1].len(), 3);
- assert_eq!(grid[1][Column(0)], cell('1'));
- assert_eq!(grid[1][Column(1)], wrap_cell('2'));
- assert_eq!(grid[1][Column(2)], Cell::default());
+ assert_eq!(grid[Line(0)].len(), 3);
+ assert_eq!(grid[Line(0)][Column(0)], cell('1'));
+ assert_eq!(grid[Line(0)][Column(1)], wrap_cell('2'));
+ assert_eq!(grid[Line(0)][Column(2)], Cell::default());
- assert_eq!(grid[0].len(), 3);
- assert_eq!(grid[0][Column(0)], cell('3'));
- assert_eq!(grid[0][Column(1)], Cell::default());
- assert_eq!(grid[0][Column(2)], Cell::default());
+ assert_eq!(grid[Line(1)].len(), 3);
+ assert_eq!(grid[Line(1)][Column(0)], cell('3'));
+ assert_eq!(grid[Line(1)][Column(1)], Cell::default());
+ assert_eq!(grid[Line(1)][Column(2)], Cell::default());
}
#[test]
fn shrink_reflow_disabled() {
- let mut grid = Grid::<Cell>::new(Line(1), Column(5), 2);
+ let mut grid = Grid::<Cell>::new(1, 5, 2);
grid[Line(0)][Column(0)] = cell('1');
grid[Line(0)][Column(1)] = cell('2');
grid[Line(0)][Column(2)] = cell('3');
grid[Line(0)][Column(3)] = cell('4');
grid[Line(0)][Column(4)] = cell('5');
- grid.resize(false, Line(1), Column(2));
+ grid.resize(false, 1, 2);
assert_eq!(grid.total_lines(), 1);
- assert_eq!(grid[0].len(), 2);
- assert_eq!(grid[0][Column(0)], cell('1'));
- assert_eq!(grid[0][Column(1)], cell('2'));
+ assert_eq!(grid[Line(0)].len(), 2);
+ assert_eq!(grid[Line(0)][Column(0)], cell('1'));
+ assert_eq!(grid[Line(0)][Column(1)], cell('2'));
}
// https://github.com/rust-lang/rust-clippy/pull/6375