summaryrefslogtreecommitdiff
path: root/alacritty_terminal/src
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
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')
-rw-r--r--alacritty_terminal/src/ansi.rs46
-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
-rw-r--r--alacritty_terminal/src/index.rs595
-rw-r--r--alacritty_terminal/src/selection.rs323
-rw-r--r--alacritty_terminal/src/term/cell.rs4
-rw-r--r--alacritty_terminal/src/term/mod.rs448
-rw-r--r--alacritty_terminal/src/term/search.rs261
-rw-r--r--alacritty_terminal/src/tty/unix.rs5
-rw-r--r--alacritty_terminal/src/tty/windows/conpty.rs9
-rw-r--r--alacritty_terminal/src/vi_mode.rs188
14 files changed, 1186 insertions, 1551 deletions
diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs
index b46aec0b..44c92d3e 100644
--- a/alacritty_terminal/src/ansi.rs
+++ b/alacritty_terminal/src/ansi.rs
@@ -299,13 +299,13 @@ pub trait Handler {
fn goto_col(&mut self, _: Column) {}
/// Insert blank characters in current line starting from cursor.
- fn insert_blank(&mut self, _: Column) {}
+ fn insert_blank(&mut self, _: usize) {}
/// Move cursor up `rows`.
- fn move_up(&mut self, _: Line) {}
+ fn move_up(&mut self, _: usize) {}
/// Move cursor down `rows`.
- fn move_down(&mut self, _: Line) {}
+ fn move_down(&mut self, _: usize) {}
/// Identify the terminal (should write back to the pty stream).
fn identify_terminal<W: io::Write>(&mut self, _: &mut W, _intermediate: Option<char>) {}
@@ -320,10 +320,10 @@ pub trait Handler {
fn move_backward(&mut self, _: Column) {}
/// Move cursor down `rows` and set to column 1.
- fn move_down_and_cr(&mut self, _: Line) {}
+ fn move_down_and_cr(&mut self, _: usize) {}
/// Move cursor up `rows` and set to column 1.
- fn move_up_and_cr(&mut self, _: Line) {}
+ fn move_up_and_cr(&mut self, _: usize) {}
/// Put `count` tabs.
fn put_tab(&mut self, _count: u16) {}
@@ -352,16 +352,16 @@ pub trait Handler {
fn set_horizontal_tabstop(&mut self) {}
/// Scroll up `rows` rows.
- fn scroll_up(&mut self, _: Line) {}
+ fn scroll_up(&mut self, _: usize) {}
/// Scroll down `rows` rows.
- fn scroll_down(&mut self, _: Line) {}
+ fn scroll_down(&mut self, _: usize) {}
/// Insert `count` blank lines.
- fn insert_blank_lines(&mut self, _: Line) {}
+ fn insert_blank_lines(&mut self, _: usize) {}
/// Delete `count` lines.
- fn delete_lines(&mut self, _: Line) {}
+ fn delete_lines(&mut self, _: usize) {}
/// Erase `count` chars in current line following cursor.
///
@@ -373,7 +373,7 @@ pub trait Handler {
///
/// Deleting a character is like the delete key on the keyboard - everything
/// to the right of the deleted things is shifted left.
- fn delete_chars(&mut self, _: Column) {}
+ fn delete_chars(&mut self, _: usize) {}
/// Move backward `count` tabs.
fn move_backward_tabs(&mut self, _count: u16) {}
@@ -1122,11 +1122,9 @@ where
};
match (action, intermediates) {
- ('@', []) => handler.insert_blank(Column(next_param_or(1) as usize)),
- ('A', []) => {
- handler.move_up(Line(next_param_or(1) as usize));
- },
- ('B', []) | ('e', []) => handler.move_down(Line(next_param_or(1) as usize)),
+ ('@', []) => handler.insert_blank(next_param_or(1) as usize),
+ ('A', []) => handler.move_up(next_param_or(1) as usize),
+ ('B', []) | ('e', []) => handler.move_down(next_param_or(1) as usize),
('b', []) => {
if let Some(c) = self.state.preceding_char {
for _ in 0..next_param_or(1) {
@@ -1141,9 +1139,9 @@ where
handler.identify_terminal(writer, intermediates.get(0).map(|&i| i as char))
},
('D', []) => handler.move_backward(Column(next_param_or(1) as usize)),
- ('d', []) => handler.goto_line(Line(next_param_or(1) as usize - 1)),
- ('E', []) => handler.move_down_and_cr(Line(next_param_or(1) as usize)),
- ('F', []) => handler.move_up_and_cr(Line(next_param_or(1) as usize)),
+ ('d', []) => handler.goto_line(Line(next_param_or(1) as i32 - 1)),
+ ('E', []) => handler.move_down_and_cr(next_param_or(1) as usize),
+ ('F', []) => handler.move_up_and_cr(next_param_or(1) as usize),
('G', []) | ('`', []) => handler.goto_col(Column(next_param_or(1) as usize - 1)),
('g', []) => {
let mode = match next_param_or(0) {
@@ -1158,7 +1156,7 @@ where
handler.clear_tabs(mode);
},
('H', []) | ('f', []) => {
- let y = next_param_or(1) as usize;
+ let y = next_param_or(1) as i32;
let x = next_param_or(1) as usize;
handler.goto(Line(y - 1), Column(x - 1));
},
@@ -1198,7 +1196,7 @@ where
handler.clear_line(mode);
},
- ('L', []) => handler.insert_blank_lines(Line(next_param_or(1) as usize)),
+ ('L', []) => handler.insert_blank_lines(next_param_or(1) as usize),
('l', intermediates) => {
for param in params_iter.map(|param| param[0]) {
match Mode::from_primitive(intermediates.get(0), param) {
@@ -1207,7 +1205,7 @@ where
}
}
},
- ('M', []) => handler.delete_lines(Line(next_param_or(1) as usize)),
+ ('M', []) => handler.delete_lines(next_param_or(1) as usize),
('m', []) => {
if params.is_empty() {
handler.terminal_attribute(Attr::Reset);
@@ -1221,7 +1219,7 @@ where
}
},
('n', []) => handler.device_status(writer, next_param_or(0) as usize),
- ('P', []) => handler.delete_chars(Column(next_param_or(1) as usize)),
+ ('P', []) => handler.delete_chars(next_param_or(1) as usize),
('q', [b' ']) => {
// DECSCUSR (CSI Ps SP q) -- Set Cursor Style.
let cursor_style_id = next_param_or(0);
@@ -1247,9 +1245,9 @@ where
handler.set_scrolling_region(top, bottom);
},
- ('S', []) => handler.scroll_up(Line(next_param_or(1) as usize)),
+ ('S', []) => handler.scroll_up(next_param_or(1) as usize),
('s', []) => handler.save_cursor_position(),
- ('T', []) => handler.scroll_down(Line(next_param_or(1) as usize)),
+ ('T', []) => handler.scroll_down(next_param_or(1) as usize),
('t', []) => match next_param_or(1) as usize {
14 => handler.text_area_size_pixels(writer),
18 => handler.text_area_size_chars(writer),
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
diff --git a/alacritty_terminal/src/index.rs b/alacritty_terminal/src/index.rs
index e8e52c80..29b3eb15 100644
--- a/alacritty_terminal/src/index.rs
+++ b/alacritty_terminal/src/index.rs
@@ -1,9 +1,9 @@
//! Line and Column newtypes for strongly typed tty/grid/terminal APIs.
/// Indexing types and implementations for Grid and Line.
-use std::cmp::{Ord, Ordering};
+use std::cmp::{max, min, Ord, Ordering};
use std::fmt;
-use std::ops::{self, Add, AddAssign, Deref, Range, Sub, SubAssign};
+use std::ops::{Add, AddAssign, Deref, Sub, SubAssign};
use serde::{Deserialize, Serialize};
@@ -28,176 +28,192 @@ impl Direction {
}
}
-/// Behavior for handling grid boundaries.
+/// Terminal grid boundaries.
pub enum Boundary {
- /// Clamp to grid boundaries.
+ /// Cursor's range of motion in the grid.
///
- /// When an operation exceeds the grid boundaries, the last point will be returned no matter
- /// how far the boundaries were exceeded.
- Clamp,
+ /// This is equal to the viewport when the user isn't scrolled into the history.
+ Cursor,
- /// Wrap around grid bondaries.
- ///
- /// When an operation exceeds the grid boundaries, the point will wrap around the entire grid
- /// history and continue at the other side.
- Wrap,
+ /// Topmost line in history until the bottommost line in the terminal.
+ Grid,
+
+ /// Unbounded.
+ None,
}
/// Index in the grid using row, column notation.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default, Eq, PartialEq)]
-pub struct Point<L = Line> {
+pub struct Point<L = Line, C = Column> {
pub line: L,
- pub column: Column,
+ pub column: C,
}
-impl<L> Point<L> {
- pub fn new(line: L, col: Column) -> Point<L> {
- Point { line, column: col }
- }
-
- #[inline]
- #[must_use = "this returns the result of the operation, without modifying the original"]
- pub fn sub(mut self, num_cols: Column, rhs: usize) -> Point<L>
- where
- L: Copy + Default + Into<Line> + Add<usize, Output = L> + Sub<usize, Output = L>,
- {
- let num_cols = num_cols.0;
- let line_changes = (rhs + num_cols - 1).saturating_sub(self.column.0) / num_cols;
- if self.line.into() >= Line(line_changes) {
- self.line = self.line - line_changes;
- self.column = Column((num_cols + self.column.0 - rhs % num_cols) % num_cols);
- self
- } else {
- Point::new(L::default(), Column(0))
- }
+impl<L, C> Point<L, C> {
+ pub fn new(line: L, column: C) -> Point<L, C> {
+ Point { line, column }
}
+}
+impl Point {
+ /// Subtract a number of columns from a point.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
- pub fn add(mut self, num_cols: Column, rhs: usize) -> Point<L>
+ pub fn sub<D>(mut self, dimensions: &D, boundary: Boundary, rhs: usize) -> Self
where
- L: Copy + Default + Into<Line> + Add<usize, Output = L> + Sub<usize, Output = L>,
+ D: Dimensions,
{
- let num_cols = num_cols.0;
- self.line = self.line + (rhs + self.column.0) / num_cols;
- self.column = Column((self.column.0 + rhs) % num_cols);
- self
+ let cols = dimensions.columns();
+ let line_changes = (rhs + cols - 1).saturating_sub(self.column.0) / cols;
+ self.line -= line_changes;
+ self.column = Column((cols + self.column.0 - rhs % cols) % cols);
+ self.grid_clamp(dimensions, boundary)
}
-}
-impl Point<usize> {
+ /// Add a number of columns to a point.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
- pub fn sub_absolute<D>(mut self, dimensions: &D, boundary: Boundary, rhs: usize) -> Point<usize>
+ pub fn add<D>(mut self, dimensions: &D, boundary: Boundary, rhs: usize) -> Self
where
D: Dimensions,
{
- let total_lines = dimensions.total_lines();
- let num_cols = dimensions.cols().0;
-
- self.line += (rhs + num_cols - 1).saturating_sub(self.column.0) / num_cols;
- self.column = Column((num_cols + self.column.0 - rhs % num_cols) % num_cols);
-
- if self.line >= total_lines {
- match boundary {
- Boundary::Clamp => Point::new(total_lines - 1, Column(0)),
- Boundary::Wrap => Point::new(self.line - total_lines, self.column),
- }
- } else {
- self
- }
+ let cols = dimensions.columns();
+ self.line += (rhs + self.column.0) / cols;
+ self.column = Column((self.column.0 + rhs) % cols);
+ self.grid_clamp(dimensions, boundary)
}
+ /// Clamp a point to a grid boundary.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
- pub fn add_absolute<D>(mut self, dimensions: &D, boundary: Boundary, rhs: usize) -> Point<usize>
+ pub fn grid_clamp<D>(mut self, dimensions: &D, boundary: Boundary) -> Self
where
D: Dimensions,
{
- let num_cols = dimensions.cols();
-
- let line_delta = (rhs + self.column.0) / num_cols.0;
-
- if self.line >= line_delta {
- self.line -= line_delta;
- self.column = Column((self.column.0 + rhs) % num_cols.0);
- self
- } else {
- match boundary {
- Boundary::Clamp => Point::new(0, num_cols - 1),
- Boundary::Wrap => {
- let col = Column((self.column.0 + rhs) % num_cols.0);
- let line = dimensions.total_lines() + self.line - line_delta;
- Point::new(line, col)
- },
- }
+ let last_column = dimensions.last_column();
+ self.column = min(self.column, last_column);
+
+ let topmost_line = dimensions.topmost_line();
+ let bottommost_line = dimensions.bottommost_line();
+
+ match boundary {
+ Boundary::Cursor if self.line < 0 => Point::new(Line(0), Column(0)),
+ Boundary::Grid if self.line < topmost_line => Point::new(topmost_line, Column(0)),
+ Boundary::Cursor | Boundary::Grid if self.line > bottommost_line => {
+ Point::new(bottommost_line, last_column)
+ },
+ Boundary::None => {
+ self.line = self.line.grid_clamp(dimensions, boundary);
+ self
+ },
+ _ => self,
}
}
}
-impl PartialOrd for Point {
- fn partial_cmp(&self, other: &Point) -> Option<Ordering> {
+impl<L: Ord, C: Ord> PartialOrd for Point<L, C> {
+ fn partial_cmp(&self, other: &Point<L, C>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
-impl Ord for Point {
- fn cmp(&self, other: &Point) -> Ordering {
+impl<L: Ord, C: Ord> Ord for Point<L, C> {
+ fn cmp(&self, other: &Point<L, C>) -> Ordering {
match (self.line.cmp(&other.line), self.column.cmp(&other.column)) {
(Ordering::Equal, ord) | (ord, _) => ord,
}
}
}
-impl PartialOrd for Point<usize> {
- fn partial_cmp(&self, other: &Point<usize>) -> Option<Ordering> {
- Some(self.cmp(other))
+/// A line.
+///
+/// Newtype to avoid passing values incorrectly.
+#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Default, Ord, PartialOrd)]
+pub struct Line(pub i32);
+
+impl Line {
+ /// Clamp a line to a grid boundary.
+ pub fn grid_clamp<D: Dimensions>(self, dimensions: &D, boundary: Boundary) -> Self {
+ match boundary {
+ Boundary::Cursor => max(Line(0), min(dimensions.bottommost_line(), self)),
+ Boundary::Grid => {
+ let bottommost_line = dimensions.bottommost_line();
+ let topmost_line = dimensions.topmost_line();
+ max(topmost_line, min(bottommost_line, self))
+ },
+ Boundary::None => {
+ let screen_lines = dimensions.screen_lines() as i32;
+ let total_lines = dimensions.total_lines() as i32;
+
+ if self >= screen_lines {
+ let topmost_line = dimensions.topmost_line();
+ let extra = (self.0 - screen_lines) % total_lines;
+ topmost_line + extra
+ } else {
+ let bottommost_line = dimensions.bottommost_line();
+ let extra = (self.0 - screen_lines + 1) % total_lines;
+ bottommost_line + extra
+ }
+ },
+ }
}
}
-impl Ord for Point<usize> {
- fn cmp(&self, other: &Point<usize>) -> Ordering {
- match (self.line.cmp(&other.line), self.column.cmp(&other.column)) {
- (Ordering::Equal, ord) => ord,
- (Ordering::Less, _) => Ordering::Greater,
- (Ordering::Greater, _) => Ordering::Less,
- }
+impl fmt::Display for Line {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.0)
}
}
-impl From<Point<usize>> for Point<isize> {
- fn from(point: Point<usize>) -> Self {
- Point::new(point.line as isize, point.column)
+impl From<usize> for Line {
+ fn from(source: usize) -> Self {
+ Self(source as i32)
}
}
-impl From<Point<usize>> for Point<Line> {
- fn from(point: Point<usize>) -> Self {
- Point::new(Line(point.line), point.column)
+impl Add<usize> for Line {
+ type Output = Line;
+
+ #[inline]
+ fn add(self, rhs: usize) -> Line {
+ self + rhs as i32
}
}
-impl From<Point<isize>> for Point<usize> {
- fn from(point: Point<isize>) -> Self {
- Point::new(point.line as usize, point.column)
+impl AddAssign<usize> for Line {
+ #[inline]
+ fn add_assign(&mut self, rhs: usize) {
+ *self += rhs as i32;
}
}
-impl From<Point> for Point<usize> {
- fn from(point: Point) -> Self {
- Point::new(point.line.0, point.column)
+impl Sub<usize> for Line {
+ type Output = Line;
+
+ #[inline]
+ fn sub(self, rhs: usize) -> Line {
+ self - rhs as i32
}
}
-/// A line.
-///
-/// Newtype to avoid passing values incorrectly.
-#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Default, Ord, PartialOrd)]
-pub struct Line(pub usize);
+impl SubAssign<usize> for Line {
+ #[inline]
+ fn sub_assign(&mut self, rhs: usize) {
+ *self -= rhs as i32;
+ }
+}
-impl fmt::Display for Line {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.0)
+impl PartialOrd<usize> for Line {
+ #[inline]
+ fn partial_cmp(&self, other: &usize) -> Option<Ordering> {
+ self.0.partial_cmp(&(*other as i32))
+ }
+}
+
+impl PartialEq<usize> for Line {
+ #[inline]
+ fn eq(&self, other: &usize) -> bool {
+ self.0.eq(&(*other as i32))
}
}
@@ -213,66 +229,25 @@ impl fmt::Display for Column {
}
}
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-//
-// implements binary operators "&T op U", "T op &U", "&T op &U"
-// based on "T op U" where T and U are expected to be `Copy`able
-macro_rules! forward_ref_binop {
- (impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
- impl<'a> $imp<$u> for &'a $t {
- type Output = <$t as $imp<$u>>::Output;
-
- #[inline]
- fn $method(self, other: $u) -> <$t as $imp<$u>>::Output {
- $imp::$method(*self, other)
- }
- }
-
- impl<'a> $imp<&'a $u> for $t {
- type Output = <$t as $imp<$u>>::Output;
+macro_rules! ops {
+ ($ty:ty, $construct:expr, $primitive:ty) => {
+ impl Deref for $ty {
+ type Target = $primitive;
#[inline]
- fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output {
- $imp::$method(self, *other)
+ fn deref(&self) -> &$primitive {
+ &self.0
}
}
- impl<'a, 'b> $imp<&'a $u> for &'b $t {
- type Output = <$t as $imp<$u>>::Output;
-
+ impl From<$primitive> for $ty {
#[inline]
- fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output {
- $imp::$method(*self, *other)
- }
- }
- };
-}
-
-/// Macro for deriving deref.
-macro_rules! deref {
- ($ty:ty, $target:ty) => {
- impl Deref for $ty {
- type Target = $target;
-
- #[inline]
- fn deref(&self) -> &$target {
- &self.0
+ fn from(val: $primitive) -> $ty {
+ $construct(val)
}
}
- };
-}
-macro_rules! add {
- ($ty:ty, $construct:expr) => {
- impl ops::Add<$ty> for $ty {
+ impl Add<$ty> for $ty {
type Output = $ty;
#[inline]
@@ -280,182 +255,94 @@ macro_rules! add {
$construct(self.0 + rhs.0)
}
}
- };
-}
-
-macro_rules! sub {
- ($ty:ty, $construct:expr) => {
- impl ops::Sub<$ty> for $ty {
- type Output = $ty;
+ impl AddAssign<$ty> for $ty {
#[inline]
- fn sub(self, rhs: $ty) -> $ty {
- $construct(self.0 - rhs.0)
+ fn add_assign(&mut self, rhs: $ty) {
+ self.0 += rhs.0;
}
}
- impl<'a> ops::Sub<$ty> for &'a $ty {
+ impl Add<$primitive> for $ty {
type Output = $ty;
#[inline]
- fn sub(self, rhs: $ty) -> $ty {
- $construct(self.0 - rhs.0)
+ fn add(self, rhs: $primitive) -> $ty {
+ $construct(self.0 + rhs)
}
}
- impl<'a> ops::Sub<&'a $ty> for $ty {
- type Output = $ty;
-
+ impl AddAssign<$primitive> for $ty {
#[inline]
- fn sub(self, rhs: &'a $ty) -> $ty {
- $construct(self.0 - rhs.0)
+ fn add_assign(&mut self, rhs: $primitive) {
+ self.0 += rhs
}
}
- impl<'a, 'b> ops::Sub<&'a $ty> for &'b $ty {
+ impl Sub<$ty> for $ty {
type Output = $ty;
#[inline]
- fn sub(self, rhs: &'a $ty) -> $ty {
+ fn sub(self, rhs: $ty) -> $ty {
$construct(self.0 - rhs.0)
}
}
- };
-}
-
-/// This exists because we can't implement Iterator on Range
-/// and the existing impl needs the unstable Step trait
-/// This should be removed and replaced with a Step impl
-/// in the ops macro when `step_by` is stabilized.
-pub struct IndexRange<T>(pub Range<T>);
-
-impl<T> From<Range<T>> for IndexRange<T> {
- fn from(from: Range<T>) -> Self {
- IndexRange(from)
- }
-}
-
-macro_rules! ops {
- ($ty:ty, $construct:expr) => {
- add!($ty, $construct);
- sub!($ty, $construct);
- deref!($ty, usize);
- forward_ref_binop!(impl Add, add for $ty, $ty);
-
- impl $ty {
- #[inline]
- fn steps_between(start: $ty, end: $ty, by: $ty) -> Option<usize> {
- if by == $construct(0) { return None; }
- if start < end {
- // Note: We assume $t <= usize here.
- let diff = (end - start).0;
- let by = by.0;
- if diff % by > 0 {
- Some(diff / by + 1)
- } else {
- Some(diff / by)
- }
- } else {
- Some(0)
- }
- }
-
- #[inline]
- fn steps_between_by_one(start: $ty, end: $ty) -> Option<usize> {
- Self::steps_between(start, end, $construct(1))
- }
- }
- impl Iterator for IndexRange<$ty> {
- type Item = $ty;
- #[inline]
- fn next(&mut self) -> Option<$ty> {
- if self.0.start < self.0.end {
- let old = self.0.start;
- self.0.start = old + 1;
- Some(old)
- } else {
- None
- }
- }
+ impl SubAssign<$ty> for $ty {
#[inline]
- fn size_hint(&self) -> (usize, Option<usize>) {
- match Self::Item::steps_between_by_one(self.0.start, self.0.end) {
- Some(hint) => (hint, Some(hint)),
- None => (0, None)
- }
+ fn sub_assign(&mut self, rhs: $ty) {
+ self.0 -= rhs.0;
}
}
- impl DoubleEndedIterator for IndexRange<$ty> {
- #[inline]
- fn next_back(&mut self) -> Option<$ty> {
- if self.0.start < self.0.end {
- let new = self.0.end - 1;
- self.0.end = new;
- Some(new)
- } else {
- None
- }
- }
- }
- impl AddAssign<$ty> for $ty {
- #[inline]
- fn add_assign(&mut self, rhs: $ty) {
- self.0 += rhs.0
- }
- }
+ impl Sub<$primitive> for $ty {
+ type Output = $ty;
- impl SubAssign<$ty> for $ty {
#[inline]
- fn sub_assign(&mut self, rhs: $ty) {
- self.0 -= rhs.0
+ fn sub(self, rhs: $primitive) -> $ty {
+ $construct(self.0 - rhs)
}
}
- impl AddAssign<usize> for $ty {
+ impl SubAssign<$primitive> for $ty {
#[inline]
- fn add_assign(&mut self, rhs: usize) {
- self.0 += rhs
+ fn sub_assign(&mut self, rhs: $primitive) {
+ self.0 -= rhs
}
}
- impl SubAssign<usize> for $ty {
+ impl PartialEq<$ty> for $primitive {
#[inline]
- fn sub_assign(&mut self, rhs: usize) {
- self.0 -= rhs
+ fn eq(&self, other: &$ty) -> bool {
+ self.eq(&other.0)
}
}
- impl From<usize> for $ty {
+ impl PartialEq<$primitive> for $ty {
#[inline]
- fn from(val: usize) -> $ty {
- $construct(val)
+ fn eq(&self, other: &$primitive) -> bool {
+ self.0.eq(other)
}
}
- impl Add<usize> for $ty {
- type Output = $ty;
-
+ impl PartialOrd<$ty> for $primitive {
#[inline]
- fn add(self, rhs: usize) -> $ty {
- $construct(self.0 + rhs)
+ fn partial_cmp(&self, other: &$ty) -> Option<Ordering> {
+ self.partial_cmp(&other.0)
}
}
- impl Sub<usize> for $ty {
- type Output = $ty;
-
+ impl PartialOrd<$primitive> for $ty {
#[inline]
- fn sub(self, rhs: usize) -> $ty {
- $construct(self.0 - rhs)
+ fn partial_cmp(&self, other: &$primitive) -> Option<Ordering> {
+ self.0.partial_cmp(other)
}
}
- }
+ };
}
-ops!(Line, Line);
-ops!(Column, Column);
+ops!(Column, Column, usize);
+ops!(Line, Line, i32);
#[cfg(test)]
mod tests {
@@ -469,154 +356,106 @@ mod tests {
assert!(Point::new(Line(1), Column(1)) > Point::new(Line(0), Column(0)));
assert!(Point::new(Line(1), Column(1)) > Point::new(Line(0), Column(1)));
assert!(Point::new(Line(1), Column(1)) > Point::new(Line(1), Column(0)));
+ assert!(Point::new(Line(0), Column(0)) > Point::new(Line(-1), Column(0)));
}
#[test]
fn sub() {
- let num_cols = Column(42);
- let point = Point::new(0, Column(13));
+ let size = (10, 42);
+ let point = Point::new(Line(0), Column(13));
- let result = point.sub(num_cols, 1);
+ let result = point.sub(&size, Boundary::Cursor, 1);
- assert_eq!(result, Point::new(0, point.column - 1));
+ assert_eq!(result, Point::new(Line(0), point.column - 1));
}
#[test]
fn sub_wrap() {
- let num_cols = Column(42);
- let point = Point::new(1, Column(0));
+ let size = (10, 42);
+ let point = Point::new(Line(1), Column(0));
- let result = point.sub(num_cols, 1);
+ let result = point.sub(&size, Boundary::Cursor, 1);
- assert_eq!(result, Point::new(0, num_cols - 1));
+ assert_eq!(result, Point::new(Line(0), size.last_column()));
}
#[test]
fn sub_clamp() {
- let num_cols = Column(42);
- let point = Point::new(0, Column(0));
+ let size = (10, 42);
+ let point = Point::new(Line(0), Column(0));
- let result = point.sub(num_cols, 1);
+ let result = point.sub(&size, Boundary::Cursor, 1);
assert_eq!(result, point);
}
#[test]
- fn add() {
- let num_cols = Column(42);
- let point = Point::new(0, Column(13));
-
- let result = point.add(num_cols, 1);
-
- assert_eq!(result, Point::new(0, point.column + 1));
- }
-
- #[test]
- fn add_wrap() {
- let num_cols = Column(42);
- let point = Point::new(0, num_cols - 1);
+ fn sub_grid_clamp() {
+ let size = (0, 42);
+ let point = Point::new(Line(0), Column(0));
- let result = point.add(num_cols, 1);
-
- assert_eq!(result, Point::new(1, Column(0)));
- }
-
- #[test]
- fn add_absolute() {
- let point = Point::new(0, Column(13));
-
- let result = point.add_absolute(&(Line(1), Column(42)), Boundary::Clamp, 1);
-
- assert_eq!(result, Point::new(0, point.column + 1));
- }
-
- #[test]
- fn add_absolute_wrapline() {
- let point = Point::new(1, Column(41));
-
- let result = point.add_absolute(&(Line(2), Column(42)), Boundary::Clamp, 1);
-
- assert_eq!(result, Point::new(0, Column(0)));
- }
-
- #[test]
- fn add_absolute_multiline_wrapline() {
- let point = Point::new(2, Column(9));
-
- let result = point.add_absolute(&(Line(3), Column(10)), Boundary::Clamp, 11);
-
- assert_eq!(result, Point::new(0, Column(0)));
- }
-
- #[test]
- fn add_absolute_clamp() {
- let point = Point::new(0, Column(41));
-
- let result = point.add_absolute(&(Line(1), Column(42)), Boundary::Clamp, 1);
+ let result = point.sub(&size, Boundary::Grid, 1);
assert_eq!(result, point);
}
#[test]
- fn add_absolute_wrap() {
- let point = Point::new(0, Column(41));
-
- let result = point.add_absolute(&(Line(3), Column(42)), Boundary::Wrap, 1);
-
- assert_eq!(result, Point::new(2, Column(0)));
- }
-
- #[test]
- fn add_absolute_multiline_wrap() {
- let point = Point::new(0, Column(9));
+ fn sub_none_clamp() {
+ let size = (10, 42);
+ let point = Point::new(Line(0), Column(0));
- let result = point.add_absolute(&(Line(3), Column(10)), Boundary::Wrap, 11);
+ let result = point.sub(&size, Boundary::None, 1);
- assert_eq!(result, Point::new(1, Column(0)));
+ assert_eq!(result, Point::new(Line(9), Column(41)));
}
#[test]
- fn sub_absolute() {
- let point = Point::new(0, Column(13));
+ fn add() {
+ let size = (10, 42);
+ let point = Point::new(Line(0), Column(13));
- let result = point.sub_absolute(&(Line(1), Column(42)), Boundary::Clamp, 1);
+ let result = point.add(&size, Boundary::Cursor, 1);
- assert_eq!(result, Point::new(0, point.column - 1));
+ assert_eq!(result, Point::new(Line(0), point.column + 1));
}
#[test]
- fn sub_absolute_wrapline() {
- let point = Point::new(0, Column(0));
+ fn add_wrap() {
+ let size = (10, 42);
+ let point = Point::new(Line(0), size.last_column());
- let result = point.sub_absolute(&(Line(2), Column(42)), Boundary::Clamp, 1);
+ let result = point.add(&size, Boundary::Cursor, 1);
- assert_eq!(result, Point::new(1, Column(41)));
+ assert_eq!(result, Point::new(Line(1), Column(0)));
}
#[test]
- fn sub_absolute_multiline_wrapline() {
- let point = Point::new(0, Column(0));
+ fn add_clamp() {
+ let size = (10, 42);
+ let point = Point::new(Line(9), Column(41));
- let result = point.sub_absolute(&(Line(3), Column(10)), Boundary::Clamp, 11);
+ let result = point.add(&size, Boundary::Cursor, 1);
- assert_eq!(result, Point::new(2, Column(9)));
+ assert_eq!(result, point);
}
#[test]
- fn sub_absolute_wrap() {
- let point = Point::new(2, Column(0));
+ fn add_grid_clamp() {
+ let size = (10, 42);
+ let point = Point::new(Line(9), Column(41));
- let result = point.sub_absolute(&(Line(3), Column(42)), Boundary::Wrap, 1);
+ let result = point.add(&size, Boundary::Grid, 1);
- assert_eq!(result, Point::new(0, Column(41)));
+ assert_eq!(result, point);
}
#[test]
- fn sub_absolute_multiline_wrap() {
- let point = Point::new(2, Column(0));
+ fn add_none_clamp() {
+ let size = (10, 42);
+ let point = Point::new(Line(9), Column(41));
- let result = point.sub_absolute(&(Line(3), Column(10)), Boundary::Wrap, 11);
+ let result = point.add(&size, Boundary::None, 1);
- assert_eq!(result, Point::new(1, Column(9)));
+ assert_eq!(result, Point::new(Line(0), Column(0)));
}
}
diff --git a/alacritty_terminal/src/selection.rs b/alacritty_terminal/src/selection.rs
index afb98c0d..95c19be4 100644
--- a/alacritty_terminal/src/selection.rs
+++ b/alacritty_terminal/src/selection.rs
@@ -5,7 +5,7 @@
//! when text is added/removed/scrolled on the screen. The selection should
//! also be cleared if the user clicks off of the selection.
-use std::convert::TryFrom;
+use std::cmp::min;
use std::mem;
use std::ops::{Bound, Range, RangeBounds};
@@ -18,34 +18,34 @@ use crate::term::{RenderableCursor, Term};
/// A Point and side within that point.
#[derive(Debug, Copy, Clone, PartialEq)]
struct Anchor {
- point: Point<usize>,
+ point: Point,
side: Side,
}
impl Anchor {
- fn new(point: Point<usize>, side: Side) -> Anchor {
+ fn new(point: Point, side: Side) -> Anchor {
Anchor { point, side }
}
}
/// Represents a range of selected cells.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub struct SelectionRange<L = usize> {
+pub struct SelectionRange {
/// Start point, top left of the selection.
- pub start: Point<L>,
+ pub start: Point,
/// End point, bottom right of the selection.
- pub end: Point<L>,
+ pub end: Point,
/// Whether this selection is a block selection.
pub is_block: bool,
}
-impl<L> SelectionRange<L> {
- pub fn new(start: Point<L>, end: Point<L>, is_block: bool) -> Self {
+impl SelectionRange {
+ pub fn new(start: Point, end: Point, is_block: bool) -> Self {
Self { start, end, is_block }
}
}
-impl SelectionRange<Line> {
+impl SelectionRange {
/// Check if a point lies within the selection.
pub fn contains(&self, point: Point) -> bool {
self.start.line <= point.line
@@ -56,7 +56,7 @@ impl SelectionRange<Line> {
}
/// Check if the cell at a point is part of the selection.
- pub fn contains_cell(&self, indexed: &Indexed<&Cell, Line>, cursor: RenderableCursor) -> bool {
+ pub fn contains_cell(&self, indexed: &Indexed<&Cell>, cursor: RenderableCursor) -> bool {
// Do not invert block cursor at selection boundaries.
if cursor.shape == CursorShape::Block
&& cursor.point == indexed.point
@@ -116,7 +116,7 @@ pub struct Selection {
}
impl Selection {
- pub fn new(ty: SelectionType, location: Point<usize>, side: Side) -> Selection {
+ pub fn new(ty: SelectionType, location: Point, side: Side) -> Selection {
Self {
region: Range { start: Anchor::new(location, side), end: Anchor::new(location, side) },
ty,
@@ -124,7 +124,7 @@ impl Selection {
}
/// Update the end of the selection.
- pub fn update(&mut self, point: Point<usize>, side: Side) {
+ pub fn update(&mut self, point: Point, side: Side) {
self.region.end = Anchor::new(point, side);
}
@@ -132,56 +132,52 @@ impl Selection {
mut self,
dimensions: &D,
range: &Range<Line>,
- delta: isize,
+ delta: i32,
) -> Option<Selection> {
- let num_lines = dimensions.screen_lines().0;
- let num_cols = dimensions.cols().0;
- let range_bottom = range.start.0;
- let range_top = range.end.0;
+ let bottommost_line = dimensions.bottommost_line();
+ let range_bottom = range.end;
+ let range_top = range.start;
let (mut start, mut end) = (&mut self.region.start, &mut self.region.end);
- if Selection::points_need_swap(start.point, end.point) {
+ if start.point > end.point {
mem::swap(&mut start, &mut end);
}
// Rotate start of selection.
- if (start.point.line < range_top || range_top == num_lines)
- && start.point.line >= range_bottom
- {
- start.point.line = usize::try_from(start.point.line as isize + delta).unwrap_or(0);
+ if (start.point.line >= range_top || range_top == 0) && start.point.line < range_bottom {
+ start.point.line = min(start.point.line - delta, bottommost_line);
// If end is within the same region, delete selection once start rotates out.
- if start.point.line < range_bottom && end.point.line >= range_bottom {
+ if start.point.line >= range_bottom && end.point.line < range_bottom {
return None;
}
// Clamp selection to start of region.
- if start.point.line >= range_top && range_top != num_lines {
+ if start.point.line < range_top && range_top != 0 {
if self.ty != SelectionType::Block {
start.point.column = Column(0);
start.side = Side::Left;
}
- start.point.line = range_top - 1;
+ start.point.line = range_top;
}
}
// Rotate end of selection.
- if (end.point.line < range_top || range_top == num_lines) && end.point.line >= range_bottom
- {
- end.point.line = usize::try_from(end.point.line as isize + delta).unwrap_or(0);
+ if (end.point.line >= range_top || range_top == 0) && end.point.line < range_bottom {
+ end.point.line = min(end.point.line - delta, bottommost_line);
// Delete selection if end has overtaken the start.
- if end.point.line > start.point.line {
+ if end.point.line < start.point.line {
return None;
}
// Clamp selection to end of region.
- if end.point.line < range_bottom {
+ if end.point.line >= range_bottom {
if self.ty != SelectionType::Block {
- end.point.column = Column(num_cols - 1);
+ end.point.column = dimensions.last_column();
end.side = Side::Right;
}
- end.point.line = range_bottom;
+ end.point.line = range_bottom - 1;
}
}
@@ -192,7 +188,7 @@ impl Selection {
match self.ty {
SelectionType::Simple => {
let (mut start, mut end) = (self.region.start, self.region.end);
- if Self::points_need_swap(start.point, end.point) {
+ if start.point > end.point {
mem::swap(&mut start, &mut end);
}
@@ -223,27 +219,27 @@ impl Selection {
}
/// Check whether selection contains any point in a given range.
- pub fn intersects_range<R: RangeBounds<usize>>(&self, range: R) -> bool {
+ pub fn intersects_range<R: RangeBounds<Line>>(&self, range: R) -> bool {
let mut start = self.region.start.point.line;
let mut end = self.region.end.point.line;
- if Self::points_need_swap(self.region.start.point, self.region.end.point) {
+ if start > end {
mem::swap(&mut start, &mut end);
}
- let range_start = match range.start_bound() {
+ let range_top = match range.start_bound() {
Bound::Included(&range_start) => range_start,
- Bound::Excluded(&range_start) => range_start.saturating_add(1),
- Bound::Unbounded => 0,
+ Bound::Excluded(&range_start) => range_start + 1,
+ Bound::Unbounded => Line(i32::min_value()),
};
- let range_end = match range.end_bound() {
+ let range_bottom = match range.end_bound() {
Bound::Included(&range_end) => range_end,
- Bound::Excluded(&range_end) => range_end.saturating_sub(1),
- Bound::Unbounded => usize::max_value(),
+ Bound::Excluded(&range_end) => range_end - 1,
+ Bound::Unbounded => Line(i32::max_value()),
};
- range_start <= start && range_end >= end
+ range_bottom >= start && range_top <= end
}
/// Expand selection sides to include all cells.
@@ -257,7 +253,7 @@ impl Selection {
(Side::Right, Side::Left)
},
SelectionType::Block => (Side::Left, Side::Right),
- _ if Self::points_need_swap(start, end) => (Side::Right, Side::Left),
+ _ if start > end => (Side::Right, Side::Left),
_ => (Side::Left, Side::Right),
};
@@ -268,63 +264,25 @@ impl Selection {
/// Convert selection to grid coordinates.
pub fn to_range<T>(&self, term: &Term<T>) -> Option<SelectionRange> {
let grid = term.grid();
- let num_cols = grid.cols();
+ let columns = grid.columns();
// Order start above the end.
let mut start = self.region.start;
let mut end = self.region.end;
- if Self::points_need_swap(start.point, end.point) {
+ if start.point > end.point {
mem::swap(&mut start, &mut end);
}
- // Clamp to inside the grid buffer.
- let is_block = self.ty == SelectionType::Block;
- let (start, end) = Self::grid_clamp(start, end, is_block, grid.total_lines()).ok()?;
-
match self.ty {
- SelectionType::Simple => self.range_simple(start, end, num_cols),
+ SelectionType::Simple => self.range_simple(start, end, columns),
SelectionType::Block => self.range_block(start, end),
SelectionType::Semantic => Some(Self::range_semantic(term, start.point, end.point)),
SelectionType::Lines => Some(Self::range_lines(term, start.point, end.point)),
}
}
- /// Bring start and end points in the correct order.
- fn points_need_swap(start: Point<usize>, end: Point<usize>) -> bool {
- start.line < end.line || start.line == end.line && start.column > end.column
- }
-
- /// Clamp selection inside grid to prevent OOB.
- fn grid_clamp(
- mut start: Anchor,
- end: Anchor,
- is_block: bool,
- lines: usize,
- ) -> Result<(Anchor, Anchor), ()> {
- // Clamp selection inside of grid to prevent OOB.
- if start.point.line >= lines {
- // Remove selection if it is fully out of the grid.
- if end.point.line >= lines {
- return Err(());
- }
-
- // Clamp to grid if it is still partially visible.
- if !is_block {
- start.side = Side::Left;
- start.point.column = Column(0);
- }
- start.point.line = lines - 1;
- }
-
- Ok((start, end))
- }
-
- fn range_semantic<T>(
- term: &Term<T>,
- mut start: Point<usize>,
- mut end: Point<usize>,
- ) -> SelectionRange {
+ fn range_semantic<T>(term: &Term<T>, mut start: Point, mut end: Point) -> SelectionRange {
if start == end {
if let Some(matching) = term.bracket_search(start) {
if (matching.line == start.line && matching.column < start.column)
@@ -339,19 +297,15 @@ impl Selection {
}
}
- start = term.semantic_search_left(start);
- end = term.semantic_search_right(end);
+ let start = term.semantic_search_left(start);
+ let end = term.semantic_search_right(end);
SelectionRange { start, end, is_block: false }
}
- fn range_lines<T>(
- term: &Term<T>,
- mut start: Point<usize>,
- mut end: Point<usize>,
- ) -> SelectionRange {
- start = term.line_search_left(start);
- end = term.line_search_right(end);
+ fn range_lines<T>(term: &Term<T>, start: Point, end: Point) -> SelectionRange {
+ let start = term.line_search_left(start);
+ let end = term.line_search_right(end);
SelectionRange { start, end, is_block: false }
}
@@ -360,7 +314,7 @@ impl Selection {
&self,
mut start: Anchor,
mut end: Anchor,
- num_cols: Column,
+ columns: usize,
) -> Option<SelectionRange> {
if self.is_empty() {
return None;
@@ -369,9 +323,9 @@ impl Selection {
// Remove last cell if selection ends to the left of a cell.
if end.side == Side::Left && start.point != end.point {
// Special case when selection ends to left of first cell.
- if end.point.column == Column(0) {
- end.point.column = num_cols - 1;
- end.point.line += 1;
+ if end.point.column == 0 {
+ end.point.column = Column(columns - 1);
+ end.point.line -= 1;
} else {
end.point.column -= 1;
}
@@ -382,8 +336,9 @@ impl Selection {
start.point.column += 1;
// Wrap to next line when selection starts to the right of last column.
- if start.point.column == num_cols {
- start.point = Point::new(start.point.line.saturating_sub(1), Column(0));
+ if start.point.column == columns {
+ start.point.column = Column(0);
+ start.point.line += 1;
}
}
@@ -429,7 +384,7 @@ mod tests {
use super::*;
use crate::config::MockConfig;
- use crate::index::{Column, Line, Point, Side};
+ use crate::index::{Column, Point, Side};
use crate::term::{SizeInfo, Term};
fn term(height: usize, width: usize) -> Term<()> {
@@ -444,7 +399,7 @@ mod tests {
/// 3. [BE]
#[test]
fn single_cell_left_to_right() {
- let location = Point { line: 0, column: Column(0) };
+ let location = Point::new(Line(0), Column(0));
let mut selection = Selection::new(SelectionType::Simple, location, Side::Left);
selection.update(location, Side::Right);
@@ -462,7 +417,7 @@ mod tests {
/// 3. [EB]
#[test]
fn single_cell_right_to_left() {
- let location = Point { line: 0, column: Column(0) };
+ let location = Point::new(Line(0), Column(0));
let mut selection = Selection::new(SelectionType::Simple, location, Side::Right);
selection.update(location, Side::Left);
@@ -481,8 +436,8 @@ mod tests {
#[test]
fn between_adjacent_cells_left_to_right() {
let mut selection =
- Selection::new(SelectionType::Simple, Point::new(0, Column(0)), Side::Right);
- selection.update(Point::new(0, Column(1)), Side::Left);
+ Selection::new(SelectionType::Simple, Point::new(Line(0), Column(0)), Side::Right);
+ selection.update(Point::new(Line(0), Column(1)), Side::Left);
assert_eq!(selection.to_range(&term(1, 2)), None);
}
@@ -495,8 +450,8 @@ mod tests {
#[test]
fn between_adjacent_cells_right_to_left() {
let mut selection =
- Selection::new(SelectionType::Simple, Point::new(0, Column(1)), Side::Left);
- selection.update(Point::new(0, Column(0)), Side::Right);
+ Selection::new(SelectionType::Simple, Point::new(Line(0), Column(1)), Side::Left);
+ selection.update(Point::new(Line(0), Column(0)), Side::Right);
assert_eq!(selection.to_range(&term(1, 2)), None);
}
@@ -512,12 +467,12 @@ mod tests {
#[test]
fn across_adjacent_lines_upward_final_cell_exclusive() {
let mut selection =
- Selection::new(SelectionType::Simple, Point::new(1, Column(1)), Side::Right);
- selection.update(Point::new(0, Column(1)), Side::Right);
+ Selection::new(SelectionType::Simple, Point::new(Line(0), Column(1)), Side::Right);
+ selection.update(Point::new(Line(1), Column(1)), Side::Right);
assert_eq!(selection.to_range(&term(2, 5)).unwrap(), SelectionRange {
- start: Point::new(1, Column(2)),
- end: Point::new(0, Column(1)),
+ start: Point::new(Line(0), Column(2)),
+ end: Point::new(Line(1), Column(1)),
is_block: false,
});
}
@@ -535,73 +490,73 @@ mod tests {
#[test]
fn selection_bigger_then_smaller() {
let mut selection =
- Selection::new(SelectionType::Simple, Point::new(0, Column(1)), Side::Right);
- selection.update(Point::new(1, Column(1)), Side::Right);
- selection.update(Point::new(1, Column(0)), Side::Right);
+ Selection::new(SelectionType::Simple, Point::new(Line(1), Column(1)), Side::Right);
+ selection.update(Point::new(Line(0), Column(1)), Side::Right);
+ selection.update(Point::new(Line(0), Column(0)), Side::Right);
assert_eq!(selection.to_range(&term(2, 5)).unwrap(), SelectionRange {
- start: Point::new(1, Column(1)),
- end: Point::new(0, Column(1)),
+ start: Point::new(Line(0), Column(1)),
+ end: Point::new(Line(1), Column(1)),
is_block: false,
});
}
#[test]
fn line_selection() {
- let size = (Line(10), Column(5));
+ let size = (10, 5);
let mut selection =
- Selection::new(SelectionType::Lines, Point::new(0, Column(1)), Side::Left);
- selection.update(Point::new(5, Column(1)), Side::Right);
- selection = selection.rotate(&size, &(Line(0)..size.0), 7).unwrap();
+ Selection::new(SelectionType::Lines, Point::new(Line(9), Column(1)), Side::Left);
+ selection.update(Point::new(Line(4), Column(1)), Side::Right);
+ selection = selection.rotate(&size, &(Line(0)..Line(size.0 as i32)), 7).unwrap();
- assert_eq!(selection.to_range(&term(*size.0, *size.1)).unwrap(), SelectionRange {
- start: Point::new(9, Column(0)),
- end: Point::new(7, Column(4)),
+ assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange {
+ start: Point::new(Line(-3), Column(0)),
+ end: Point::new(Line(2), Column(4)),
is_block: false,
});
}
#[test]
fn semantic_selection() {
- let size = (Line(10), Column(5));
+ let size = (10, 5);
let mut selection =
- Selection::new(SelectionType::Semantic, Point::new(0, Column(3)), Side::Left);
- selection.update(Point::new(5, Column(1)), Side::Right);
- selection = selection.rotate(&size, &(Line(0)..size.0), 7).unwrap();
+ Selection::new(SelectionType::Semantic, Point::new(Line(9), Column(3)), Side::Left);
+ selection.update(Point::new(Line(4), Column(1)), Side::Right);
+ selection = selection.rotate(&size, &(Line(0)..Line(size.0 as i32)), 4).unwrap();
- assert_eq!(selection.to_range(&term(*size.0, *size.1)).unwrap(), SelectionRange {
- start: Point::new(9, Column(0)),
- end: Point::new(7, Column(3)),
+ assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange {
+ start: Point::new(Line(0), Column(1)),
+ end: Point::new(Line(5), Column(3)),
is_block: false,
});
}
#[test]
fn simple_selection() {
- let size = (Line(10), Column(5));
+ let size = (10, 5);
let mut selection =
- Selection::new(SelectionType::Simple, Point::new(0, Column(3)), Side::Right);
- selection.update(Point::new(5, Column(1)), Side::Right);
- selection = selection.rotate(&size, &(Line(0)..size.0), 7).unwrap();
+ Selection::new(SelectionType::Simple, Point::new(Line(9), Column(3)), Side::Right);
+ selection.update(Point::new(Line(4), Column(1)), Side::Right);
+ selection = selection.rotate(&size, &(Line(0)..Line(size.0 as i32)), 7).unwrap();
- assert_eq!(selection.to_range(&term(*size.0, *size.1)).unwrap(), SelectionRange {
- start: Point::new(9, Column(0)),
- end: Point::new(7, Column(3)),
+ assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange {
+ start: Point::new(Line(-3), Column(2)),
+ end: Point::new(Line(2), Column(3)),
is_block: false,
});
}
#[test]
fn block_selection() {
- let size = (Line(10), Column(5));
+ let size = (10, 5);
let mut selection =
- Selection::new(SelectionType::Block, Point::new(0, Column(3)), Side::Right);
- selection.update(Point::new(5, Column(1)), Side::Right);
- selection = selection.rotate(&size, &(Line(0)..size.0), 7).unwrap();
+ Selection::new(SelectionType::Block, Point::new(Line(9), Column(3)), Side::Right);
+ selection.update(Point::new(Line(4), Column(1)), Side::Right);
+ selection = selection.rotate(&size, &(Line(0)..Line(size.0 as i32)), 7).unwrap();
- assert_eq!(selection.to_range(&term(*size.0, *size.1)).unwrap(), SelectionRange {
- start: Point::new(9, Column(2)),
- end: Point::new(7, Column(3)),
+ assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange {
+ start: Point::new(Line(-3), Column(2)),
+ end: Point::new(Line(2), Column(3)),
is_block: true
});
}
@@ -609,72 +564,72 @@ mod tests {
#[test]
fn simple_is_empty() {
let mut selection =
- Selection::new(SelectionType::Simple, Point::new(0, Column(0)), Side::Right);
+ Selection::new(SelectionType::Simple, Point::new(Line(1), Column(0)), Side::Right);
assert!(selection.is_empty());
- selection.update(Point::new(0, Column(1)), Side::Left);
+ selection.update(Point::new(Line(1), Column(1)), Side::Left);
assert!(selection.is_empty());
- selection.update(Point::new(1, Column(0)), Side::Right);
+ selection.update(Point::new(Line(0), Column(0)), Side::Right);
assert!(!selection.is_empty());
}
#[test]
fn block_is_empty() {
let mut selection =
- Selection::new(SelectionType::Block, Point::new(0, Column(0)), Side::Right);
+ Selection::new(SelectionType::Block, Point::new(Line(1), Column(0)), Side::Right);
assert!(selection.is_empty());
- selection.update(Point::new(0, Column(1)), Side::Left);
+ selection.update(Point::new(Line(1), Column(1)), Side::Left);
assert!(selection.is_empty());
- selection.update(Point::new(0, Column(1)), Side::Right);
+ selection.update(Point::new(Line(1), Column(1)), Side::Right);
assert!(!selection.is_empty());
- selection.update(Point::new(1, Column(0)), Side::Right);
+ selection.update(Point::new(Line(0), Column(0)), Side::Right);
assert!(selection.is_empty());
- selection.update(Point::new(1, Column(1)), Side::Left);
+ selection.update(Point::new(Line(0), Column(1)), Side::Left);
assert!(selection.is_empty());
- selection.update(Point::new(1, Column(1)), Side::Right);
+ selection.update(Point::new(Line(0), Column(1)), Side::Right);
assert!(!selection.is_empty());
}
#[test]
fn rotate_in_region_up() {
- let size = (Line(10), Column(5));
+ let size = (10, 5);
let mut selection =
- Selection::new(SelectionType::Simple, Point::new(2, Column(3)), Side::Right);
- selection.update(Point::new(5, Column(1)), Side::Right);
- selection = selection.rotate(&size, &(Line(1)..(size.0 - 1)), 4).unwrap();
+ Selection::new(SelectionType::Simple, Point::new(Line(7), Column(3)), Side::Right);
+ selection.update(Point::new(Line(4), Column(1)), Side::Right);
+ selection = selection.rotate(&size, &(Line(1)..Line(size.0 as i32 - 1)), 4).unwrap();
- assert_eq!(selection.to_range(&term(*size.0, *size.1)).unwrap(), SelectionRange {
- start: Point::new(8, Column(0)),
- end: Point::new(6, Column(3)),
+ assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange {
+ start: Point::new(Line(1), Column(0)),
+ end: Point::new(Line(3), Column(3)),
is_block: false,
});
}
#[test]
fn rotate_in_region_down() {
- let size = (Line(10), Column(5));
+ let size = (10, 5);
let mut selection =
- Selection::new(SelectionType::Simple, Point::new(5, Column(3)), Side::Right);
- selection.update(Point::new(8, Column(1)), Side::Left);
- selection = selection.rotate(&size, &(Line(1)..(size.0 - 1)), -5).unwrap();
+ Selection::new(SelectionType::Simple, Point::new(Line(4), Column(3)), Side::Right);
+ selection.update(Point::new(Line(1), Column(1)), Side::Left);
+ selection = selection.rotate(&size, &(Line(1)..Line(size.0 as i32 - 1)), -5).unwrap();
- assert_eq!(selection.to_range(&term(*size.0, *size.1)).unwrap(), SelectionRange {
- start: Point::new(3, Column(1)),
- end: Point::new(1, size.1 - 1),
+ assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange {
+ start: Point::new(Line(6), Column(1)),
+ end: Point::new(Line(8), size.last_column()),
is_block: false,
});
}
#[test]
fn rotate_in_region_up_block() {
- let size = (Line(10), Column(5));
+ let size = (10, 5);
let mut selection =
- Selection::new(SelectionType::Block, Point::new(2, Column(3)), Side::Right);
- selection.update(Point::new(5, Column(1)), Side::Right);
- selection = selection.rotate(&size, &(Line(1)..(size.0 - 1)), 4).unwrap();
+ Selection::new(SelectionType::Block, Point::new(Line(7), Column(3)), Side::Right);
+ selection.update(Point::new(Line(4), Column(1)), Side::Right);
+ selection = selection.rotate(&size, &(Line(1)..Line(size.0 as i32 - 1)), 4).unwrap();
- assert_eq!(selection.to_range(&term(*size.0, *size.1)).unwrap(), SelectionRange {
- start: Point::new(8, Column(2)),
- end: Point::new(6, Column(3)),
+ assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange {
+ start: Point::new(Line(1), Column(2)),
+ end: Point::new(Line(3), Column(3)),
is_block: true,
});
}
@@ -682,17 +637,17 @@ mod tests {
#[test]
fn range_intersection() {
let mut selection =
- Selection::new(SelectionType::Lines, Point::new(6, Column(1)), Side::Left);
- selection.update(Point::new(3, Column(1)), Side::Right);
+ Selection::new(SelectionType::Lines, Point::new(Line(3), Column(1)), Side::Left);
+ selection.update(Point::new(Line(6), Column(1)), Side::Right);
assert!(selection.intersects_range(..));
- assert!(selection.intersects_range(2..));
- assert!(selection.intersects_range(2..=4));
- assert!(selection.intersects_range(2..=7));
- assert!(selection.intersects_range(4..=5));
- assert!(selection.intersects_range(5..8));
-
- assert!(!selection.intersects_range(..=2));
- assert!(!selection.intersects_range(7..=8));
+ assert!(selection.intersects_range(Line(2)..));
+ assert!(selection.intersects_range(Line(2)..=Line(4)));
+ assert!(selection.intersects_range(Line(2)..=Line(7)));
+ assert!(selection.intersects_range(Line(4)..=Line(5)));
+ assert!(selection.intersects_range(Line(5)..Line(8)));
+
+ assert!(!selection.intersects_range(..=Line(2)));
+ assert!(!selection.intersects_range(Line(7)..=Line(8)));
}
}
diff --git a/alacritty_terminal/src/term/cell.rs b/alacritty_terminal/src/term/cell.rs
index 91ee3391..255cbce7 100644
--- a/alacritty_terminal/src/term/cell.rs
+++ b/alacritty_terminal/src/term/cell.rs
@@ -178,7 +178,7 @@ mod tests {
#[test]
fn line_length_works() {
- let mut row = Row::<Cell>::new(Column(10));
+ let mut row = Row::<Cell>::new(10);
row[Column(5)].c = 'a';
assert_eq!(row.line_length(), Column(6));
@@ -186,7 +186,7 @@ mod tests {
#[test]
fn line_length_works_with_wrapline() {
- let mut row = Row::<Cell>::new(Column(10));
+ let mut row = Row::<Cell>::new(10);
row[Column(9)].flags.insert(super::Flags::WRAPLINE);
assert_eq!(row.line_length(), Column(10));
diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs
index b853ceba..8d0fc0f8 100644
--- a/alacritty_terminal/src/term/mod.rs
+++ b/alacritty_terminal/src/term/mod.rs
@@ -16,7 +16,7 @@ use crate::ansi::{
use crate::config::Config;
use crate::event::{Event, EventListener};
use crate::grid::{Dimensions, DisplayIter, Grid, Scroll};
-use crate::index::{self, Boundary, Column, Direction, IndexRange, Line, Point, Side};
+use crate::index::{self, Boundary, Column, Direction, Line, Point, Side};
use crate::selection::{Selection, SelectionRange};
use crate::term::cell::{Cell, Flags, LineLength};
use crate::term::color::{Colors, Rgb};
@@ -29,7 +29,7 @@ pub mod search;
/// Minimum number of columns.
///
/// A minimum of 2 is necessary to hold fullwidth unicode characters.
-pub const MIN_COLS: usize = 2;
+pub const MIN_COLUMNS: usize = 2;
/// Minimum number of visible lines.
pub const MIN_SCREEN_LINES: usize = 1;
@@ -97,10 +97,10 @@ pub struct SizeInfo {
padding_y: f32,
/// Number of lines in the viewport.
- screen_lines: Line,
+ screen_lines: usize,
/// Number of columns in the viewport.
- cols: Column,
+ columns: usize,
}
impl SizeInfo {
@@ -120,10 +120,10 @@ impl SizeInfo {
}
let lines = (height - 2. * padding_y) / cell_height;
- let screen_lines = Line(max(lines as usize, MIN_SCREEN_LINES));
+ let screen_lines = max(lines as usize, MIN_SCREEN_LINES);
- let cols = (width - 2. * padding_x) / cell_width;
- let cols = Column(max(cols as usize, MIN_COLS));
+ let columns = (width - 2. * padding_x) / cell_width;
+ let columns = max(columns as usize, MIN_COLUMNS);
SizeInfo {
width,
@@ -133,13 +133,13 @@ impl SizeInfo {
padding_x: padding_x.floor(),
padding_y: padding_y.floor(),
screen_lines,
- cols,
+ columns,
}
}
#[inline]
pub fn reserve_lines(&mut self, count: usize) {
- self.screen_lines = Line(max(self.screen_lines.saturating_sub(count), MIN_SCREEN_LINES));
+ self.screen_lines = max(self.screen_lines.saturating_sub(count), MIN_SCREEN_LINES);
}
/// Check if coordinates are inside the terminal grid.
@@ -147,26 +147,12 @@ impl SizeInfo {
/// The padding, message bar or search are not counted as part of the grid.
#[inline]
pub fn contains_point(&self, x: usize, y: usize) -> bool {
- x <= (self.padding_x + self.cols.0 as f32 * self.cell_width) as usize
+ x <= (self.padding_x + self.columns as f32 * self.cell_width) as usize
&& x > self.padding_x as usize
- && y <= (self.padding_y + self.screen_lines.0 as f32 * self.cell_height) as usize
+ && y <= (self.padding_y + self.screen_lines as f32 * self.cell_height) as usize
&& y > self.padding_y as usize
}
- /// Convert window space pixels to terminal grid coordinates.
- ///
- /// If the coordinates are outside of the terminal grid, like positions inside the padding, the
- /// coordinates will be clamped to the closest grid coordinates.
- pub fn pixels_to_coords(&self, x: usize, y: usize) -> Point {
- let col = Column(x.saturating_sub(self.padding_x as usize) / (self.cell_width as usize));
- let line = Line(y.saturating_sub(self.padding_y as usize) / (self.cell_height as usize));
-
- Point {
- line: min(line, Line(self.screen_lines.saturating_sub(1))),
- column: min(col, Column(self.cols.saturating_sub(1))),
- }
- }
-
#[inline]
pub fn width(&self) -> f32 {
self.width
@@ -197,20 +183,27 @@ impl SizeInfo {
self.padding_y
}
+ /// Calculate padding to spread it evenly around the terminal content.
#[inline]
- pub fn screen_lines(&self) -> Line {
- self.screen_lines
+ fn dynamic_padding(padding: f32, dimension: f32, cell_dimension: f32) -> f32 {
+ padding + ((dimension - 2. * padding) % cell_dimension) / 2.
}
+}
+impl Dimensions for SizeInfo {
#[inline]
- pub fn cols(&self) -> Column {
- self.cols
+ fn columns(&self) -> usize {
+ self.columns
}
- /// Calculate padding to spread it evenly around the terminal content.
#[inline]
- fn dynamic_padding(padding: f32, dimension: f32, cell_dimension: f32) -> f32 {
- padding + ((dimension - 2. * padding) % cell_dimension) / 2.
+ fn screen_lines(&self) -> usize {
+ self.screen_lines
+ }
+
+ #[inline]
+ fn total_lines(&self) -> usize {
+ self.screen_lines()
}
}
@@ -286,19 +279,26 @@ impl<T> Term<T> {
{
self.grid.scroll_display(scroll);
self.event_proxy.send_event(Event::MouseCursorDirty);
+
+ // Clamp vi mode cursor to the viewport.
+ let viewport_start = -(self.grid.display_offset() as i32);
+ let viewport_end = viewport_start + self.bottommost_line().0;
+ let vi_cursor_line = &mut self.vi_mode_cursor.point.line.0;
+ *vi_cursor_line = min(viewport_end, max(viewport_start, *vi_cursor_line));
+ self.vi_mode_recompute_selection();
}
pub fn new<C>(config: &Config<C>, size: SizeInfo, event_proxy: T) -> Term<T> {
- let num_cols = size.cols;
+ let num_cols = size.columns;
let num_lines = size.screen_lines;
let history_size = config.scrolling.history() as usize;
let grid = Grid::new(num_lines, num_cols, history_size);
let alt = Grid::new(num_lines, num_cols, 0);
- let tabs = TabStops::new(grid.cols());
+ let tabs = TabStops::new(grid.columns());
- let scroll_region = Line(0)..grid.screen_lines();
+ let scroll_region = Line(0)..Line(grid.screen_lines() as i32);
Term {
grid,
@@ -353,11 +353,11 @@ impl<T> Term<T> {
let mut res = String::new();
if is_block {
- for line in (end.line + 1..=start.line).rev() {
+ for line in (start.line.0..end.line.0).map(Line::from) {
res += &self.line_to_string(line, start.column..end.column, start.column.0 != 0);
// If the last column is included, newline is appended automatically.
- if end.column != self.cols() - 1 {
+ if end.column != self.columns() - 1 {
res += "\n";
}
}
@@ -370,12 +370,12 @@ impl<T> Term<T> {
}
/// Convert range between two points to a String.
- pub fn bounds_to_string(&self, start: Point<usize>, end: Point<usize>) -> String {
+ pub fn bounds_to_string(&self, start: Point, end: Point) -> String {
let mut res = String::new();
- for line in (end.line..=start.line).rev() {
+ for line in (start.line.0..=end.line.0).map(Line::from) {
let start_col = if line == start.line { start.column } else { Column(0) };
- let end_col = if line == end.line { end.column } else { self.cols() - 1 };
+ let end_col = if line == end.line { end.column } else { self.last_column() };
res += &self.line_to_string(line, start_col..end_col, line == end.line);
}
@@ -386,7 +386,7 @@ impl<T> Term<T> {
/// Convert a single line in the grid to a String.
fn line_to_string(
&self,
- line: usize,
+ line: Line,
mut cols: Range<Column>,
include_wrapped_wide: bool,
) -> String {
@@ -401,12 +401,12 @@ impl<T> Term<T> {
}
let mut tab_mode = false;
- for col in IndexRange::from(cols.start..line_length) {
- let cell = &grid_line[col];
+ for column in (cols.start.0..line_length.0).map(Column::from) {
+ let cell = &grid_line[column];
// Skip over cells until next tab-stop once a tab was found.
if tab_mode {
- if self.tabs[col] {
+ if self.tabs[column] {
tab_mode = false;
} else {
continue;
@@ -428,7 +428,7 @@ impl<T> Term<T> {
}
}
- if cols.end >= self.cols() - 1
+ if cols.end >= self.columns() - 1
&& (line_length.0 == 0
|| !self.grid[line][line_length - 1].flags.contains(Flags::WRAPLINE))
{
@@ -436,22 +436,17 @@ impl<T> Term<T> {
}
// If wide char is not part of the selection, but leading spacer is, include it.
- if line_length == self.cols()
+ if line_length == self.columns()
&& line_length.0 >= 2
&& grid_line[line_length - 1].flags.contains(Flags::LEADING_WIDE_CHAR_SPACER)
&& include_wrapped_wide
{
- text.push(self.grid[line - 1][Column(0)].c);
+ text.push(self.grid[line - 1i32][Column(0)].c);
}
text
}
- #[inline]
- pub fn visible_to_buffer(&self, point: Point) -> Point<usize> {
- self.grid.visible_to_buffer(point)
- }
-
/// Terminal content required for rendering.
#[inline]
pub fn renderable_content(&self) -> RenderableContent<'_>
@@ -480,10 +475,10 @@ impl<T> Term<T> {
self.cell_width = size.cell_width as usize;
self.cell_height = size.cell_height as usize;
- let old_cols = self.cols();
+ let old_cols = self.columns();
let old_lines = self.screen_lines();
- let num_cols = size.cols;
+ let num_cols = size.columns;
let num_lines = size.screen_lines;
if old_cols == num_cols && old_lines == num_lines {
@@ -493,6 +488,13 @@ impl<T> Term<T> {
debug!("New num_cols is {} and num_lines is {}", num_cols, num_lines);
+ // Move vi mode cursor with the content.
+ let history_size = self.history_size();
+ let mut delta = num_lines as i32 - old_lines as i32;
+ let min_delta = min(0, num_lines as i32 - self.grid.cursor.point.line.0 - 1);
+ delta = min(max(delta, min_delta), history_size as i32);
+ self.vi_mode_cursor.point.line += delta;
+
// Invalidate selection and tabs only when necessary.
if old_cols != num_cols {
self.selection = None;
@@ -500,27 +502,21 @@ impl<T> Term<T> {
// Recreate tabs list.
self.tabs.resize(num_cols);
} else if let Some(selection) = self.selection.take() {
- // Move the selection if only number of lines changed.
- let delta = if num_lines > old_lines {
- (num_lines - old_lines.0).saturating_sub(self.history_size()) as isize
- } else {
- let cursor_line = self.grid.cursor.point.line;
- -(min(old_lines - cursor_line - 1, old_lines - num_lines).0 as isize)
- };
- self.selection = selection.rotate(self, &(Line(0)..num_lines), delta);
+ let range = Line(0)..Line(num_lines as i32);
+ self.selection = selection.rotate(self, &range, -delta);
}
let is_alt = self.mode.contains(TermMode::ALT_SCREEN);
-
self.grid.resize(!is_alt, num_lines, num_cols);
self.inactive_grid.resize(is_alt, num_lines, num_cols);
// Clamp vi cursor to viewport.
- self.vi_mode_cursor.point.column = min(self.vi_mode_cursor.point.column, num_cols - 1);
- self.vi_mode_cursor.point.line = min(self.vi_mode_cursor.point.line, num_lines - 1);
+ let vi_point = self.vi_mode_cursor.point;
+ self.vi_mode_cursor.point.column = min(vi_point.column, Column(num_cols - 1));
+ self.vi_mode_cursor.point.line = min(vi_point.line, Line(num_lines as i32 - 1));
// Reset scrolling region.
- self.scroll_region = Line(0)..self.screen_lines();
+ self.scroll_region = Line(0)..Line(self.screen_lines() as i32);
}
/// Active terminal modes.
@@ -547,49 +543,22 @@ impl<T> Term<T> {
self.selection = None;
}
- /// Get the selection within the viewport.
- fn visible_selection(&self) -> Option<SelectionRange<Line>> {
- let selection = self.selection.as_ref()?.to_range(self)?;
-
- // Set horizontal limits for block selection.
- let (limit_start, limit_end) = if selection.is_block {
- (selection.start.column, selection.end.column)
- } else {
- (Column(0), self.cols() - 1)
- };
-
- let range = self.grid.clamp_buffer_range_to_visible(&(selection.start..=selection.end))?;
- let mut start = *range.start();
- let mut end = *range.end();
-
- // Trim start/end with partially visible block selection.
- start.column = max(limit_start, start.column);
- end.column = min(limit_end, end.column);
-
- Some(SelectionRange::new(start, end, selection.is_block))
- }
-
/// Scroll screen down.
///
/// Text moves down; clear at bottom
/// Expects origin to be in scroll range.
#[inline]
- fn scroll_down_relative(&mut self, origin: Line, mut lines: Line) {
+ fn scroll_down_relative(&mut self, origin: Line, mut lines: usize) {
trace!("Scrolling down relative: origin={}, lines={}", origin, lines);
- let num_lines = self.screen_lines();
-
- lines = min(lines, self.scroll_region.end - self.scroll_region.start);
- lines = min(lines, self.scroll_region.end - origin);
+ lines = min(lines, (self.scroll_region.end - self.scroll_region.start).0 as usize);
+ lines = min(lines, (self.scroll_region.end - origin).0 as usize);
let region = origin..self.scroll_region.end;
- let absolute_region = (num_lines - region.end)..(num_lines - region.start);
// Scroll selection.
- self.selection = self
- .selection
- .take()
- .and_then(|s| s.rotate(self, &absolute_region, -(lines.0 as isize)));
+ self.selection =
+ self.selection.take().and_then(|s| s.rotate(self, &region, -(lines as i32)));
// Scroll between origin and bottom
self.grid.scroll_down(&region, lines);
@@ -600,19 +569,15 @@ impl<T> Term<T> {
/// Text moves up; clear at top
/// Expects origin to be in scroll range.
#[inline]
- fn scroll_up_relative(&mut self, origin: Line, mut lines: Line) {
+ fn scroll_up_relative(&mut self, origin: Line, mut lines: usize) {
trace!("Scrolling up relative: origin={}, lines={}", origin, lines);
- let num_lines = self.screen_lines();
-
- lines = min(lines, self.scroll_region.end - self.scroll_region.start);
+ lines = min(lines, (self.scroll_region.end - self.scroll_region.start).0 as usize);
let region = origin..self.scroll_region.end;
- let absolute_region = (num_lines - region.end)..(num_lines - region.start);
// Scroll selection.
- self.selection =
- self.selection.take().and_then(|s| s.rotate(self, &absolute_region, lines.0 as isize));
+ self.selection = self.selection.take().and_then(|s| s.rotate(self, &region, lines as i32));
// Scroll from origin to bottom less number of lines.
self.grid.scroll_up(&region, lines);
@@ -648,9 +613,7 @@ impl<T> Term<T> {
if self.mode.contains(TermMode::VI) {
// Reset vi mode cursor position to match primary cursor.
- let cursor = self.grid.cursor.point;
- let line = min(cursor.line + self.grid.display_offset(), self.screen_lines() - 1);
- self.vi_mode_cursor = ViModeCursor::new(Point::new(line, cursor.column));
+ self.vi_mode_cursor = ViModeCursor::new(self.grid.cursor.point);
}
// Update UI about cursor blinking state changes.
@@ -673,9 +636,9 @@ impl<T> Term<T> {
self.vi_mode_recompute_selection();
}
- /// Move vi cursor to absolute point in grid.
+ /// Move vi cursor to a point in the grid.
#[inline]
- pub fn vi_goto_point(&mut self, point: Point<usize>)
+ pub fn vi_goto_point(&mut self, point: Point)
where
T: EventListener,
{
@@ -683,7 +646,7 @@ impl<T> Term<T> {
self.scroll_to_point(point);
// Move vi cursor to the point.
- self.vi_mode_cursor.point = self.grid.clamp_buffer_to_visible(point);
+ self.vi_mode_cursor.point = point;
self.vi_mode_recompute_selection();
}
@@ -696,43 +659,38 @@ impl<T> Term<T> {
return;
}
- let viewport_point = self.visible_to_buffer(self.vi_mode_cursor.point);
-
// Update only if non-empty selection is present.
- let selection = match &mut self.selection {
- Some(selection) if !selection.is_empty() => selection,
- _ => return,
- };
-
- selection.update(viewport_point, Side::Left);
- selection.include_all();
+ if let Some(selection) = self.selection.as_mut().filter(|s| !s.is_empty()) {
+ selection.update(self.vi_mode_cursor.point, Side::Left);
+ selection.include_all();
+ }
}
/// Scroll display to point if it is outside of viewport.
- pub fn scroll_to_point(&mut self, point: Point<usize>)
+ pub fn scroll_to_point(&mut self, point: Point)
where
T: EventListener,
{
- let display_offset = self.grid.display_offset();
- let num_lines = self.screen_lines().0;
+ let display_offset = self.grid.display_offset() as i32;
+ let screen_lines = self.grid.screen_lines() as i32;
- if point.line >= display_offset + num_lines {
- let lines = point.line.saturating_sub(display_offset + num_lines - 1);
- self.scroll_display(Scroll::Delta(lines as isize));
- } else if point.line < display_offset {
- let lines = display_offset.saturating_sub(point.line);
- self.scroll_display(Scroll::Delta(-(lines as isize)));
+ if point.line < -display_offset {
+ let lines = point.line + display_offset;
+ self.scroll_display(Scroll::Delta(-lines.0));
+ } else if point.line >= (screen_lines - display_offset) {
+ let lines = point.line + display_offset - screen_lines + 1i32;
+ self.scroll_display(Scroll::Delta(-lines.0));
}
}
/// Jump to the end of a wide cell.
- pub fn expand_wide(&self, mut point: Point<usize>, direction: Direction) -> Point<usize> {
+ pub fn expand_wide(&self, mut point: Point, direction: Direction) -> Point {
let flags = self.grid[point.line][point.column].flags;
match direction {
Direction::Right if flags.contains(Flags::LEADING_WIDE_CHAR_SPACER) => {
point.column = Column(1);
- point.line -= 1;
+ point.line += 1;
},
Direction::Right if flags.contains(Flags::WIDE_CHAR) => point.column += 1,
Direction::Left if flags.intersects(Flags::WIDE_CHAR | Flags::WIDE_CHAR_SPACER) => {
@@ -740,7 +698,7 @@ impl<T> Term<T> {
point.column -= 1;
}
- let prev = point.sub_absolute(self, Boundary::Clamp, 1);
+ let prev = point.sub(self, Boundary::Grid, 1);
if self.grid[prev].flags.contains(Flags::LEADING_WIDE_CHAR_SPACER) {
point = prev;
}
@@ -784,7 +742,7 @@ impl<T> Term<T> {
self.grid.cursor_cell().flags.insert(Flags::WRAPLINE);
- if (self.grid.cursor.point.line + 1) >= self.scroll_region.end {
+ if self.grid.cursor.point.line + 1 >= self.scroll_region.end {
self.linefeed();
} else {
self.grid.cursor.point.line += 1;
@@ -817,12 +775,12 @@ impl<T> Term<T> {
impl<T> Dimensions for Term<T> {
#[inline]
- fn cols(&self) -> Column {
- self.grid.cols()
+ fn columns(&self) -> usize {
+ self.grid.columns()
}
#[inline]
- fn screen_lines(&self) -> Line {
+ fn screen_lines(&self) -> usize {
self.grid.screen_lines()
}
@@ -865,7 +823,7 @@ impl<T: EventListener> Handler for Term<T> {
self.wrapline();
}
- let num_cols = self.cols();
+ let num_cols = self.columns();
// If in insert mode, first shift cells to the right.
if self.mode.contains(TermMode::INSERT) && self.grid.cursor.point.column + width < num_cols
@@ -874,7 +832,7 @@ impl<T: EventListener> Handler for Term<T> {
let col = self.grid.cursor.point.column;
let row = &mut self.grid[line][..];
- for col in (col.0..(num_cols - width).0).rev() {
+ for col in (col.0..(num_cols - width)).rev() {
row.swap(col + width, col);
}
}
@@ -913,8 +871,8 @@ impl<T: EventListener> Handler for Term<T> {
fn decaln(&mut self) {
trace!("Decalnning");
- for line in 0..self.screen_lines().0 {
- for column in 0..self.cols().0 {
+ for line in (0..self.screen_lines()).map(Line::from) {
+ for column in 0..self.columns() {
let cell = &mut self.grid[line][Column(column)];
*cell = Cell::default();
cell.c = 'E';
@@ -928,11 +886,11 @@ impl<T: EventListener> Handler for Term<T> {
let (y_offset, max_y) = if self.mode.contains(TermMode::ORIGIN) {
(self.scroll_region.start, self.scroll_region.end - 1)
} else {
- (Line(0), self.screen_lines() - 1)
+ (Line(0), self.bottommost_line())
};
- self.grid.cursor.point.line = min(line + y_offset, max_y);
- self.grid.cursor.point.column = min(col, self.cols() - 1);
+ self.grid.cursor.point.line = max(min(line + y_offset, max_y), Line(0));
+ self.grid.cursor.point.column = min(col, self.last_column());
self.grid.cursor.input_needs_wrap = false;
}
@@ -949,50 +907,48 @@ impl<T: EventListener> Handler for Term<T> {
}
#[inline]
- fn insert_blank(&mut self, count: Column) {
+ fn insert_blank(&mut self, count: usize) {
let cursor = &self.grid.cursor;
let bg = cursor.template.bg;
// Ensure inserting within terminal bounds
- let count = min(count, self.cols() - cursor.point.column);
+ let count = min(count, self.columns() - cursor.point.column.0);
let source = cursor.point.column;
- let destination = cursor.point.column + count;
- let num_cells = (self.cols() - destination).0;
+ let destination = cursor.point.column.0 + count;
+ let num_cells = self.columns() - destination;
let line = cursor.point.line;
let row = &mut self.grid[line][..];
for offset in (0..num_cells).rev() {
- row.swap(destination.0 + offset, source.0 + offset);
+ row.swap(destination + offset, source.0 + offset);
}
// Cells were just moved out toward the end of the line;
// fill in between source and dest with blanks.
- for cell in &mut row[source.0..destination.0] {
+ for cell in &mut row[source.0..destination] {
*cell = bg.into();
}
}
#[inline]
- fn move_up(&mut self, lines: Line) {
+ fn move_up(&mut self, lines: usize) {
trace!("Moving up: {}", lines);
- let move_to = Line(self.grid.cursor.point.line.0.saturating_sub(lines.0));
- self.goto(move_to, self.grid.cursor.point.column)
+ self.goto(self.grid.cursor.point.line - lines, self.grid.cursor.point.column)
}
#[inline]
- fn move_down(&mut self, lines: Line) {
+ fn move_down(&mut self, lines: usize) {
trace!("Moving down: {}", lines);
- let move_to = self.grid.cursor.point.line + lines;
- self.goto(move_to, self.grid.cursor.point.column)
+ self.goto(self.grid.cursor.point.line + lines, self.grid.cursor.point.column)
}
#[inline]
fn move_forward(&mut self, cols: Column) {
trace!("Moving forward: {}", cols);
- let num_cols = self.cols();
- self.grid.cursor.point.column = min(self.grid.cursor.point.column + cols, num_cols - 1);
+ let last_column = self.last_column();
+ self.grid.cursor.point.column = min(self.grid.cursor.point.column + cols, last_column);
self.grid.cursor.input_needs_wrap = false;
}
@@ -1037,17 +993,15 @@ impl<T: EventListener> Handler for Term<T> {
}
#[inline]
- fn move_down_and_cr(&mut self, lines: Line) {
+ fn move_down_and_cr(&mut self, lines: usize) {
trace!("Moving down and cr: {}", lines);
- let move_to = self.grid.cursor.point.line + lines;
- self.goto(move_to, Column(0))
+ self.goto(self.grid.cursor.point.line + lines, Column(0))
}
#[inline]
- fn move_up_and_cr(&mut self, lines: Line) {
+ fn move_up_and_cr(&mut self, lines: usize) {
trace!("Moving up and cr: {}", lines);
- let move_to = Line(self.grid.cursor.point.line.0.saturating_sub(lines.0));
- self.goto(move_to, Column(0))
+ self.goto(self.grid.cursor.point.line - lines, Column(0))
}
/// Insert tab at cursor position.
@@ -1059,7 +1013,7 @@ impl<T: EventListener> Handler for Term<T> {
return;
}
- while self.grid.cursor.point.column < self.cols() && count != 0 {
+ while self.grid.cursor.point.column < self.columns() && count != 0 {
count -= 1;
let c = self.grid.cursor.charsets[self.active_charset].map('\t');
@@ -1069,7 +1023,7 @@ impl<T: EventListener> Handler for Term<T> {
}
loop {
- if (self.grid.cursor.point.column + 1) == self.cols() {
+ if (self.grid.cursor.point.column + 1) == self.columns() {
break;
}
@@ -1107,7 +1061,7 @@ impl<T: EventListener> Handler for Term<T> {
trace!("Linefeed");
let next = self.grid.cursor.point.line + 1;
if next == self.scroll_region.end {
- self.scroll_up(Line(1));
+ self.scroll_up(1);
} else if next < self.screen_lines() {
self.grid.cursor.point.line += 1;
}
@@ -1163,19 +1117,19 @@ impl<T: EventListener> Handler for Term<T> {
}
#[inline]
- fn scroll_up(&mut self, lines: Line) {
+ fn scroll_up(&mut self, lines: usize) {
let origin = self.scroll_region.start;
self.scroll_up_relative(origin, lines);
}
#[inline]
- fn scroll_down(&mut self, lines: Line) {
+ fn scroll_down(&mut self, lines: usize) {
let origin = self.scroll_region.start;
self.scroll_down_relative(origin, lines);
}
#[inline]
- fn insert_blank_lines(&mut self, lines: Line) {
+ fn insert_blank_lines(&mut self, lines: usize) {
trace!("Inserting blank {} lines", lines);
let origin = self.grid.cursor.point.line;
@@ -1185,13 +1139,13 @@ impl<T: EventListener> Handler for Term<T> {
}
#[inline]
- fn delete_lines(&mut self, lines: Line) {
+ fn delete_lines(&mut self, lines: usize) {
let origin = self.grid.cursor.point.line;
- let lines = min(self.screen_lines() - origin, lines);
+ let lines = min(self.screen_lines() - origin.0 as usize, lines);
trace!("Deleting {} lines", lines);
- if lines.0 > 0 && self.scroll_region.contains(&self.grid.cursor.point.line) {
+ if lines > 0 && self.scroll_region.contains(&origin) {
self.scroll_up_relative(origin, lines);
}
}
@@ -1203,7 +1157,7 @@ impl<T: EventListener> Handler for Term<T> {
trace!("Erasing chars: count={}, col={}", count, cursor.point.column);
let start = cursor.point.column;
- let end = min(start + count, self.cols());
+ let end = min(start + count, Column(self.columns()));
// Cleared cells have current background color set.
let bg = self.grid.cursor.template.bg;
@@ -1215,28 +1169,28 @@ impl<T: EventListener> Handler for Term<T> {
}
#[inline]
- fn delete_chars(&mut self, count: Column) {
- let cols = self.cols();
+ fn delete_chars(&mut self, count: usize) {
+ let columns = self.columns();
let cursor = &self.grid.cursor;
let bg = cursor.template.bg;
// Ensure deleting within terminal bounds.
- let count = min(count, cols);
+ let count = min(count, columns);
- let start = cursor.point.column;
- let end = min(start + count, cols - 1);
- let num_cells = (cols - end).0;
+ let start = cursor.point.column.0;
+ let end = min(start + count, columns - 1);
+ let num_cells = columns - end;
let line = cursor.point.line;
let row = &mut self.grid[line][..];
for offset in 0..num_cells {
- row.swap(start.0 + offset, end.0 + offset);
+ row.swap(start + offset, end + offset);
}
// Clear last `count` cells in the row. If deleting 1 char, need to delete
// 1 cell.
- let end = (cols - count).0;
+ let end = columns - count;
for cell in &mut row[end..] {
*cell = bg.into();
}
@@ -1305,11 +1259,8 @@ impl<T: EventListener> Handler for Term<T> {
},
}
- let cursor_buffer_line = (self.screen_lines() - self.grid.cursor.point.line - 1).0;
- self.selection = self
- .selection
- .take()
- .filter(|s| !s.intersects_range(cursor_buffer_line..=cursor_buffer_line));
+ let range = self.grid.cursor.point.line..=self.grid.cursor.point.line;
+ self.selection = self.selection.take().filter(|s| !s.intersects_range(range));
}
/// Set the indexed color value.
@@ -1384,29 +1335,26 @@ impl<T: EventListener> Handler for Term<T> {
trace!("Clearing screen: {:?}", mode);
let bg = self.grid.cursor.template.bg;
- let num_lines = self.screen_lines().0;
- let cursor_buffer_line = num_lines - self.grid.cursor.point.line.0 - 1;
+ let screen_lines = self.screen_lines();
match mode {
ansi::ClearMode::Above => {
let cursor = self.grid.cursor.point;
// If clearing more than one line.
- if cursor.line > Line(1) {
+ if cursor.line > 1 {
// Fully clear all lines before the current line.
self.grid.reset_region(..cursor.line);
}
// Clear up to the current column in the current line.
- let end = min(cursor.column + 1, self.cols());
+ let end = min(cursor.column + 1, Column(self.columns()));
for cell in &mut self.grid[cursor.line][..end] {
*cell = bg.into();
}
- self.selection = self
- .selection
- .take()
- .filter(|s| !s.intersects_range(cursor_buffer_line..num_lines));
+ let range = Line(0)..=cursor.line;
+ self.selection = self.selection.take().filter(|s| !s.intersects_range(range));
},
ansi::ClearMode::Below => {
let cursor = self.grid.cursor.point;
@@ -1414,12 +1362,12 @@ impl<T: EventListener> Handler for Term<T> {
*cell = bg.into();
}
- if cursor.line.0 < num_lines - 1 {
+ if (cursor.line.0 as usize) < screen_lines - 1 {
self.grid.reset_region((cursor.line + 1)..);
}
- self.selection =
- self.selection.take().filter(|s| !s.intersects_range(..=cursor_buffer_line));
+ let range = cursor.line..Line(screen_lines as i32);
+ self.selection = self.selection.take().filter(|s| !s.intersects_range(range));
},
ansi::ClearMode::All => {
if self.mode.contains(TermMode::ALT_SCREEN) {
@@ -1428,12 +1376,12 @@ impl<T: EventListener> Handler for Term<T> {
self.grid.clear_viewport();
}
- self.selection = self.selection.take().filter(|s| !s.intersects_range(..num_lines));
+ self.selection = None;
},
ansi::ClearMode::Saved if self.history_size() > 0 => {
self.grid.clear_history();
- self.selection = self.selection.take().filter(|s| !s.intersects_range(num_lines..));
+ self.selection = self.selection.take().filter(|s| !s.intersects_range(..Line(0)));
},
// We have no history to clear.
ansi::ClearMode::Saved => (),
@@ -1463,8 +1411,8 @@ impl<T: EventListener> Handler for Term<T> {
self.cursor_style = None;
self.grid.reset();
self.inactive_grid.reset();
- self.scroll_region = Line(0)..self.screen_lines();
- self.tabs = TabStops::new(self.cols());
+ self.scroll_region = Line(0)..Line(self.screen_lines() as i32);
+ self.tabs = TabStops::new(self.columns());
self.title_stack = Vec::new();
self.title = None;
self.selection = None;
@@ -1482,9 +1430,9 @@ impl<T: EventListener> Handler for Term<T> {
trace!("Reversing index");
// If cursor is at the top.
if self.grid.cursor.point.line == self.scroll_region.start {
- self.scroll_down(Line(1));
+ self.scroll_down(1);
} else {
- self.grid.cursor.point.line = Line(self.grid.cursor.point.line.saturating_sub(1));
+ self.grid.cursor.point.line = max(self.grid.cursor.point.line - 1, Line(0));
}
}
@@ -1628,7 +1576,7 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn set_scrolling_region(&mut self, top: usize, bottom: Option<usize>) {
// Fallback to the last line as default.
- let bottom = bottom.unwrap_or_else(|| self.screen_lines().0);
+ let bottom = bottom.unwrap_or_else(|| self.screen_lines());
if top >= bottom {
debug!("Invalid scrolling region: ({};{})", top, bottom);
@@ -1639,13 +1587,14 @@ impl<T: EventListener> Handler for Term<T> {
// usually included. One option would be to use an inclusive
// range, but instead we just let the open range end be 1
// higher.
- let start = Line(top - 1);
- let end = Line(bottom);
+ let start = Line(top as i32 - 1);
+ let end = Line(bottom as i32);
trace!("Setting scrolling region: ({};{})", start, end);
- self.scroll_region.start = min(start, self.screen_lines());
- self.scroll_region.end = min(end, self.screen_lines());
+ let screen_lines = Line(self.screen_lines() as i32);
+ self.scroll_region.start = min(start, screen_lines);
+ self.scroll_region.end = min(end, screen_lines);
self.goto(Line(0), Column(0));
}
@@ -1732,14 +1681,14 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn text_area_size_pixels<W: io::Write>(&mut self, writer: &mut W) {
- let width = self.cell_width * self.cols().0;
- let height = self.cell_height * self.screen_lines().0;
+ let width = self.cell_width * self.columns();
+ let height = self.cell_height * self.screen_lines();
let _ = write!(writer, "\x1b[4;{};{}t", height, width);
}
#[inline]
fn text_area_size_chars<W: io::Write>(&mut self, writer: &mut W) {
- let _ = write!(writer, "\x1b[8;{};{}t", self.screen_lines(), self.cols());
+ let _ = write!(writer, "\x1b[8;{};{}t", self.screen_lines(), self.columns());
}
}
@@ -1776,12 +1725,8 @@ struct TabStops {
impl TabStops {
#[inline]
- fn new(num_cols: Column) -> TabStops {
- TabStops {
- tabs: IndexRange::from(Column(0)..num_cols)
- .map(|i| (*i as usize) % INITIAL_TABSTOPS == 0)
- .collect::<Vec<bool>>(),
- }
+ fn new(columns: usize) -> TabStops {
+ TabStops { tabs: (0..columns).map(|i| i % INITIAL_TABSTOPS == 0).collect() }
}
/// Remove all tabstops.
@@ -1794,9 +1739,9 @@ impl TabStops {
/// Increase tabstop capacity.
#[inline]
- fn resize(&mut self, num_cols: Column) {
+ fn resize(&mut self, columns: usize) {
let mut index = self.tabs.len();
- self.tabs.resize_with(num_cols.0, || {
+ self.tabs.resize_with(columns, || {
let is_tabstop = index % INITIAL_TABSTOPS == 0;
index += 1;
is_tabstop
@@ -1829,19 +1774,10 @@ impl RenderableCursor {
fn new<T>(term: &Term<T>) -> Self {
// Cursor position.
let vi_mode = term.mode().contains(TermMode::VI);
- let mut point = if vi_mode {
- term.vi_mode_cursor.point
- } else {
- let mut point = term.grid.cursor.point;
- point.line += term.grid.display_offset();
- point
- };
+ let point = if vi_mode { term.vi_mode_cursor.point } else { term.grid.cursor.point };
// Cursor shape.
- let shape = if !vi_mode
- && (!term.mode().contains(TermMode::SHOW_CURSOR) || point.line >= term.screen_lines())
- {
- point.line = Line(0);
+ let shape = if !vi_mode && !term.mode().contains(TermMode::SHOW_CURSOR) {
CursorShape::Hidden
} else {
term.cursor_style().shape
@@ -1856,7 +1792,7 @@ impl RenderableCursor {
/// This contains all content required to render the current terminal view.
pub struct RenderableContent<'a> {
pub display_iter: DisplayIter<'a, Cell>,
- pub selection: Option<SelectionRange<Line>>,
+ pub selection: Option<SelectionRange>,
pub cursor: RenderableCursor,
pub display_offset: usize,
pub colors: &'a color::Colors,
@@ -1869,7 +1805,7 @@ impl<'a> RenderableContent<'a> {
display_iter: term.grid().display_iter(),
display_offset: term.grid().display_offset(),
cursor: RenderableCursor::new(term),
- selection: term.visible_selection(),
+ selection: term.selection.as_ref().and_then(|s| s.to_range(term)),
colors: &term.colors,
mode: *term.mode(),
}
@@ -1917,8 +1853,9 @@ pub mod test {
let mut term = Term::new(&Config::<()>::default(), size, ());
// Fill terminal with content.
- for (line, text) in lines.iter().rev().enumerate() {
- if !text.ends_with('\r') && line != 0 {
+ for (line, text) in lines.iter().enumerate() {
+ let line = Line(line as i32);
+ if !text.ends_with('\r') && line + 1 != lines.len() {
term.grid[line][Column(num_cols - 1)].flags.insert(Flags::WRAPLINE);
}
@@ -1950,15 +1887,15 @@ mod tests {
use crate::ansi::{self, CharsetIndex, Handler, StandardCharset};
use crate::config::MockConfig;
use crate::grid::{Grid, Scroll};
- use crate::index::{Column, Line, Point, Side};
+ use crate::index::{Column, Point, Side};
use crate::selection::{Selection, SelectionType};
use crate::term::cell::{Cell, Flags};
#[test]
fn semantic_selection_works() {
- let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
+ let size = SizeInfo::new(5., 3., 1.0, 1.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, ());
- let mut grid: Grid<Cell> = Grid::new(Line(3), Column(5), 0);
+ let mut grid: Grid<Cell> = Grid::new(3, 5, 0);
for i in 0..5 {
for j in 0..2 {
grid[Line(j)][Column(i)].c = 'a';
@@ -1977,7 +1914,7 @@ mod tests {
{
term.selection = Some(Selection::new(
SelectionType::Semantic,
- Point { line: 2, column: Column(1) },
+ Point { line: Line(0), column: Column(1) },
Side::Left,
));
assert_eq!(term.selection_to_string(), Some(String::from("aa")));
@@ -1986,7 +1923,7 @@ mod tests {
{
term.selection = Some(Selection::new(
SelectionType::Semantic,
- Point { line: 2, column: Column(4) },
+ Point { line: Line(0), column: Column(4) },
Side::Left,
));
assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
@@ -1995,7 +1932,7 @@ mod tests {
{
term.selection = Some(Selection::new(
SelectionType::Semantic,
- Point { line: 1, column: Column(1) },
+ Point { line: Line(1), column: Column(1) },
Side::Left,
));
assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
@@ -2004,9 +1941,9 @@ mod tests {
#[test]
fn line_selection_works() {
- let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
+ let size = SizeInfo::new(5., 1., 1.0, 1.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, ());
- let mut grid: Grid<Cell> = Grid::new(Line(1), Column(5), 0);
+ let mut grid: Grid<Cell> = Grid::new(1, 5, 0);
for i in 0..5 {
grid[Line(0)][Column(i)].c = 'a';
}
@@ -2017,7 +1954,7 @@ mod tests {
term.selection = Some(Selection::new(
SelectionType::Lines,
- Point { line: 0, column: Column(3) },
+ Point { line: Line(0), column: Column(3) },
Side::Left,
));
assert_eq!(term.selection_to_string(), Some(String::from("\"aa\"a\n")));
@@ -2025,9 +1962,9 @@ mod tests {
#[test]
fn selecting_empty_line() {
- let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
+ let size = SizeInfo::new(3.0, 3.0, 1.0, 1.0, 0.0, 0.0, false);
let mut term = Term::new(&MockConfig::default(), size, ());
- let mut grid: Grid<Cell> = Grid::new(Line(3), Column(3), 0);
+ let mut grid: Grid<Cell> = Grid::new(3, 3, 0);
for l in 0..3 {
if l != 1 {
for c in 0..3 {
@@ -2038,9 +1975,12 @@ mod tests {
mem::swap(&mut term.grid, &mut grid);
- let mut selection =
- Selection::new(SelectionType::Simple, Point { line: 2, column: Column(0) }, Side::Left);
- selection.update(Point { line: 0, column: Column(2) }, Side::Right);
+ let mut selection = Selection::new(
+ SelectionType::Simple,
+ Point { line: Line(0), column: Column(0) },
+ Side::Left,
+ );
+ selection.update(Point { line: Line(2), column: Column(2) }, Side::Right);
term.selection = Some(selection);
assert_eq!(term.selection_to_string(), Some("aaa\n\naaa\n".into()));
}
@@ -2051,7 +1991,7 @@ mod tests {
/// test this property with a T=Cell.
#[test]
fn grid_serde() {
- let grid: Grid<Cell> = Grid::new(Line(24), Column(80), 0);
+ let grid: Grid<Cell> = Grid::new(24, 80, 0);
let serialized = serde_json::to_string(&grid).expect("ser");
let deserialized = serde_json::from_str::<Grid<Cell>>(&serialized).expect("de");
@@ -2075,7 +2015,7 @@ mod tests {
let mut term = Term::new(&MockConfig::default(), size, ());
// Add one line of scrollback.
- term.grid.scroll_up(&(Line(0)..Line(1)), Line(1));
+ term.grid.scroll_up(&(Line(0)..Line(1)), 1);
// Clear the history.
term.clear_screen(ansi::ClearMode::Saved);
@@ -2104,7 +2044,7 @@ mod tests {
assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0)));
// Increase visible lines.
- size.screen_lines.0 = 30;
+ size.screen_lines = 30;
term.resize(size);
assert_eq!(term.history_size(), 0);
@@ -2127,7 +2067,7 @@ mod tests {
term.set_mode(ansi::Mode::SwapScreenAndSetRestoreCursor);
// Increase visible lines.
- size.screen_lines.0 = 30;
+ size.screen_lines = 30;
term.resize(size);
// Leave alt screen.
@@ -2150,7 +2090,7 @@ mod tests {
assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0)));
// Increase visible lines.
- size.screen_lines.0 = 5;
+ size.screen_lines = 5;
term.resize(size);
assert_eq!(term.history_size(), 15);
@@ -2173,7 +2113,7 @@ mod tests {
term.set_mode(ansi::Mode::SwapScreenAndSetRestoreCursor);
// Increase visible lines.
- size.screen_lines.0 = 5;
+ size.screen_lines = 5;
term.resize(size);
// Leave alt screen.
diff --git a/alacritty_terminal/src/term/search.rs b/alacritty_terminal/src/term/search.rs
index 0eba7567..638df670 100644
--- a/alacritty_terminal/src/term/search.rs
+++ b/alacritty_terminal/src/term/search.rs
@@ -1,4 +1,4 @@
-use std::cmp::min;
+use std::cmp::max;
use std::mem;
use std::ops::RangeInclusive;
@@ -12,7 +12,7 @@ use crate::term::Term;
/// Used to match equal brackets, when performing a bracket-pair selection.
const BRACKET_PAIRS: [(char, char); 4] = [('(', ')'), ('[', ']'), ('{', '}'), ('<', '>')];
-pub type Match = RangeInclusive<Point<usize>>;
+pub type Match = RangeInclusive<Point>;
/// Terminal regex search state.
#[derive(Clone, Debug)]
@@ -53,7 +53,7 @@ impl<T> Term<T> {
pub fn search_next(
&self,
dfas: &RegexSearch,
- mut origin: Point<usize>,
+ mut origin: Point,
direction: Direction,
side: Side,
mut max_lines: Option<usize>,
@@ -72,7 +72,7 @@ impl<T> Term<T> {
fn next_match_right(
&self,
dfas: &RegexSearch,
- origin: Point<usize>,
+ origin: Point,
side: Side,
max_lines: Option<usize>,
) -> Option<Match> {
@@ -80,13 +80,12 @@ impl<T> Term<T> {
let mut end = start;
// Limit maximum number of lines searched.
- let total_lines = self.total_lines();
end = match max_lines {
Some(max_lines) => {
- let line = (start.line + total_lines - max_lines) % total_lines;
- Point::new(line, self.cols() - 1)
+ let line = (start.line + max_lines).grid_clamp(self, Boundary::Grid);
+ Point::new(line, self.last_column())
},
- _ => end.sub_absolute(self, Boundary::Wrap, 1),
+ _ => end.sub(self, Boundary::None, 1),
};
let mut regex_iter = RegexIter::new(start, end, Direction::Right, &self, dfas).peekable();
@@ -99,8 +98,8 @@ impl<T> Term<T> {
let match_point = Self::match_side(&regex_match, side);
// If the match's point is beyond the origin, we're done.
- match_point.line > start.line
- || match_point.line < origin.line
+ match_point.line < start.line
+ || match_point.line > origin.line
|| (match_point.line == origin.line && match_point.column >= origin.column)
})
.unwrap_or(first_match);
@@ -112,7 +111,7 @@ impl<T> Term<T> {
fn next_match_left(
&self,
dfas: &RegexSearch,
- origin: Point<usize>,
+ origin: Point,
side: Side,
max_lines: Option<usize>,
) -> Option<Match> {
@@ -121,8 +120,11 @@ impl<T> Term<T> {
// Limit maximum number of lines searched.
end = match max_lines {
- Some(max_lines) => Point::new((start.line + max_lines) % self.total_lines(), Column(0)),
- _ => end.add_absolute(self, Boundary::Wrap, 1),
+ Some(max_lines) => {
+ let line = (start.line - max_lines).grid_clamp(self, Boundary::Grid);
+ Point::new(line, Column(0))
+ },
+ _ => end.add(self, Boundary::None, 1),
};
let mut regex_iter = RegexIter::new(start, end, Direction::Left, &self, dfas).peekable();
@@ -135,8 +137,8 @@ impl<T> Term<T> {
let match_point = Self::match_side(&regex_match, side);
// If the match's point is beyond the origin, we're done.
- match_point.line < start.line
- || match_point.line > origin.line
+ match_point.line > start.line
+ || match_point.line < origin.line
|| (match_point.line == origin.line && match_point.column <= origin.column)
})
.unwrap_or(first_match);
@@ -145,7 +147,7 @@ impl<T> Term<T> {
}
/// Get the side of a match.
- fn match_side(regex_match: &Match, side: Side) -> Point<usize> {
+ fn match_side(regex_match: &Match, side: Side) -> Point {
match side {
Side::Right => *regex_match.end(),
Side::Left => *regex_match.start(),
@@ -155,12 +157,7 @@ impl<T> Term<T> {
/// Find the next regex match to the left of the origin point.
///
/// The origin is always included in the regex.
- pub fn regex_search_left(
- &self,
- dfas: &RegexSearch,
- start: Point<usize>,
- end: Point<usize>,
- ) -> Option<Match> {
+ pub fn regex_search_left(&self, dfas: &RegexSearch, start: Point, end: Point) -> Option<Match> {
// Find start and end of match.
let match_start = self.regex_search(start, end, Direction::Left, &dfas.left_fdfa)?;
let match_end = self.regex_search(match_start, start, Direction::Right, &dfas.left_rdfa)?;
@@ -174,8 +171,8 @@ impl<T> Term<T> {
pub fn regex_search_right(
&self,
dfas: &RegexSearch,
- start: Point<usize>,
- end: Point<usize>,
+ start: Point,
+ end: Point,
) -> Option<Match> {
// Find start and end of match.
let match_end = self.regex_search(start, end, Direction::Right, &dfas.right_fdfa)?;
@@ -189,13 +186,14 @@ impl<T> Term<T> {
/// This will always return the side of the first match which is farthest from the start point.
fn regex_search(
&self,
- start: Point<usize>,
- end: Point<usize>,
+ start: Point,
+ end: Point,
direction: Direction,
dfa: &impl DFA,
- ) -> Option<Point<usize>> {
- let last_line = self.total_lines() - 1;
- let last_col = self.cols() - 1;
+ ) -> Option<Point> {
+ let topmost_line = self.topmost_line();
+ let screen_lines = self.screen_lines() as i32;
+ let last_column = self.last_column();
// Advance the iterator.
let next = match direction {
@@ -250,7 +248,8 @@ impl<T> Term<T> {
Some(Indexed { cell, .. }) => cell,
None => {
// Wrap around to other end of the scrollback buffer.
- let start = Point::new(last_line - point.line, last_col - point.column);
+ let line = topmost_line - point.line + screen_lines - 1;
+ let start = Point::new(line, last_column - point.column);
iter = self.grid.iter_from(start);
iter.cell()
},
@@ -262,8 +261,8 @@ impl<T> Term<T> {
let last_point = mem::replace(&mut point, iter.point());
// Handle linebreaks.
- if (last_point.column == last_col && point.column == Column(0) && !last_wrapped)
- || (last_point.column == Column(0) && point.column == last_col && !wrapped)
+ if (last_point.column == last_column && point.column == Column(0) && !last_wrapped)
+ || (last_point.column == Column(0) && point.column == last_column && !wrapped)
{
match regex_match {
Some(_) => break,
@@ -299,7 +298,7 @@ impl<T> Term<T> {
*cell = new_cell;
}
- let prev = iter.point().sub_absolute(self, Boundary::Clamp, 1);
+ let prev = iter.point().sub(self, Boundary::Grid, 1);
if self.grid[prev].flags.contains(Flags::LEADING_WIDE_CHAR_SPACER) {
iter.prev();
}
@@ -309,8 +308,8 @@ impl<T> Term<T> {
}
/// Find next matching bracket.
- pub fn bracket_search(&self, point: Point<usize>) -> Option<Point<usize>> {
- let start_char = self.grid[point.line][point.column].c;
+ pub fn bracket_search(&self, point: Point) -> Option<Point> {
+ let start_char = self.grid[point].c;
// Find the matching bracket we're looking for
let (forward, end_char) = BRACKET_PAIRS.iter().find_map(|(open, close)| {
@@ -353,12 +352,12 @@ impl<T> Term<T> {
}
/// Find left end of semantic block.
- pub fn semantic_search_left(&self, mut point: Point<usize>) -> Point<usize> {
+ pub fn semantic_search_left(&self, mut point: Point) -> Point {
// Limit the starting point to the last line in the history
- point.line = min(point.line, self.total_lines() - 1);
+ point.line = max(point.line, self.topmost_line());
let mut iter = self.grid.iter_from(point);
- let last_col = self.cols() - Column(1);
+ let last_column = self.columns() - 1;
let wide = Flags::WIDE_CHAR | Flags::WIDE_CHAR_SPACER | Flags::LEADING_WIDE_CHAR_SPACER;
while let Some(cell) = iter.prev() {
@@ -366,7 +365,7 @@ impl<T> Term<T> {
break;
}
- if cell.point.column == last_col && !cell.flags.contains(Flags::WRAPLINE) {
+ if cell.point.column == last_column && !cell.flags.contains(Flags::WRAPLINE) {
break; // cut off if on new line or hit escape char
}
@@ -377,12 +376,12 @@ impl<T> Term<T> {
}
/// Find right end of semantic block.
- pub fn semantic_search_right(&self, mut point: Point<usize>) -> Point<usize> {
+ pub fn semantic_search_right(&self, mut point: Point) -> Point {
// Limit the starting point to the last line in the history
- point.line = min(point.line, self.total_lines() - 1);
+ point.line = max(point.line, self.topmost_line());
let wide = Flags::WIDE_CHAR | Flags::WIDE_CHAR_SPACER | Flags::LEADING_WIDE_CHAR_SPACER;
- let last_col = self.cols() - 1;
+ let last_column = self.columns() - 1;
for cell in self.grid.iter_from(point) {
if !cell.flags.intersects(wide) && self.semantic_escape_chars.contains(cell.c) {
@@ -391,7 +390,7 @@ impl<T> Term<T> {
point = cell.point;
- if point.column == last_col && !cell.flags.contains(Flags::WRAPLINE) {
+ if point.column == last_column && !cell.flags.contains(Flags::WRAPLINE) {
break; // cut off if on new line or hit escape char
}
}
@@ -400,11 +399,11 @@ impl<T> Term<T> {
}
/// Find the beginning of the current line across linewraps.
- pub fn line_search_left(&self, mut point: Point<usize>) -> Point<usize> {
- while point.line + 1 < self.total_lines()
- && self.grid[point.line + 1][self.cols() - 1].flags.contains(Flags::WRAPLINE)
+ pub fn line_search_left(&self, mut point: Point) -> Point {
+ while point.line > self.topmost_line()
+ && self.grid[point.line - 1i32][self.last_column()].flags.contains(Flags::WRAPLINE)
{
- point.line += 1;
+ point.line -= 1;
}
point.column = Column(0);
@@ -413,14 +412,14 @@ impl<T> Term<T> {
}
/// Find the end of the current line across linewraps.
- pub fn line_search_right(&self, mut point: Point<usize>) -> Point<usize> {
- while point.line > 0
- && self.grid[point.line][self.cols() - 1].flags.contains(Flags::WRAPLINE)
+ pub fn line_search_right(&self, mut point: Point) -> Point {
+ while point.line + 1 < self.screen_lines()
+ && self.grid[point.line][self.last_column()].flags.contains(Flags::WRAPLINE)
{
- point.line -= 1;
+ point.line += 1;
}
- point.column = self.cols() - 1;
+ point.column = self.last_column();
point
}
@@ -428,8 +427,8 @@ impl<T> Term<T> {
/// Iterator over regex matches.
pub struct RegexIter<'a, T> {
- point: Point<usize>,
- end: Point<usize>,
+ point: Point,
+ end: Point,
direction: Direction,
dfas: &'a RegexSearch,
term: &'a Term<T>,
@@ -438,8 +437,8 @@ pub struct RegexIter<'a, T> {
impl<'a, T> RegexIter<'a, T> {
pub fn new(
- start: Point<usize>,
- end: Point<usize>,
+ start: Point,
+ end: Point,
direction: Direction,
term: &'a Term<T>,
dfas: &'a RegexSearch,
@@ -452,8 +451,8 @@ impl<'a, T> RegexIter<'a, T> {
self.point = self.term.expand_wide(self.point, self.direction);
self.point = match self.direction {
- Direction::Right => self.point.add_absolute(self.term, Boundary::Wrap, 1),
- Direction::Left => self.point.sub_absolute(self.term, Boundary::Wrap, 1),
+ Direction::Right => self.point.add(self.term, Boundary::None, 1),
+ Direction::Left => self.point.sub(self.term, Boundary::None, 1),
};
}
@@ -498,7 +497,7 @@ impl<'a, T> Iterator for RegexIter<'a, T> {
mod tests {
use super::*;
- use crate::index::Column;
+ use crate::index::{Column, Line};
use crate::term::test::mock_term;
#[test]
@@ -514,10 +513,10 @@ mod tests {
// Check regex across wrapped and unwrapped lines.
let dfas = RegexSearch::new("Ala.*123").unwrap();
- let start = Point::new(3, Column(0));
- let end = Point::new(0, Column(2));
- let match_start = Point::new(3, Column(0));
- let match_end = Point::new(2, Column(2));
+ let start = Point::new(Line(1), Column(0));
+ let end = Point::new(Line(4), Column(2));
+ let match_start = Point::new(Line(1), Column(0));
+ let match_end = Point::new(Line(2), Column(2));
assert_eq!(term.regex_search_right(&dfas, start, end), Some(match_start..=match_end));
}
@@ -534,10 +533,10 @@ mod tests {
// Check regex across wrapped and unwrapped lines.
let dfas = RegexSearch::new("Ala.*123").unwrap();
- let start = Point::new(0, Column(2));
- let end = Point::new(3, Column(0));
- let match_start = Point::new(3, Column(0));
- let match_end = Point::new(2, Column(2));
+ let start = Point::new(Line(4), Column(2));
+ let end = Point::new(Line(1), Column(0));
+ let match_start = Point::new(Line(1), Column(0));
+ let match_end = Point::new(Line(2), Column(2));
assert_eq!(term.regex_search_left(&dfas, start, end), Some(match_start..=match_end));
}
@@ -551,14 +550,14 @@ mod tests {
// Greedy stopped at linebreak.
let dfas = RegexSearch::new("Ala.*critty").unwrap();
- let start = Point::new(1, Column(0));
- let end = Point::new(1, Column(25));
+ let start = Point::new(Line(0), Column(0));
+ let end = Point::new(Line(0), Column(25));
assert_eq!(term.regex_search_right(&dfas, start, end), Some(start..=end));
// Greedy stopped at dead state.
let dfas = RegexSearch::new("Ala[^y]*critty").unwrap();
- let start = Point::new(1, Column(0));
- let end = Point::new(1, Column(15));
+ let start = Point::new(Line(0), Column(0));
+ let end = Point::new(Line(0), Column(15));
assert_eq!(term.regex_search_right(&dfas, start, end), Some(start..=end));
}
@@ -572,8 +571,8 @@ mod tests {
");
let dfas = RegexSearch::new("nothing").unwrap();
- let start = Point::new(2, Column(0));
- let end = Point::new(0, Column(4));
+ let start = Point::new(Line(2), Column(0));
+ let end = Point::new(Line(0), Column(4));
assert_eq!(term.regex_search_right(&dfas, start, end), None);
}
@@ -587,8 +586,8 @@ mod tests {
");
let dfas = RegexSearch::new("nothing").unwrap();
- let start = Point::new(0, Column(4));
- let end = Point::new(2, Column(0));
+ let start = Point::new(Line(0), Column(4));
+ let end = Point::new(Line(2), Column(0));
assert_eq!(term.regex_search_left(&dfas, start, end), None);
}
@@ -602,10 +601,10 @@ mod tests {
// Make sure the cell containing the linebreak is not skipped.
let dfas = RegexSearch::new("te.*123").unwrap();
- let start = Point::new(0, Column(0));
- let end = Point::new(1, Column(0));
- let match_start = Point::new(1, Column(0));
- let match_end = Point::new(1, Column(9));
+ let start = Point::new(Line(1), Column(0));
+ let end = Point::new(Line(0), Column(0));
+ let match_start = Point::new(Line(0), Column(0));
+ let match_end = Point::new(Line(0), Column(9));
assert_eq!(term.regex_search_left(&dfas, start, end), Some(match_start..=match_end));
}
@@ -619,9 +618,9 @@ mod tests {
// Make sure the cell containing the linebreak is not skipped.
let dfas = RegexSearch::new("te.*123").unwrap();
- let start = Point::new(1, Column(2));
- let end = Point::new(0, Column(9));
- let match_start = Point::new(0, Column(0));
+ let start = Point::new(Line(0), Column(2));
+ let end = Point::new(Line(1), Column(9));
+ let match_start = Point::new(Line(1), Column(0));
assert_eq!(term.regex_search_right(&dfas, start, end), Some(match_start..=end));
}
@@ -631,8 +630,8 @@ mod tests {
// Make sure dead state cell is skipped when reversing.
let dfas = RegexSearch::new("alacrit").unwrap();
- let start = Point::new(0, Column(0));
- let end = Point::new(0, Column(6));
+ let start = Point::new(Line(0), Column(0));
+ let end = Point::new(Line(0), Column(6));
assert_eq!(term.regex_search_right(&dfas, start, end), Some(start..=end));
}
@@ -642,10 +641,10 @@ mod tests {
// Make sure the reverse DFA operates the same as a forward DFA.
let dfas = RegexSearch::new("zoo").unwrap();
- let start = Point::new(0, Column(9));
- let end = Point::new(0, Column(0));
- let match_start = Point::new(0, Column(0));
- let match_end = Point::new(0, Column(2));
+ let start = Point::new(Line(0), Column(9));
+ let end = Point::new(Line(0), Column(0));
+ let match_start = Point::new(Line(0), Column(0));
+ let match_end = Point::new(Line(0), Column(2));
assert_eq!(term.regex_search_left(&dfas, start, end), Some(match_start..=match_end));
}
@@ -654,13 +653,13 @@ mod tests {
let term = mock_term("testвосибing");
let dfas = RegexSearch::new("te.*ing").unwrap();
- let start = Point::new(0, Column(0));
- let end = Point::new(0, Column(11));
+ let start = Point::new(Line(0), Column(0));
+ let end = Point::new(Line(0), Column(11));
assert_eq!(term.regex_search_right(&dfas, start, end), Some(start..=end));
let dfas = RegexSearch::new("te.*ing").unwrap();
- let start = Point::new(0, Column(11));
- let end = Point::new(0, Column(0));
+ let start = Point::new(Line(0), Column(11));
+ let end = Point::new(Line(0), Column(0));
assert_eq!(term.regex_search_left(&dfas, start, end), Some(end..=start));
}
@@ -669,13 +668,13 @@ mod tests {
let term = mock_term("a🦇x🦇");
let dfas = RegexSearch::new("[^ ]*").unwrap();
- let start = Point::new(0, Column(0));
- let end = Point::new(0, Column(5));
+ let start = Point::new(Line(0), Column(0));
+ let end = Point::new(Line(0), Column(5));
assert_eq!(term.regex_search_right(&dfas, start, end), Some(start..=end));
let dfas = RegexSearch::new("[^ ]*").unwrap();
- let start = Point::new(0, Column(5));
- let end = Point::new(0, Column(0));
+ let start = Point::new(Line(0), Column(5));
+ let end = Point::new(Line(0), Column(0));
assert_eq!(term.regex_search_left(&dfas, start, end), Some(end..=start));
}
@@ -684,13 +683,13 @@ mod tests {
let term = mock_term("🦇");
let dfas = RegexSearch::new("🦇").unwrap();
- let start = Point::new(0, Column(0));
- let end = Point::new(0, Column(1));
+ let start = Point::new(Line(0), Column(0));
+ let end = Point::new(Line(0), Column(1));
assert_eq!(term.regex_search_right(&dfas, start, end), Some(start..=end));
let dfas = RegexSearch::new("🦇").unwrap();
- let start = Point::new(0, Column(1));
- let end = Point::new(0, Column(0));
+ let start = Point::new(Line(0), Column(1));
+ let end = Point::new(Line(0), Column(0));
assert_eq!(term.regex_search_left(&dfas, start, end), Some(end..=start));
}
@@ -703,15 +702,15 @@ mod tests {
");
let dfas = RegexSearch::new("xxx").unwrap();
- let start = Point::new(0, Column(2));
- let end = Point::new(1, Column(2));
- let match_start = Point::new(1, Column(0));
+ let start = Point::new(Line(0), Column(2));
+ let end = Point::new(Line(1), Column(2));
+ let match_start = Point::new(Line(1), Column(0));
assert_eq!(term.regex_search_right(&dfas, start, end), Some(match_start..=end));
let dfas = RegexSearch::new("xxx").unwrap();
- let start = Point::new(1, Column(0));
- let end = Point::new(0, Column(0));
- let match_end = Point::new(0, Column(2));
+ let start = Point::new(Line(1), Column(0));
+ let end = Point::new(Line(0), Column(0));
+ let match_end = Point::new(Line(0), Column(2));
assert_eq!(term.regex_search_left(&dfas, start, end), Some(end..=match_end));
}
@@ -724,17 +723,17 @@ mod tests {
");
let dfas = RegexSearch::new("🦇x").unwrap();
- let start = Point::new(0, Column(0));
- let end = Point::new(1, Column(3));
- let match_start = Point::new(1, Column(0));
- let match_end = Point::new(1, Column(2));
+ let start = Point::new(Line(1), Column(0));
+ let end = Point::new(Line(0), Column(3));
+ let match_start = Point::new(Line(0), Column(0));
+ let match_end = Point::new(Line(0), Column(2));
assert_eq!(term.regex_search_right(&dfas, start, end), Some(match_start..=match_end));
let dfas = RegexSearch::new("x🦇").unwrap();
- let start = Point::new(1, Column(2));
- let end = Point::new(0, Column(0));
- let match_start = Point::new(0, Column(1));
- let match_end = Point::new(0, Column(3));
+ let start = Point::new(Line(0), Column(2));
+ let end = Point::new(Line(1), Column(0));
+ let match_start = Point::new(Line(1), Column(1));
+ let match_end = Point::new(Line(1), Column(3));
assert_eq!(term.regex_search_left(&dfas, start, end), Some(match_start..=match_end));
}
@@ -745,34 +744,34 @@ mod tests {
xxx \n\
🦇xx\
");
- term.grid[1][Column(3)].flags.insert(Flags::LEADING_WIDE_CHAR_SPACER);
+ term.grid[Line(0)][Column(3)].flags.insert(Flags::LEADING_WIDE_CHAR_SPACER);
let dfas = RegexSearch::new("🦇x").unwrap();
- let start = Point::new(1, Column(0));
- let end = Point::new(0, Column(3));
- let match_start = Point::new(1, Column(3));
- let match_end = Point::new(0, Column(2));
+ let start = Point::new(Line(0), Column(0));
+ let end = Point::new(Line(1), Column(3));
+ let match_start = Point::new(Line(0), Column(3));
+ let match_end = Point::new(Line(1), Column(2));
assert_eq!(term.regex_search_right(&dfas, start, end), Some(match_start..=match_end));
let dfas = RegexSearch::new("🦇x").unwrap();
- let start = Point::new(0, Column(3));
- let end = Point::new(1, Column(0));
- let match_start = Point::new(1, Column(3));
- let match_end = Point::new(0, Column(2));
+ let start = Point::new(Line(1), Column(3));
+ let end = Point::new(Line(0), Column(0));
+ let match_start = Point::new(Line(0), Column(3));
+ let match_end = Point::new(Line(1), Column(2));
assert_eq!(term.regex_search_left(&dfas, start, end), Some(match_start..=match_end));
let dfas = RegexSearch::new("x🦇").unwrap();
- let start = Point::new(1, Column(0));
- let end = Point::new(0, Column(3));
- let match_start = Point::new(1, Column(2));
- let match_end = Point::new(0, Column(1));
+ let start = Point::new(Line(0), Column(0));
+ let end = Point::new(Line(1), Column(3));
+ let match_start = Point::new(Line(0), Column(2));
+ let match_end = Point::new(Line(1), Column(1));
assert_eq!(term.regex_search_right(&dfas, start, end), Some(match_start..=match_end));
let dfas = RegexSearch::new("x🦇").unwrap();
- let start = Point::new(0, Column(3));
- let end = Point::new(1, Column(0));
- let match_start = Point::new(1, Column(2));
- let match_end = Point::new(0, Column(1));
+ let start = Point::new(Line(1), Column(3));
+ let end = Point::new(Line(0), Column(0));
+ let match_start = Point::new(Line(0), Column(2));
+ let match_end = Point::new(Line(1), Column(1));
assert_eq!(term.regex_search_left(&dfas, start, end), Some(match_start..=match_end));
}
}
diff --git a/alacritty_terminal/src/tty/unix.rs b/alacritty_terminal/src/tty/unix.rs
index 9cce1983..ba59bb66 100644
--- a/alacritty_terminal/src/tty/unix.rs
+++ b/alacritty_terminal/src/tty/unix.rs
@@ -25,6 +25,7 @@ use signal_hook::{self as sighook, iterator::Signals};
use crate::config::{Config, Program};
use crate::event::OnResize;
+use crate::grid::Dimensions;
use crate::term::SizeInfo;
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite};
@@ -357,8 +358,8 @@ pub trait ToWinsize {
impl<'a> ToWinsize for &'a SizeInfo {
fn to_winsize(&self) -> winsize {
winsize {
- ws_row: self.screen_lines().0 as libc::c_ushort,
- ws_col: self.cols().0 as libc::c_ushort,
+ ws_row: self.screen_lines() as libc::c_ushort,
+ ws_col: self.columns() as libc::c_ushort,
ws_xpixel: self.width() as libc::c_ushort,
ws_ypixel: self.height() as libc::c_ushort,
}
diff --git a/alacritty_terminal/src/tty/windows/conpty.rs b/alacritty_terminal/src/tty/windows/conpty.rs
index de3d8d4e..919bd00f 100644
--- a/alacritty_terminal/src/tty/windows/conpty.rs
+++ b/alacritty_terminal/src/tty/windows/conpty.rs
@@ -19,6 +19,7 @@ use winapi::um::wincontypes::{COORD, HPCON};
use crate::config::Config;
use crate::event::OnResize;
+use crate::grid::Dimensions;
use crate::term::SizeInfo;
use crate::tty::windows::child::ChildExitWatcher;
use crate::tty::windows::{cmdline, win32_string, Pty};
@@ -185,11 +186,11 @@ impl OnResize for Conpty {
/// Helper to build a COORD from a SizeInfo, returning None in overflow cases.
fn coord_from_sizeinfo(size: &SizeInfo) -> Option<COORD> {
- let cols = size.cols().0;
- let lines = size.screen_lines().0;
+ let lines = size.screen_lines();
+ let columns = size.columns();
- if cols <= i16::MAX as usize && lines <= i16::MAX as usize {
- Some(COORD { X: cols as i16, Y: lines as i16 })
+ if columns <= i16::MAX as usize && lines <= i16::MAX as usize {
+ Some(COORD { X: columns as i16, Y: lines as i16 })
} else {
None
}
diff --git a/alacritty_terminal/src/vi_mode.rs b/alacritty_terminal/src/vi_mode.rs
index 1b3390d6..54229998 100644
--- a/alacritty_terminal/src/vi_mode.rs
+++ b/alacritty_terminal/src/vi_mode.rs
@@ -65,135 +65,126 @@ impl ViModeCursor {
/// Move vi mode cursor.
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn motion<T: EventListener>(mut self, term: &mut Term<T>, motion: ViMotion) -> Self {
- let display_offset = term.grid().display_offset();
- let lines = term.screen_lines();
- let cols = term.cols();
-
- let mut buffer_point = term.visible_to_buffer(self.point);
-
match motion {
ViMotion::Up => {
- if buffer_point.line + 1 < term.total_lines() {
- buffer_point.line += 1;
+ if self.point.line > term.topmost_line() {
+ self.point.line -= 1;
+ }
+ },
+ ViMotion::Down => {
+ if self.point.line + 1 < term.screen_lines() as i32 {
+ self.point.line += 1;
}
},
- ViMotion::Down => buffer_point.line = buffer_point.line.saturating_sub(1),
ViMotion::Left => {
- buffer_point = term.expand_wide(buffer_point, Direction::Left);
- let wrap_point = Point::new(buffer_point.line + 1, cols - 1);
- if buffer_point.column.0 == 0
- && buffer_point.line + 1 < term.total_lines()
+ self.point = term.expand_wide(self.point, Direction::Left);
+ let wrap_point = Point::new(self.point.line - 1, term.last_column());
+ if self.point.column == 0
+ && self.point.line > term.topmost_line()
&& is_wrap(term, wrap_point)
{
- buffer_point = wrap_point;
+ self.point = wrap_point;
} else {
- buffer_point.column = Column(buffer_point.column.saturating_sub(1));
+ self.point.column = Column(self.point.column.saturating_sub(1));
}
},
ViMotion::Right => {
- buffer_point = term.expand_wide(buffer_point, Direction::Right);
- if is_wrap(term, buffer_point) {
- buffer_point = Point::new(buffer_point.line - 1, Column(0));
+ self.point = term.expand_wide(self.point, Direction::Right);
+ if is_wrap(term, self.point) {
+ self.point = Point::new(self.point.line + 1, Column(0));
} else {
- buffer_point.column = min(buffer_point.column + 1, cols - 1);
+ self.point.column = min(self.point.column + 1, term.last_column());
}
},
ViMotion::First => {
- buffer_point = term.expand_wide(buffer_point, Direction::Left);
- while buffer_point.column.0 == 0
- && buffer_point.line + 1 < term.total_lines()
- && is_wrap(term, Point::new(buffer_point.line + 1, cols - 1))
+ self.point = term.expand_wide(self.point, Direction::Left);
+ while self.point.column == 0
+ && self.point.line > term.topmost_line()
+ && is_wrap(term, Point::new(self.point.line - 1, term.last_column()))
{
- buffer_point.line += 1;
+ self.point.line -= 1;
}
- buffer_point.column = Column(0);
+ self.point.column = Column(0);
},
- ViMotion::Last => buffer_point = last(term, buffer_point),
- ViMotion::FirstOccupied => buffer_point = first_occupied(term, buffer_point),
+ ViMotion::Last => self.point = last(term, self.point),
+ ViMotion::FirstOccupied => self.point = first_occupied(term, self.point),
ViMotion::High => {
- let line = display_offset + lines.0 - 1;
+ let line = Line(-(term.grid().display_offset() as i32));
let col = first_occupied_in_line(term, line).unwrap_or_default().column;
- buffer_point = Point::new(line, col);
+ self.point = Point::new(line, col);
},
ViMotion::Middle => {
- let line = display_offset + lines.0 / 2;
+ let display_offset = term.grid().display_offset() as i32;
+ let line = Line(-display_offset + term.screen_lines() as i32 / 2 - 1);
let col = first_occupied_in_line(term, line).unwrap_or_default().column;
- buffer_point = Point::new(line, col);
+ self.point = Point::new(line, col);
},
ViMotion::Low => {
- let line = display_offset;
+ let display_offset = term.grid().display_offset() as i32;
+ let line = Line(-display_offset + term.screen_lines() as i32 - 1);
let col = first_occupied_in_line(term, line).unwrap_or_default().column;
- buffer_point = Point::new(line, col);
+ self.point = Point::new(line, col);
},
ViMotion::SemanticLeft => {
- buffer_point = semantic(term, buffer_point, Direction::Left, Side::Left);
+ self.point = semantic(term, self.point, Direction::Left, Side::Left);
},
ViMotion::SemanticRight => {
- buffer_point = semantic(term, buffer_point, Direction::Right, Side::Left);
+ self.point = semantic(term, self.point, Direction::Right, Side::Left);
},
ViMotion::SemanticLeftEnd => {
- buffer_point = semantic(term, buffer_point, Direction::Left, Side::Right);
+ self.point = semantic(term, self.point, Direction::Left, Side::Right);
},
ViMotion::SemanticRightEnd => {
- buffer_point = semantic(term, buffer_point, Direction::Right, Side::Right);
+ self.point = semantic(term, self.point, Direction::Right, Side::Right);
},
ViMotion::WordLeft => {
- buffer_point = word(term, buffer_point, Direction::Left, Side::Left);
+ self.point = word(term, self.point, Direction::Left, Side::Left);
},
ViMotion::WordRight => {
- buffer_point = word(term, buffer_point, Direction::Right, Side::Left);
+ self.point = word(term, self.point, Direction::Right, Side::Left);
},
ViMotion::WordLeftEnd => {
- buffer_point = word(term, buffer_point, Direction::Left, Side::Right);
+ self.point = word(term, self.point, Direction::Left, Side::Right);
},
ViMotion::WordRightEnd => {
- buffer_point = word(term, buffer_point, Direction::Right, Side::Right);
- },
- ViMotion::Bracket => {
- buffer_point = term.bracket_search(buffer_point).unwrap_or(buffer_point);
+ self.point = word(term, self.point, Direction::Right, Side::Right);
},
+ ViMotion::Bracket => self.point = term.bracket_search(self.point).unwrap_or(self.point),
}
- term.scroll_to_point(buffer_point);
- self.point = term.grid().clamp_buffer_to_visible(buffer_point);
+ term.scroll_to_point(self.point);
self
}
/// Get target cursor point for vim-like page movement.
#[must_use = "this returns the result of the operation, without modifying the original"]
- pub fn scroll<T: EventListener>(mut self, term: &Term<T>, lines: isize) -> Self {
+ pub fn scroll<T: EventListener>(mut self, term: &Term<T>, lines: i32) -> Self {
// Check number of lines the cursor needs to be moved.
let overscroll = if lines > 0 {
let max_scroll = term.history_size() - term.grid().display_offset();
- max(0, lines - max_scroll as isize)
+ max(0, lines - max_scroll as i32)
} else {
let max_scroll = term.grid().display_offset();
- min(0, lines + max_scroll as isize)
+ min(0, lines + max_scroll as i32)
};
// Clamp movement to within visible region.
- let mut line = self.point.line.0 as isize;
- line -= overscroll;
- line = max(0, min(term.screen_lines().0 as isize - 1, line));
+ let line = (self.point.line - overscroll).grid_clamp(term, Boundary::Cursor);
// Find the first occupied cell after scrolling has been performed.
- let buffer_point = term.visible_to_buffer(self.point);
- let mut target_line = buffer_point.line as isize + lines;
- target_line = max(0, min(term.total_lines() as isize - 1, target_line));
- let col = first_occupied_in_line(term, target_line as usize).unwrap_or_default().column;
+ let target_line = (self.point.line - lines).grid_clamp(term, Boundary::Grid);
+ let column = first_occupied_in_line(term, target_line).unwrap_or_default().column;
// Move cursor.
- self.point = Point::new(Line(line as usize), col);
+ self.point = Point::new(line, column);
self
}
}
/// Find next end of line to move to.
-fn last<T>(term: &Term<T>, mut point: Point<usize>) -> Point<usize> {
- let cols = term.cols();
-
+fn last<T>(term: &Term<T>, mut point: Point) -> Point {
// Expand across wide cells.
point = term.expand_wide(point, Direction::Right);
@@ -205,35 +196,35 @@ fn last<T>(term: &Term<T>, mut point: Point<usize>) -> Point<usize> {
occupied
} else if is_wrap(term, point) {
// Jump to last occupied cell across linewraps.
- while point.line > 0 && is_wrap(term, point) {
- point.line -= 1;
+ while is_wrap(term, point) {
+ point.line += 1;
}
last_occupied_in_line(term, point.line).unwrap_or(point)
} else {
// Jump to last column when beyond the last occupied cell.
- Point::new(point.line, cols - 1)
+ Point::new(point.line, term.last_column())
}
}
/// Find next non-empty cell to move to.
-fn first_occupied<T>(term: &Term<T>, mut point: Point<usize>) -> Point<usize> {
- let cols = term.cols();
+fn first_occupied<T>(term: &Term<T>, mut point: Point) -> Point {
+ let last_column = term.last_column();
// Expand left across wide chars, since we're searching lines left to right.
point = term.expand_wide(point, Direction::Left);
// Find first non-empty cell in current line.
let occupied = first_occupied_in_line(term, point.line)
- .unwrap_or_else(|| Point::new(point.line, cols - 1));
+ .unwrap_or_else(|| Point::new(point.line, last_column));
// Jump across wrapped lines if we're already at this line's first occupied cell.
if point == occupied {
let mut occupied = None;
// Search for non-empty cell in previous lines.
- for line in (point.line + 1)..term.total_lines() {
- if !is_wrap(term, Point::new(line, cols - 1)) {
+ for line in (term.topmost_line().0..point.line.0).rev().map(Line::from) {
+ if !is_wrap(term, Point::new(line, last_column)) {
break;
}
@@ -247,12 +238,12 @@ fn first_occupied<T>(term: &Term<T>, mut point: Point<usize>) -> Point<usize> {
break occupied;
}
- let last_cell = Point::new(line, cols - 1);
- if line == 0 || !is_wrap(term, last_cell) {
+ let last_cell = Point::new(line, last_column);
+ if !is_wrap(term, last_cell) {
break last_cell;
}
- line -= 1;
+ line += 1;
})
} else {
occupied
@@ -262,14 +253,14 @@ fn first_occupied<T>(term: &Term<T>, mut point: Point<usize>) -> Point<usize> {
/// Move by semantically separated word, like w/b/e/ge in vi.
fn semantic<T: EventListener>(
term: &mut Term<T>,
- mut point: Point<usize>,
+ mut point: Point,
direction: Direction,
side: Side,
-) -> Point<usize> {
+) -> Point {
// Expand semantically based on movement direction.
- let expand_semantic = |point: Point<usize>| {
+ let expand_semantic = |point: Point| {
// Do not expand when currently on a semantic escape char.
- let cell = &term.grid()[point.line][point.column];
+ let cell = &term.grid()[point];
if term.semantic_escape_chars().contains(cell.c)
&& !cell.flags.intersects(Flags::WIDE_CHAR_SPACER | Flags::LEADING_WIDE_CHAR_SPACER)
{
@@ -312,10 +303,10 @@ fn semantic<T: EventListener>(
/// Move by whitespace separated word, like W/B/E/gE in vi.
fn word<T: EventListener>(
term: &mut Term<T>,
- mut point: Point<usize>,
+ mut point: Point,
direction: Direction,
side: Side,
-) -> Point<usize> {
+) -> Point {
// Make sure we jump above wide chars.
point = term.expand_wide(point, direction);
@@ -351,45 +342,46 @@ fn word<T: EventListener>(
}
/// Find first non-empty cell in line.
-fn first_occupied_in_line<T>(term: &Term<T>, line: usize) -> Option<Point<usize>> {
- (0..term.cols().0)
+fn first_occupied_in_line<T>(term: &Term<T>, line: Line) -> Option<Point> {
+ (0..term.columns())
.map(|col| Point::new(line, Column(col)))
.find(|&point| !is_space(term, point))
}
/// Find last non-empty cell in line.
-fn last_occupied_in_line<T>(term: &Term<T>, line: usize) -> Option<Point<usize>> {
- (0..term.cols().0)
+fn last_occupied_in_line<T>(term: &Term<T>, line: Line) -> Option<Point> {
+ (0..term.columns())
.map(|col| Point::new(line, Column(col)))
.rfind(|&point| !is_space(term, point))
}
/// Advance point based on direction.
-fn advance<T>(term: &Term<T>, point: Point<usize>, direction: Direction) -> Point<usize> {
+fn advance<T>(term: &Term<T>, point: Point, direction: Direction) -> Point {
if direction == Direction::Left {
- point.sub_absolute(term, Boundary::Clamp, 1)
+ point.sub(term, Boundary::Grid, 1)
} else {
- point.add_absolute(term, Boundary::Clamp, 1)
+ point.add(term, Boundary::Grid, 1)
}
}
/// Check if cell at point contains whitespace.
-fn is_space<T>(term: &Term<T>, point: Point<usize>) -> bool {
+fn is_space<T>(term: &Term<T>, point: Point) -> bool {
let cell = &term.grid()[point.line][point.column];
!cell.flags().intersects(Flags::WIDE_CHAR_SPACER | Flags::LEADING_WIDE_CHAR_SPACER)
&& (cell.c == ' ' || cell.c == '\t')
}
-fn is_wrap<T>(term: &Term<T>, point: Point<usize>) -> bool {
- point.line != 0 && term.grid()[point.line][point.column].flags.contains(Flags::WRAPLINE)
+/// Check if the cell at a point contains the WRAPLINE flag.
+fn is_wrap<T>(term: &Term<T>, point: Point) -> bool {
+ term.grid()[point].flags.contains(Flags::WRAPLINE)
}
/// Check if point is at screen boundary.
-fn is_boundary<T>(term: &Term<T>, point: Point<usize>, direction: Direction) -> bool {
- let total_lines = term.total_lines();
- let num_cols = term.cols();
- (point.line + 1 >= total_lines && point.column.0 == 0 && direction == Direction::Left)
- || (point.line == 0 && point.column + 1 >= num_cols && direction == Direction::Right)
+fn is_boundary<T>(term: &Term<T>, point: Point, direction: Direction) -> bool {
+ (point.line <= term.topmost_line() && point.column == 0 && direction == Direction::Left)
+ || (point.line == term.bottommost_line()
+ && point.column + 1 >= term.columns()
+ && direction == Direction::Right)
}
#[cfg(test)]
@@ -647,12 +639,12 @@ mod tests {
#[test]
fn scroll_semantic() {
let mut term = term();
- term.grid_mut().scroll_up(&(Line(0)..Line(20)), Line(5));
+ term.grid_mut().scroll_up(&(Line(0)..Line(20)), 5);
let mut cursor = ViModeCursor::new(Point::new(Line(0), Column(0)));
cursor = cursor.motion(&mut term, ViMotion::SemanticLeft);
- assert_eq!(cursor.point, Point::new(Line(0), Column(0)));
+ assert_eq!(cursor.point, Point::new(Line(-5), Column(0)));
assert_eq!(term.grid().display_offset(), 5);
cursor = cursor.motion(&mut term, ViMotion::SemanticRight);
@@ -660,7 +652,7 @@ mod tests {
assert_eq!(term.grid().display_offset(), 0);
cursor = cursor.motion(&mut term, ViMotion::SemanticLeftEnd);
- assert_eq!(cursor.point, Point::new(Line(0), Column(0)));
+ assert_eq!(cursor.point, Point::new(Line(-5), Column(0)));
assert_eq!(term.grid().display_offset(), 5);
cursor = cursor.motion(&mut term, ViMotion::SemanticRightEnd);
@@ -723,12 +715,12 @@ mod tests {
#[test]
fn scroll_word() {
let mut term = term();
- term.grid_mut().scroll_up(&(Line(0)..Line(20)), Line(5));
+ term.grid_mut().scroll_up(&(Line(0)..Line(20)), 5);
let mut cursor = ViModeCursor::new(Point::new(Line(0), Column(0)));
cursor = cursor.motion(&mut term, ViMotion::WordLeft);
- assert_eq!(cursor.point, Point::new(Line(0), Column(0)));
+ assert_eq!(cursor.point, Point::new(Line(-5), Column(0)));
assert_eq!(term.grid().display_offset(), 5);
cursor = cursor.motion(&mut term, ViMotion::WordRight);
@@ -736,7 +728,7 @@ mod tests {
assert_eq!(term.grid().display_offset(), 0);
cursor = cursor.motion(&mut term, ViMotion::WordLeftEnd);
- assert_eq!(cursor.point, Point::new(Line(0), Column(0)));
+ assert_eq!(cursor.point, Point::new(Line(-5), Column(0)));
assert_eq!(term.grid().display_offset(), 5);
cursor = cursor.motion(&mut term, ViMotion::WordRightEnd);