diff options
Diffstat (limited to 'src/grid')
-rw-r--r-- | src/grid/mod.rs | 886 | ||||
-rw-r--r-- | src/grid/row.rs | 264 | ||||
-rw-r--r-- | src/grid/storage.rs | 922 | ||||
-rw-r--r-- | src/grid/tests.rs | 292 |
4 files changed, 0 insertions, 2364 deletions
diff --git a/src/grid/mod.rs b/src/grid/mod.rs deleted file mode 100644 index 3a6bacf8..00000000 --- a/src/grid/mod.rs +++ /dev/null @@ -1,886 +0,0 @@ -// Copyright 2016 Joe Wilm, The Alacritty Project Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! A specialized 2d grid implementation optimized for use in a terminal. - -use std::cmp::{max, min, Ordering}; -use std::ops::{Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo}; - -use crate::index::{self, Column, IndexRange, Line, Point}; -use crate::selection::Selection; - -mod row; -pub use self::row::Row; - -#[cfg(test)] -mod tests; - -mod storage; -use self::storage::Storage; - -const MIN_INIT_SIZE: usize = 1_000; - -/// Bidirection 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) - && self.scroll_limit.eq(&other.scroll_limit) - && self.selection.eq(&other.selection) - && self.url_highlight.eq(&other.url_highlight) - } -} - -pub trait GridCell { - fn is_empty(&self) -> bool; - fn is_wrap(&self) -> bool; - fn set_wrap(&mut self, wrap: bool); -} - -/// Represents the terminal display contents -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Grid<T> { - /// Lines in the grid. Each row holds a list of cells corresponding to the - /// columns in that row. - raw: Storage<T>, - - /// Number of columns - cols: index::Column, - - /// Number of lines. - /// - /// Invariant: lines is equivalent to raw.len() - lines: index::Line, - - /// Offset of displayed area - /// - /// If the displayed region isn't at the bottom of the screen, it stays - /// stationary while more text is emitted. The scrolling implementation - /// updates this offset accordingly. - #[serde(default)] - display_offset: usize, - - /// An limit on how far back it's possible to scroll - #[serde(default)] - scroll_limit: usize, - - /// Selected region - #[serde(skip)] - pub selection: Option<Selection>, - - #[serde(default)] - max_scroll_limit: usize, - - /// Range for URL hover highlights - #[serde(default)] - pub url_highlight: Option<RangeInclusive<index::Linear>>, -} - -#[derive(Copy, Clone)] -pub enum Scroll { - Lines(isize), - PageUp, - PageDown, - Top, - Bottom, -} - -#[derive(Copy, Clone)] -pub enum ViewportPosition { - Visible(Line), - Above, - Below, -} - -impl<T: GridCell + Copy + Clone> Grid<T> { - pub fn new(lines: index::Line, cols: index::Column, scrollback: usize, template: T) -> Grid<T> { - let raw = Storage::with_capacity(lines, Row::new(cols, &template)); - Grid { - raw, - cols, - lines, - display_offset: 0, - scroll_limit: 0, - selection: None, - max_scroll_limit: scrollback, - url_highlight: None, - } - } - - pub fn visible_to_buffer(&self, point: Point) -> Point<usize> { - Point { line: self.visible_line_to_buffer(point.line), col: point.col } - } - - pub fn buffer_line_to_visible(&self, line: usize) -> ViewportPosition { - let offset = line.saturating_sub(self.display_offset); - if line < self.display_offset { - ViewportPosition::Below - } else if offset >= *self.num_lines() { - ViewportPosition::Above - } else { - ViewportPosition::Visible(self.lines - offset - 1) - } - } - - pub fn visible_line_to_buffer(&self, line: Line) -> usize { - self.line_to_offset(line) + self.display_offset - } - - /// Update the size of the scrollback history - pub fn update_history(&mut self, history_size: usize, template: &T) { - self.raw.update_history(history_size, Row::new(self.cols, &template)); - self.max_scroll_limit = history_size; - self.scroll_limit = min(self.scroll_limit, history_size); - self.display_offset = min(self.display_offset, self.scroll_limit); - } - - pub fn scroll_display(&mut self, scroll: Scroll) { - match scroll { - Scroll::Lines(count) => { - self.display_offset = min( - max((self.display_offset as isize) + count, 0isize) as usize, - self.scroll_limit, - ); - }, - Scroll::PageUp => { - self.display_offset = min(self.display_offset + self.lines.0, self.scroll_limit); - }, - Scroll::PageDown => { - self.display_offset -= min(self.display_offset, self.lines.0); - }, - Scroll::Top => self.display_offset = self.scroll_limit, - Scroll::Bottom => self.display_offset = 0, - } - } - - pub fn resize( - &mut self, - lines: index::Line, - cols: index::Column, - cursor_pos: &mut Point, - template: &T, - ) { - // Check that there's actually work to do and return early if not - if lines == self.lines && cols == self.cols { - return; - } - - match self.lines.cmp(&lines) { - Ordering::Less => self.grow_lines(lines, template), - Ordering::Greater => self.shrink_lines(lines), - Ordering::Equal => (), - } - - match self.cols.cmp(&cols) { - Ordering::Less => self.grow_cols(cols, cursor_pos, template), - Ordering::Greater => self.shrink_cols(cols, template), - Ordering::Equal => (), - } - } - - fn increase_scroll_limit(&mut self, count: usize, template: &T) { - self.scroll_limit = min(self.scroll_limit + count, self.max_scroll_limit); - - // Initialize new lines when the history buffer is smaller than the scroll limit - let history_size = self.raw.len().saturating_sub(*self.lines); - if history_size < self.scroll_limit { - let new = min( - max(self.scroll_limit - history_size, MIN_INIT_SIZE), - self.max_scroll_limit - history_size, - ); - self.raw.initialize(new, Row::new(self.cols, template)); - } - } - - fn decrease_scroll_limit(&mut self, count: usize) { - self.scroll_limit = self.scroll_limit.saturating_sub(count); - } - - /// Add lines to the visible area - /// - /// 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(&mut self, new_line_count: index::Line, template: &T) { - let lines_added = new_line_count - self.lines; - - // Need to "resize" before updating buffer - self.raw.grow_visible_lines(new_line_count, Row::new(self.cols, template)); - self.lines = new_line_count; - - // Move existing lines up if there is no scrollback to fill new lines - if lines_added.0 > self.scroll_limit { - let scroll_lines = lines_added - self.scroll_limit; - self.scroll_up(&(Line(0)..new_line_count), scroll_lines, template); - } - - self.scroll_limit = self.scroll_limit.saturating_sub(*lines_added); - self.display_offset = self.display_offset.saturating_sub(*lines_added); - } - - fn grow_cols(&mut self, cols: index::Column, cursor_pos: &mut Point, template: &T) { - // Truncate all buffered lines - self.raw.grow_hidden(cols, template); - - let max_lines = self.lines.0 + self.max_scroll_limit; - - // Iterate backwards with indices for mutation during iteration - let mut i = self.raw.len(); - while i > 0 { - i -= 1; - - // Grow the current line if there's wrapped content available - while i >= 1 - && self.raw[i].len() < cols.0 - && self.raw[i].last().map(GridCell::is_wrap) == Some(true) - { - // Remove wrap flag before appending additional cells - if let Some(cell) = self.raw[i].last_mut() { - cell.set_wrap(false); - } - - // Append as many cells from the next line as possible - let len = min(self.raw[i - 1].len(), cols.0 - self.raw[i].len()); - let mut cells = self.raw[i - 1].front_split_off(len); - self.raw[i].append(&mut cells); - - if self.raw[i - 1].is_empty() { - // Remove following line if all cells have been drained - self.raw.remove(i - 1); - - if self.raw.len() < self.lines.0 || self.scroll_limit == 0 { - // Add new line and move lines up if we can't pull from history - self.raw.insert(0, Row::new(cols, template), max_lines); - cursor_pos.line = Line(cursor_pos.line.saturating_sub(1)); - } else { - // Make sure viewport doesn't move if line is outside of the visible area - if i < self.display_offset { - self.display_offset = self.display_offset.saturating_sub(1); - } - - // Remove one line from scrollback, since we just moved it to the viewport - self.scroll_limit = self.scroll_limit.saturating_sub(1); - self.display_offset = min(self.display_offset, self.scroll_limit); - i -= 1; - } - } else if let Some(cell) = self.raw[i].last_mut() { - // Set wrap flag if next line still has cells - cell.set_wrap(true); - } - } - - // Fill remaining cells - if self.raw[i].len() < cols.0 { - self.raw[i].grow(cols, template); - } - } - - self.cols = cols; - } - - fn shrink_cols(&mut self, cols: index::Column, template: &T) { - // Truncate all buffered lines - self.raw.shrink_hidden(cols); - - let max_lines = self.lines.0 + self.max_scroll_limit; - - // Iterate backwards with indices for mutation during iteration - let mut i = self.raw.len(); - while i > 0 { - i -= 1; - - if let Some(mut new_row) = self.raw[i].shrink(cols) { - // Set line as wrapped if cells got removed - if let Some(cell) = self.raw[i].last_mut() { - cell.set_wrap(true); - } - - if Some(true) == new_row.last().map(|c| c.is_wrap() && i >= 1) - && new_row.len() < cols.0 - { - // Make sure previous wrap flag doesn't linger around - if let Some(cell) = new_row.last_mut() { - cell.set_wrap(false); - } - - // Add removed cells to start of next row - self.raw[i - 1].append_front(new_row); - } else { - // Make sure viewport doesn't move if line is outside of the visible area - if i < self.display_offset { - self.display_offset = min(self.display_offset + 1, self.max_scroll_limit); - } - - // Make sure new row is at least as long as new width - let occ = new_row.len(); - if occ < cols.0 { - new_row.append(&mut vec![*template; cols.0 - occ]); - } - let row = Row::from_vec(new_row, occ); - - // Add new row with all removed cells - self.raw.insert(i, row, max_lines); - - // Increase scrollback history - self.scroll_limit = min(self.scroll_limit + 1, self.max_scroll_limit); - - // Since inserted might exceed cols, we need to check the same line again - i += 1; - } - } - } - - self.cols = cols; - } - - /// Remove lines from the visible area - /// - /// The behavior in Terminal.app and iTerm.app is to keep the cursor at the - /// bottom of the screen. This is achieved by pushing history "out the top" - /// of the terminal window. - /// - /// Alacritty takes the same approach. - fn shrink_lines(&mut self, target: index::Line) { - let prev = self.lines; - - self.selection = None; - self.url_highlight = None; - self.raw.rotate(*prev as isize - *target as isize); - self.raw.shrink_visible_lines(target); - self.lines = target; - } - - /// Convert a Line index (active region) to a buffer offset - /// - /// # Panics - /// - /// This method will panic if `Line` is larger than the grid dimensions - pub fn line_to_offset(&self, line: index::Line) -> usize { - assert!(line < self.num_lines()); - - *(self.num_lines() - line - 1) - } - - #[inline] - pub fn scroll_down( - &mut self, - region: &Range<index::Line>, - positions: index::Line, - template: &T, - ) { - // Whether or not there is a scrolling region active, as long as it - // starts at the top, we can do a full rotation which just involves - // changing the start index. - // - // To accomodate scroll regions, rows are reordered at the end. - if region.start == Line(0) { - // Rotate the entire line buffer. If there's a scrolling region - // active, the bottom lines are restored in the next step. - self.raw.rotate_up(*positions); - if let Some(ref mut selection) = self.selection { - selection.rotate(-(*positions as isize)); - } - self.url_highlight = None; - - self.decrease_scroll_limit(*positions); - - // Now, restore any scroll region lines - let lines = self.lines; - for i in IndexRange(region.end..lines) { - self.raw.swap_lines(i, i + positions); - } - - // Finally, reset recycled lines - for i in IndexRange(Line(0)..positions) { - self.raw[i].reset(&template); - } - } else { - // Subregion rotation - for line in IndexRange((region.start + positions)..region.end).rev() { - self.raw.swap_lines(line, line - positions); - } - - for line in IndexRange(region.start..(region.start + positions)) { - self.raw[line].reset(&template); - } - } - } - - /// scroll_up moves lines at the bottom towards the top - /// - /// This is the performance-sensitive part of scrolling. - pub fn scroll_up(&mut self, region: &Range<index::Line>, positions: index::Line, template: &T) { - if region.start == Line(0) { - // Update display offset when not pinned to active area - if self.display_offset != 0 { - self.display_offset = - min(self.display_offset + *positions, self.len() - self.num_lines().0); - } - - self.increase_scroll_limit(*positions, template); - - // Rotate the entire line buffer. If there's a scrolling region - // active, the bottom lines are restored in the next step. - self.raw.rotate(-(*positions as isize)); - if let Some(ref mut selection) = self.selection { - selection.rotate(*positions as isize); - } - self.url_highlight = None; - - // // This next loop swaps "fixed" lines outside of a scroll region - // // back into place after the rotation. The work is done in buffer- - // // space rather than terminal-space to avoid redundant - // // transformations. - let fixed_lines = *self.num_lines() - *region.end; - - for i in 0..fixed_lines { - self.raw.swap(i, i + *positions); - } - - // Finally, reset recycled lines - // - // Recycled lines are just above the end of the scrolling region. - for i in 0..*positions { - self.raw[i + fixed_lines].reset(&template); - } - } else { - // Subregion rotation - for line in IndexRange(region.start..(region.end - positions)) { - self.raw.swap_lines(line, line + positions); - } - - // Clear reused lines - for line in IndexRange((region.end - positions)..region.end) { - self.raw[line].reset(&template); - } - } - } - - // Completely reset the grid state - pub fn reset(&mut self, template: &T) { - // Explicitly purge all lines from history - let shrinkage = self.raw.len() - self.lines.0; - self.raw.shrink_lines(shrinkage); - self.clear_history(); - - // Reset all visible lines - for row in 0..self.raw.len() { - self.raw[row].reset(template); - } - - self.display_offset = 0; - self.selection = None; - self.url_highlight = None; - } -} - -#[allow(clippy::len_without_is_empty)] -impl<T> Grid<T> { - #[inline] - pub fn num_lines(&self) -> index::Line { - self.lines - } - - pub fn display_iter(&self) -> DisplayIter<'_, T> { - DisplayIter::new(self) - } - - #[inline] - pub fn num_cols(&self) -> index::Column { - self.cols - } - - pub fn clear_history(&mut self) { - self.scroll_limit = 0; - } - - #[inline] - pub fn scroll_limit(&self) -> usize { - self.scroll_limit - } - - /// Total number of lines in the buffer, this includes scrollback + visible lines - #[inline] - pub fn len(&self) -> usize { - self.raw.len() - } - - #[inline] - pub fn history_size(&self) -> usize { - self.raw.len().saturating_sub(*self.lines) - } - - /// This is used only for initializing after loading ref-tests - pub fn initialize_all(&mut self, template: &T) - where - T: Copy, - { - let history_size = self.raw.len().saturating_sub(*self.lines); - self.raw.initialize(self.max_scroll_limit - history_size, Row::new(self.cols, template)); - } - - /// This is used only for truncating before saving ref-tests - pub fn truncate(&mut self) { - self.raw.truncate(); - } - - pub fn iter_from(&self, point: Point<usize>) -> GridIterator<'_, T> { - GridIterator { grid: self, cur: point } - } - - #[inline] - pub fn contains(&self, point: &Point) -> bool { - self.lines > point.line && self.cols > point.col - } - - #[inline] - pub fn display_offset(&self) -> usize { - self.display_offset - } -} - -pub struct GridIterator<'a, T> { - /// Immutable grid reference - grid: &'a Grid<T>, - - /// Current position of the iterator within the grid. - pub cur: Point<usize>, -} - -impl<'a, T> Iterator for GridIterator<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<Self::Item> { - let last_col = self.grid.num_cols() - Column(1); - match self.cur { - Point { line, col } if line == 0 && col == last_col => None, - Point { col, .. } if (col == last_col) => { - self.cur.line -= 1; - self.cur.col = Column(0); - Some(&self.grid[self.cur.line][self.cur.col]) - }, - _ => { - self.cur.col += Column(1); - Some(&self.grid[self.cur.line][self.cur.col]) - }, - } - } -} - -impl<'a, T> BidirectionalIterator for GridIterator<'a, T> { - fn prev(&mut self) -> Option<Self::Item> { - let num_cols = self.grid.num_cols(); - - match self.cur { - Point { line, col: Column(0) } if line == self.grid.len() - 1 => None, - Point { col: Column(0), .. } => { - self.cur.line += 1; - self.cur.col = num_cols - Column(1); - Some(&self.grid[self.cur.line][self.cur.col]) - }, - _ => { - self.cur.col -= Column(1); - Some(&self.grid[self.cur.line][self.cur.col]) - }, - } - } -} - -/// Index active region by line -impl<T> Index<index::Line> for Grid<T> { - type Output = Row<T>; - - #[inline] - fn index(&self, index: index::Line) -> &Row<T> { - &self.raw[index] - } -} - -/// 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<index::Line> for Grid<T> { - #[inline] - fn index_mut(&mut self, index: index::Line) -> &mut Row<T> { - &mut 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<'point, T> Index<&'point Point> for Grid<T> { - type Output = T; - - #[inline] - fn index<'a>(&'a self, point: &Point) -> &'a T { - &self[point.line][point.col] - } -} - -impl<'point, T> IndexMut<&'point Point> for Grid<T> { - #[inline] - fn index_mut<'a, 'b>(&'a mut self, point: &'b Point) -> &'a mut T { - &mut self[point.line][point.col] - } -} - -// ------------------------------------------------------------------------------------------------- -// REGIONS -// ------------------------------------------------------------------------------------------------- - -/// 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>, -} - -/// 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>, -} - -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) - } - } - } -} - -pub trait IndexRegion<I, T> { - /// Get an immutable region of Self - fn region(&self, _: I) -> Region<'_, T>; - - /// Get a mutable region of Self - fn region_mut(&mut self, _: I) -> RegionMut<'_, T>; -} - -impl<T> IndexRegion<Range<Line>, T> for Grid<T> { - fn region(&self, index: Range<Line>) -> Region<'_, T> { - assert!(index.start < self.num_lines()); - assert!(index.end <= self.num_lines()); - assert!(index.start <= index.end); - Region { start: index.start, end: index.end, raw: &self.raw } - } - - fn region_mut(&mut self, index: Range<Line>) -> RegionMut<'_, T> { - assert!(index.start < self.num_lines()); - assert!(index.end <= self.num_lines()); - assert!(index.start <= index.end); - RegionMut { start: index.start, end: index.end, raw: &mut self.raw } - } -} - -impl<T> IndexRegion<RangeTo<Line>, T> for Grid<T> { - fn region(&self, index: RangeTo<Line>) -> Region<'_, T> { - assert!(index.end <= self.num_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.num_lines()); - RegionMut { start: Line(0), end: index.end, raw: &mut self.raw } - } -} - -impl<T> IndexRegion<RangeFrom<Line>, T> for Grid<T> { - fn region(&self, index: RangeFrom<Line>) -> Region<'_, T> { - assert!(index.start < self.num_lines()); - Region { start: index.start, end: self.num_lines(), raw: &self.raw } - } - - fn region_mut(&mut self, index: RangeFrom<Line>) -> RegionMut<'_, T> { - assert!(index.start < self.num_lines()); - RegionMut { start: index.start, end: self.num_lines(), raw: &mut self.raw } - } -} - -impl<T> IndexRegion<RangeFull, T> for Grid<T> { - fn region(&self, _: RangeFull) -> Region<'_, T> { - Region { start: Line(0), end: self.num_lines(), raw: &self.raw } - } - - fn region_mut(&mut self, _: RangeFull) -> RegionMut<'_, T> { - RegionMut { start: Line(0), end: self.num_lines(), raw: &mut self.raw } - } -} - -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 } - } -} - -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 } - } -} - -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 - } - } -} - -impl<'a, T> Iterator for RegionIterMut<'a, T> { - type Item = &'a mut Row<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 - } - } -} - -// ------------------------------------------------------------------------------------------------- -// DISPLAY ITERATOR -// ------------------------------------------------------------------------------------------------- - -/// Iterates over the visible area accounting for buffer transform -pub struct DisplayIter<'a, T> { - 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.num_lines() - 1; - let limit = grid.display_offset; - let col = Column(0); - let line = Line(0); - - DisplayIter { grid, offset, col, limit, line } - } - - pub fn offset(&self) -> usize { - self.offset - } - - pub fn column(&self) -> Column { - self.col - } - - pub fn line(&self) -> Line { - self.line - } -} - -impl<'a, T: Copy + 'a> Iterator for DisplayIter<'a, T> { - type Item = Indexed<T>; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - // Return None if we've reached the end. - if self.offset == self.limit && self.grid.num_cols() == self.col { - return None; - } - - // Get the next item. - let item = Some(Indexed { - inner: self.grid.raw[self.offset][self.col], - line: self.line, - column: self.col, - }); - - // Update line/col to point to next item - self.col += 1; - if self.col == self.grid.num_cols() && self.offset != self.limit { - self.offset -= 1; - - self.col = Column(0); - self.line = Line(*self.grid.lines - 1 - (self.offset - self.limit)); - } - - item - } -} diff --git a/src/grid/row.rs b/src/grid/row.rs deleted file mode 100644 index 88a23871..00000000 --- a/src/grid/row.rs +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2016 Joe Wilm, The Alacritty Project Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Defines the Row type which makes up lines in the grid - -use std::cmp::{max, min}; -use std::ops::{Index, IndexMut}; -use std::ops::{Range, RangeFrom, RangeFull, RangeTo, RangeToInclusive}; -use std::slice; - -use crate::grid::GridCell; -use crate::index::Column; - -/// A row in the grid -#[derive(Default, Clone, Debug, Serialize, Deserialize)] -pub struct Row<T> { - inner: Vec<T>, - - /// occupied entries - /// - /// Semantically, this value can be understood as the **end** of an - /// Exclusive Range. Thus, - /// - /// - Zero means there are no occupied entries - /// - 1 means there is a value at index zero, but nowhere else - /// - `occ == inner.len` means every value is occupied - pub(crate) occ: usize, -} - -impl<T: PartialEq> PartialEq for Row<T> { - fn eq(&self, other: &Self) -> bool { - self.inner == other.inner - } -} - -impl<T: Copy> Row<T> { - pub fn new(columns: Column, template: &T) -> Row<T> { - Row { inner: vec![*template; *columns], occ: 0 } - } - - pub fn grow(&mut self, cols: Column, template: &T) { - if self.inner.len() >= cols.0 { - return; - } - - self.inner.append(&mut vec![*template; cols.0 - self.len()]); - } - - pub fn shrink(&mut self, cols: Column) -> Option<Vec<T>> - where - T: GridCell, - { - if self.inner.len() <= cols.0 { - return None; - } - - // Split off cells for a new row - let mut new_row = self.inner.split_off(cols.0); - 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); - - if new_row.is_empty() { - None - } else { - Some(new_row) - } - } - - /// Resets contents to the contents of `other` - #[inline(never)] - pub fn reset(&mut self, other: &T) { - for item in &mut self.inner[..self.occ] { - *item = *other; - } - self.occ = 0; - } -} - -#[allow(clippy::len_without_is_empty)] -impl<T> Row<T> { - #[inline] - pub fn from_vec(vec: Vec<T>, occ: usize) -> Row<T> { - Row { inner: vec, occ } - } - - #[inline] - pub fn len(&self) -> usize { - self.inner.len() - } - - #[inline] - pub fn last(&self) -> Option<&T> { - self.inner.last() - } - - #[inline] - pub fn last_mut(&mut self) -> Option<&mut T> { - self.occ = self.inner.len(); - self.inner.last_mut() - } - - #[inline] - pub fn append(&mut self, vec: &mut Vec<T>) - where - T: GridCell, - { - self.inner.append(vec); - self.occ = self.inner.iter().rposition(|c| !c.is_empty()).map(|i| i + 1).unwrap_or(0); - } - - #[inline] - pub fn append_front(&mut self, mut vec: Vec<T>) { - self.occ += vec.len(); - vec.append(&mut self.inner); - self.inner = vec; - } - - #[inline] - pub fn is_empty(&self) -> bool - where - T: GridCell, - { - self.inner.iter().all(GridCell::is_empty) - } - - #[inline] - pub fn front_split_off(&mut self, at: usize) -> Vec<T> { - self.occ = self.occ.saturating_sub(at); - - let mut split = self.inner.split_off(at); - std::mem::swap(&mut split, &mut self.inner); - split - } -} - -impl<'a, T> IntoIterator for &'a mut Row<T> { - type IntoIter = slice::IterMut<'a, T>; - type Item = &'a mut T; - - #[inline] - fn into_iter(self) -> slice::IterMut<'a, T> { - self.occ = self.len(); - self.inner.iter_mut() - } -} - -impl<T> Index<Column> for Row<T> { - type Output = T; - - #[inline] - fn index(&self, index: Column) -> &T { - &self.inner[index.0] - } -} - -impl<T> IndexMut<Column> for Row<T> { - #[inline] - fn index_mut(&mut self, index: Column) -> &mut T { - self.occ = max(self.occ, *index + 1); - &mut self.inner[index.0] - } -} - -// ----------------------------------------------------------------------------- -// Index ranges of columns -// ----------------------------------------------------------------------------- - -impl<T> Index<Range<Column>> for Row<T> { - type Output = [T]; - - #[inline] - fn index(&self, index: Range<Column>) -> &[T] { - &self.inner[(index.start.0)..(index.end.0)] - } -} - -impl<T> IndexMut<Range<Column>> for Row<T> { - #[inline] - fn index_mut(&mut self, index: Range<Column>) -> &mut [T] { - self.occ = max(self.occ, *index.end); - &mut self.inner[(index.start.0)..(index.end.0)] - } -} - -impl<T> Index<RangeTo<Column>> for Row<T> { - type Output = [T]; - - #[inline] - fn index(&self, index: RangeTo<Column>) -> &[T] { - &self.inner[..(index.end.0)] - } -} - -impl<T> IndexMut<RangeTo<Column>> for Row<T> { - #[inline] - fn index_mut(&mut self, index: RangeTo<Column>) -> &mut [T] { - self.occ = max(self.occ, *index.end); - &mut self.inner[..(index.end.0)] - } -} - -impl<T> Index<RangeFrom<Column>> for Row<T> { - type Output = [T]; - - #[inline] - fn index(&self, index: RangeFrom<Column>) -> &[T] { - &self.inner[(index.start.0)..] - } -} - -impl<T> IndexMut<RangeFrom<Column>> for Row<T> { - #[inline] - fn index_mut(&mut self, index: RangeFrom<Column>) -> &mut [T] { - self.occ = self.len(); - &mut self.inner[(index.start.0)..] - } -} - -impl<T> Index<RangeFull> for Row<T> { - type Output = [T]; - - #[inline] - fn index(&self, _: RangeFull) -> &[T] { - &self.inner[..] - } -} - -impl<T> IndexMut<RangeFull> for Row<T> { - #[inline] - fn index_mut(&mut self, _: RangeFull) -> &mut [T] { - self.occ = self.len(); - &mut self.inner[..] - } -} - -impl<T> Index<RangeToInclusive<Column>> for Row<T> { - type Output = [T]; - - #[inline] - fn index(&self, index: RangeToInclusive<Column>) -> &[T] { - &self.inner[..=(index.end.0)] - } -} - -impl<T> IndexMut<RangeToInclusive<Column>> for Row<T> { - #[inline] - fn index_mut(&mut self, index: RangeToInclusive<Column>) -> &mut [T] { - self.occ = max(self.occ, *index.end); - &mut self.inner[..=(index.end.0)] - } -} diff --git a/src/grid/storage.rs b/src/grid/storage.rs deleted file mode 100644 index 32260426..00000000 --- a/src/grid/storage.rs +++ /dev/null @@ -1,922 +0,0 @@ -/// Wrapper around Vec which supports fast indexing and rotation -/// -/// The rotation implemented by grid::Storage is a simple integer addition. -/// Compare with standard library rotation which requires rearranging items in -/// memory. -/// -/// As a consequence, the indexing operators need to be reimplemented for this -/// type to account for the 0th element not always being at the start of the -/// allocation. -/// -/// Because certain Vec operations are no longer valid on this type, no Deref -/// implementation is provided. Anything from Vec that should be exposed must be -/// done so manually. -use std::ops::{Index, IndexMut}; - -use static_assertions::assert_eq_size; - -use super::Row; -use crate::grid::GridCell; -use crate::index::{Column, Line}; - -/// Maximum number of invisible lines before buffer is resized -const TRUNCATE_STEP: usize = 100; - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Storage<T> { - inner: Vec<Row<T>>, - zero: usize, - visible_lines: Line, - - /// Total number of lines currently active in the terminal (scrollback + visible) - /// - /// Shrinking this length allows reducing the number of lines in the scrollback buffer without - /// having to truncate the raw `inner` buffer. - /// As long as `len` is bigger than `inner`, it is also possible to grow the scrollback buffer - /// without any additional insertions. - #[serde(default)] - len: usize, -} - -impl<T: PartialEq> ::std::cmp::PartialEq for Storage<T> { - fn eq(&self, other: &Self) -> bool { - // Make sure length is equal - if self.inner.len() != other.inner.len() { - return false; - } - - // Check which vec has the bigger zero - let (ref bigger, ref smaller) = - if self.zero >= other.zero { (self, other) } else { (other, self) }; - - // Calculate the actual zero offset - let len = self.inner.len(); - let bigger_zero = bigger.zero % len; - let smaller_zero = smaller.zero % len; - - // Compare the slices in chunks - // Chunks: - // - Bigger zero to the end - // - Remaining lines in smaller zero vec - // - Beginning of smaller zero vec - // - // Example: - // Bigger Zero (6): - // 4 5 6 | 7 8 9 | 0 1 2 3 - // C2 C2 C2 | C3 C3 C3 | C1 C1 C1 C1 - // Smaller Zero (3): - // 7 8 9 | 0 1 2 3 | 4 5 6 - // C3 C3 C3 | C1 C1 C1 C1 | C2 C2 C2 - bigger.inner[bigger_zero..] - == smaller.inner[smaller_zero..smaller_zero + (len - bigger_zero)] - && bigger.inner[..bigger_zero - smaller_zero] - == smaller.inner[smaller_zero + (len - bigger_zero)..] - && bigger.inner[bigger_zero - smaller_zero..bigger_zero] - == smaller.inner[..smaller_zero] - } -} - -impl<T> Storage<T> { - #[inline] - pub fn with_capacity(lines: Line, template: Row<T>) -> Storage<T> - where - T: Clone, - { - // Initialize visible lines, the scrollback buffer is initialized dynamically - let inner = vec![template; lines.0]; - - Storage { inner, zero: 0, visible_lines: lines - 1, len: lines.0 } - } - - /// Update the size of the scrollback history - pub fn update_history(&mut self, history_size: usize, template_row: Row<T>) - where - T: Clone, - { - let current_history = self.len - (self.visible_lines.0 + 1); - if history_size > current_history { - self.grow_lines(history_size - current_history, template_row); - } else if history_size < current_history { - self.shrink_lines(current_history - history_size); - } - } - - /// Increase the number of lines in the buffer - pub fn grow_visible_lines(&mut self, next: Line, template_row: Row<T>) - where - T: Clone, - { - // Number of lines the buffer needs to grow - let growage = (next - (self.visible_lines + 1)).0; - self.grow_lines(growage, template_row); - - // Update visible lines - self.visible_lines = next - 1; - } - - /// Grow the number of lines in the buffer, filling new lines with the template - fn grow_lines(&mut self, growage: usize, template_row: Row<T>) - where - T: Clone, - { - // Only grow if there are not enough lines still hidden - let mut new_growage = 0; - if growage > (self.inner.len() - self.len) { - // Lines to grow additionally to invisible lines - new_growage = growage - (self.inner.len() - self.len); - - // Split off the beginning of the raw inner buffer - let mut start_buffer = self.inner.split_off(self.zero); - - // Insert new template rows at the end of the raw inner buffer - let mut new_lines = vec![template_row; new_growage]; - self.inner.append(&mut new_lines); - - // Add the start to the raw inner buffer again - self.inner.append(&mut start_buffer); - } - - // Update raw buffer length and zero offset - self.zero = (self.zero + new_growage) % self.inner.len(); - self.len += growage; - } - - /// Decrease the number of lines in the buffer - pub fn shrink_visible_lines(&mut self, next: Line) { - // Shrink the size without removing any lines - let shrinkage = (self.visible_lines - (next - 1)).0; - self.shrink_lines(shrinkage); - - // Update visible lines - self.visible_lines = next - 1; - } - - // Shrink the number of lines in the buffer - pub fn shrink_lines(&mut self, shrinkage: usize) { - self.len -= shrinkage; - - // Free memory - if self.inner.len() > self.len() + TRUNCATE_STEP { - self.truncate(); - } - } - - /// Truncate the invisible elements from the raw buffer - pub fn truncate(&mut self) { - self.inner.rotate_left(self.zero); - self.inner.truncate(self.len); - - self.zero = 0; - } - - /// Dynamically grow the storage buffer at runtime - pub fn initialize(&mut self, num_rows: usize, template_row: Row<T>) - where - T: Clone, - { - let mut new = vec![template_row; num_rows]; - - let mut split = self.inner.split_off(self.zero); - self.inner.append(&mut new); - self.inner.append(&mut split); - - self.zero += num_rows; - self.len += num_rows; - } - - #[inline] - pub fn len(&self) -> usize { - self.len - } - - #[inline] - /// Compute actual index in underlying storage given the requested index. - fn compute_index(&self, requested: usize) -> usize { - debug_assert!(requested < self.len); - let zeroed = requested + self.zero; - - // This part is critical for performance, - // so an if/else is used here instead of a moludo operation - if zeroed >= self.inner.len() { - zeroed - self.inner.len() - } else { - zeroed - } - } - - pub fn swap_lines(&mut self, a: Line, b: Line) { - let offset = self.inner.len() + self.zero + *self.visible_lines; - 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 - /// swap than going through slice::swap. - /// - /// 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) { - assert_eq_size!(Row<T>, [usize; 4]); - - let a = self.compute_index(a); - let b = self.compute_index(b); - - unsafe { - // Cast to a qword array to opt out of copy restrictions and avoid - // drop hazards. Byte array is no good here since for whatever - // reason LLVM won't optimized it. - let a_ptr = self.inner.as_mut_ptr().add(a) as *mut usize; - let b_ptr = self.inner.as_mut_ptr().add(b) as *mut usize; - - // Copy 1 qword at a time - // - // The optimizer unrolls this loop and vectorizes it. - let mut tmp: usize; - for i in 0..4 { - tmp = *a_ptr.offset(i); - *a_ptr.offset(i) = *b_ptr.offset(i); - *b_ptr.offset(i) = tmp; - } - } - } - - #[inline] - pub fn rotate(&mut self, count: isize) { - debug_assert!(count.abs() as usize <= self.inner.len()); - - let len = self.inner.len(); - self.zero = (self.zero as isize + count + len as isize) as usize % len; - } - - // Fast path - #[inline] - pub fn rotate_up(&mut self, count: usize) { - self.zero = (self.zero + count) % self.inner.len(); - } - - #[inline] - pub fn insert(&mut self, index: usize, row: Row<T>, max_lines: usize) { - let index = self.compute_index(index); - self.inner.insert(index, row); - - if index < self.zero { - self.zero += 1; - } - - if self.len < max_lines { - self.len += 1; - } - } - - #[inline] - pub fn remove(&mut self, index: usize) -> Row<T> { - let index = self.compute_index(index); - if index < self.zero { - self.zero -= 1; - } - self.len -= 1; - - self.inner.remove(index) - } - - /// Shrink columns of hidden buffered lines. - /// - /// XXX This suggests that Storage is a leaky abstraction. Ultimately, this - /// is needed because of the grow/shrink lines functionality. - #[inline] - pub fn shrink_hidden(&mut self, cols: Column) - where - T: GridCell + Copy, - { - let start = self.zero + self.len; - let end = self.zero + self.inner.len(); - for mut i in start..end { - if i >= self.inner.len() { - i -= self.inner.len(); - } - - self.inner[i].shrink(cols); - } - } - - /// Grow columns of hidden buffered lines. - /// - /// XXX This suggests that Storage is a leaky abstraction. Ultimately, this - /// is needed because of the grow/shrink lines functionality. - #[inline] - pub fn grow_hidden(&mut self, cols: Column, template: &T) - where - T: Copy + Clone, - { - let start = self.zero + self.len; - let end = self.zero + self.inner.len(); - for mut i in start..end { - if i >= self.inner.len() { - i -= self.inner.len(); - } - - self.inner[i].grow(cols, template); - } - } -} - -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 - index; - &self[*index] - } -} - -impl<T> IndexMut<Line> for Storage<T> { - #[inline] - fn index_mut(&mut self, index: Line) -> &mut Self::Output { - let index = self.visible_lines - index; - &mut self[*index] - } -} - -/// Grow the buffer one line at the end of the buffer -/// -/// Before: -/// 0: 0 <- Zero -/// 1: 1 -/// 2: - -/// After: -/// 0: - -/// 1: 0 <- Zero -/// 2: 1 -/// 3: - -#[test] -fn grow_after_zero() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'-'), - ], - zero: 0, - visible_lines: Line(2), - len: 3, - }; - - // Grow buffer - storage.grow_visible_lines(Line(4), Row::new(Column(1), &'-')); - - // Make sure the result is correct - let expected = Storage { - inner: vec![ - Row::new(Column(1), &'-'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'-'), - ], - zero: 1, - visible_lines: Line(0), - len: 4, - }; - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// Grow the buffer one line at the start of the buffer -/// -/// Before: -/// 0: - -/// 1: 0 <- Zero -/// 2: 1 -/// After: -/// 0: - -/// 1: - -/// 2: 0 <- Zero -/// 3: 1 -#[test] -fn grow_before_zero() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'-'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - ], - zero: 1, - visible_lines: Line(2), - len: 3, - }; - - // Grow buffer - storage.grow_visible_lines(Line(4), Row::new(Column(1), &'-')); - - // Make sure the result is correct - let expected = Storage { - inner: vec![ - Row::new(Column(1), &'-'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - ], - zero: 2, - visible_lines: Line(0), - len: 4, - }; - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// Shrink the buffer one line at the start of the buffer -/// -/// Before: -/// 0: 2 -/// 1: 0 <- Zero -/// 2: 1 -/// After: -/// 0: 2 <- Hidden -/// 0: 0 <- Zero -/// 1: 1 -#[test] -fn shrink_before_zero() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'2'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - ], - zero: 1, - visible_lines: Line(2), - len: 3, - }; - - // Shrink buffer - storage.shrink_visible_lines(Line(2)); - - // Make sure the result is correct - let expected = Storage { - inner: vec![ - Row::new(Column(1), &'2'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - ], - zero: 1, - visible_lines: Line(0), - len: 2, - }; - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// Shrink the buffer one line at the end of the buffer -/// -/// Before: -/// 0: 0 <- Zero -/// 1: 1 -/// 2: 2 -/// After: -/// 0: 0 <- Zero -/// 1: 1 -/// 2: 2 <- Hidden -#[test] -fn shrink_after_zero() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - ], - zero: 0, - visible_lines: Line(2), - len: 3, - }; - - // Shrink buffer - storage.shrink_visible_lines(Line(2)); - - // Make sure the result is correct - let expected = Storage { - inner: vec![ - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - ], - zero: 0, - visible_lines: Line(0), - len: 2, - }; - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// Shrink the buffer at the start and end of the buffer -/// -/// Before: -/// 0: 4 -/// 1: 5 -/// 2: 0 <- Zero -/// 3: 1 -/// 4: 2 -/// 5: 3 -/// After: -/// 0: 4 <- Hidden -/// 1: 5 <- Hidden -/// 2: 0 <- Zero -/// 3: 1 -/// 4: 2 <- Hidden -/// 5: 3 <- Hidden -#[test] -fn shrink_before_and_after_zero() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(5), - len: 6, - }; - - // Shrink buffer - storage.shrink_visible_lines(Line(2)); - - // Make sure the result is correct - let expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 2, - }; - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// Check that when truncating all hidden lines are removed from the raw buffer -/// -/// Before: -/// 0: 4 <- Hidden -/// 1: 5 <- Hidden -/// 2: 0 <- Zero -/// 3: 1 -/// 4: 2 <- Hidden -/// 5: 3 <- Hidden -/// After: -/// 0: 0 <- Zero -/// 1: 1 -#[test] -fn truncate_invisible_lines() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(1), - len: 2, - }; - - // Truncate buffer - storage.truncate(); - - // Make sure the result is correct - let expected = Storage { - inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1')], - zero: 0, - visible_lines: Line(1), - len: 2, - }; - assert_eq!(storage.visible_lines, expected.visible_lines); - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// Truncate buffer only at the beginning -/// -/// Before: -/// 0: 1 -/// 1: 2 <- Hidden -/// 2: 0 <- Zero -/// After: -/// 0: 1 -/// 0: 0 <- Zero -#[test] -fn truncate_invisible_lines_beginning() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'0'), - ], - zero: 2, - visible_lines: Line(1), - len: 2, - }; - - // Truncate buffer - storage.truncate(); - - // Make sure the result is correct - let expected = Storage { - inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1')], - zero: 0, - visible_lines: Line(1), - len: 2, - }; - assert_eq!(storage.visible_lines, expected.visible_lines); - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// First shrink the buffer and then grow it again -/// -/// Before: -/// 0: 4 -/// 1: 5 -/// 2: 0 <- Zero -/// 3: 1 -/// 4: 2 -/// 5: 3 -/// After Shrinking: -/// 0: 4 <- Hidden -/// 1: 5 <- Hidden -/// 2: 0 <- Zero -/// 3: 1 -/// 4: 2 -/// 5: 3 <- Hidden -/// After Growing: -/// 0: 4 -/// 1: 5 -/// 2: - -/// 3: 0 <- Zero -/// 4: 1 -/// 5: 2 -/// 6: 3 -#[test] -fn shrink_then_grow() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - - // Shrink buffer - storage.shrink_lines(3); - - // Make sure the result after shrinking is correct - let shrinking_expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 3, - }; - assert_eq!(storage.inner, shrinking_expected.inner); - assert_eq!(storage.zero, shrinking_expected.zero); - assert_eq!(storage.len, shrinking_expected.len); - - // Grow buffer - storage.grow_lines(4, Row::new(Column(1), &'-')); - - // Make sure the result after shrinking is correct - let growing_expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 3, - visible_lines: Line(0), - len: 7, - }; - assert_eq!(storage.inner, growing_expected.inner); - assert_eq!(storage.zero, growing_expected.zero); - assert_eq!(storage.len, growing_expected.len); -} - -#[test] -fn initialize() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - - // Initialize additional lines - storage.initialize(3, Row::new(Column(1), &'-')); - - // Make sure the lines are present and at the right location - let shrinking_expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 5, - visible_lines: Line(0), - len: 9, - }; - assert_eq!(storage.inner, shrinking_expected.inner); - assert_eq!(storage.zero, shrinking_expected.zero); - assert_eq!(storage.len, shrinking_expected.len); -} - -#[test] -fn insert() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - - // Initialize additional lines - storage.insert(2, Row::new(Column(1), &'-'), 100); - - // Make sure the lines are present and at the right location - let shrinking_expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 7, - }; - assert_eq!(storage.inner, shrinking_expected.inner); - assert_eq!(storage.zero, shrinking_expected.zero); - assert_eq!(storage.len, shrinking_expected.len); -} - -#[test] -fn insert_truncate_max() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - - // Initialize additional lines - storage.insert(2, Row::new(Column(1), &'-'), 6); - - // Make sure the lines are present and at the right location - let shrinking_expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - assert_eq!(storage.inner, shrinking_expected.inner); - assert_eq!(storage.zero, shrinking_expected.zero); - assert_eq!(storage.len, shrinking_expected.len); -} - -#[test] -fn insert_at_zero() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - - // Initialize additional lines - storage.insert(0, Row::new(Column(1), &'-'), 6); - - // Make sure the lines are present and at the right location - let shrinking_expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - assert_eq!(storage.inner, shrinking_expected.inner); - assert_eq!(storage.zero, shrinking_expected.zero); - assert_eq!(storage.len, shrinking_expected.len); -} diff --git a/src/grid/tests.rs b/src/grid/tests.rs deleted file mode 100644 index fc41fdc6..00000000 --- a/src/grid/tests.rs +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2016 Joe Wilm, The Alacritty Project Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Tests for the Gird - -use super::{BidirectionalIterator, Grid}; -use crate::grid::GridCell; -use crate::index::{Column, Line, Point}; -use crate::term::cell::{Cell, Flags}; - -impl GridCell for usize { - fn is_empty(&self) -> bool { - false - } - - fn is_wrap(&self) -> bool { - false - } - - fn set_wrap(&mut self, _wrap: bool) {} -} - -// Scroll up moves lines upwards -#[test] -fn scroll_up() { - let mut grid = Grid::new(Line(10), Column(1), 0, 0); - for i in 0..10 { - grid[Line(i)][Column(0)] = i; - } - - grid.scroll_up(&(Line(0)..Line(10)), Line(2), &0); - - assert_eq!(grid[Line(0)][Column(0)], 2); - assert_eq!(grid[Line(0)].occ, 1); - assert_eq!(grid[Line(1)][Column(0)], 3); - assert_eq!(grid[Line(1)].occ, 1); - assert_eq!(grid[Line(2)][Column(0)], 4); - assert_eq!(grid[Line(2)].occ, 1); - assert_eq!(grid[Line(3)][Column(0)], 5); - assert_eq!(grid[Line(3)].occ, 1); - assert_eq!(grid[Line(4)][Column(0)], 6); - assert_eq!(grid[Line(4)].occ, 1); - assert_eq!(grid[Line(5)][Column(0)], 7); - assert_eq!(grid[Line(5)].occ, 1); - assert_eq!(grid[Line(6)][Column(0)], 8); - assert_eq!(grid[Line(6)].occ, 1); - assert_eq!(grid[Line(7)][Column(0)], 9); - assert_eq!(grid[Line(7)].occ, 1); - assert_eq!(grid[Line(8)][Column(0)], 0); // was 0 - assert_eq!(grid[Line(8)].occ, 0); - assert_eq!(grid[Line(9)][Column(0)], 0); // was 1 - assert_eq!(grid[Line(9)].occ, 0); -} - -// Scroll down moves lines downwards -#[test] -fn scroll_down() { - let mut grid = Grid::new(Line(10), Column(1), 0, 0); - for i in 0..10 { - grid[Line(i)][Column(0)] = i; - } - - grid.scroll_down(&(Line(0)..Line(10)), Line(2), &0); - - 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 that GridIterator works -#[test] -fn test_iter() { - let mut grid = Grid::new(Line(5), Column(5), 0, 0); - for i in 0..5 { - for j in 0..5 { - grid[Line(i)][Column(j)] = i * 5 + j; - } - } - - let mut iter = grid.iter_from(Point { line: 4, col: Column(0) }); - - assert_eq!(None, iter.prev()); - assert_eq!(Some(&1), iter.next()); - assert_eq!(Column(1), iter.cur.col); - assert_eq!(4, iter.cur.line); - - assert_eq!(Some(&2), iter.next()); - assert_eq!(Some(&3), iter.next()); - assert_eq!(Some(&4), iter.next()); - - // test linewrapping - assert_eq!(Some(&5), iter.next()); - assert_eq!(Column(0), iter.cur.col); - assert_eq!(3, iter.cur.line); - - assert_eq!(Some(&4), iter.prev()); - assert_eq!(Column(4), iter.cur.col); - assert_eq!(4, iter.cur.line); - - // test that iter ends at end of grid - let mut final_iter = grid.iter_from(Point { line: 0, col: Column(4) }); - assert_eq!(None, final_iter.next()); - assert_eq!(Some(&23), final_iter.prev()); -} - -#[test] -fn shrink_reflow() { - let mut grid = Grid::new(Line(1), Column(5), 2, cell('x')); - 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(Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default()); - - assert_eq!(grid.len(), 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[1].len(), 2); - assert_eq!(grid[1][Column(0)], cell('3')); - assert_eq!(grid[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()); -} - -#[test] -fn shrink_reflow_twice() { - let mut grid = Grid::new(Line(1), Column(5), 2, cell('x')); - 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(Line(1), Column(4), &mut Point::new(Line(0), Column(0)), &Cell::default()); - grid.resize(Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default()); - - assert_eq!(grid.len(), 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[1].len(), 2); - assert_eq!(grid[1][Column(0)], cell('3')); - assert_eq!(grid[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()); -} - -#[test] -fn shrink_reflow_empty_cell_inside_line() { - let mut grid = Grid::new(Line(1), Column(5), 3, cell('x')); - 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(Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default()); - - assert_eq!(grid.len(), 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[0].len(), 2); - assert_eq!(grid[0][Column(0)], cell('3')); - assert_eq!(grid[0][Column(1)], cell('4')); - - grid.resize(Line(1), Column(1), &mut Point::new(Line(0), Column(0)), &Cell::default()); - - assert_eq!(grid.len(), 4); - - assert_eq!(grid[3].len(), 1); - assert_eq!(grid[3][Column(0)], wrap_cell('1')); - - assert_eq!(grid[2].len(), 1); - assert_eq!(grid[2][Column(0)], wrap_cell(' ')); - - assert_eq!(grid[1].len(), 1); - assert_eq!(grid[1][Column(0)], wrap_cell('3')); - - assert_eq!(grid[0].len(), 1); - assert_eq!(grid[0][Column(0)], cell('4')); -} - -#[test] -fn grow_reflow() { - let mut grid = Grid::new(Line(2), Column(2), 0, cell('x')); - 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(Line(2), Column(3), &mut Point::new(Line(0), Column(0)), &Cell::default()); - - assert_eq!(grid.len(), 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')); - - // 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()); -} - -#[test] -fn grow_reflow_multiline() { - let mut grid = Grid::new(Line(3), Column(2), 0, cell('x')); - 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)] = wrap_cell('4'); - grid[Line(2)][Column(0)] = cell('5'); - grid[Line(2)][Column(1)] = cell('6'); - - grid.resize(Line(3), Column(6), &mut Point::new(Line(0), Column(0)), &Cell::default()); - - assert_eq!(grid.len(), 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')); - - // Make sure rest of grid is empty - // https://github.com/rust-lang/rust-clippy/issues/3788 - #[allow(clippy::needless_range_loop)] - for r in 0..2 { - assert_eq!(grid[r].len(), 6); - for c in 0..6 { - assert_eq!(grid[r][Column(c)], Cell::default()); - } - } -} - -fn cell(c: char) -> Cell { - let mut cell = Cell::default(); - cell.c = c; - cell -} - -fn wrap_cell(c: char) -> Cell { - let mut cell = cell(c); - cell.flags.insert(Flags::WRAPLINE); - cell -} |