aboutsummaryrefslogtreecommitdiff
path: root/alacritty_terminal
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2020-05-30 20:45:44 +0000
committerGitHub <noreply@github.com>2020-05-30 20:45:44 +0000
commit1dacc99183373bffa3ba287aa3241f3b1da67016 (patch)
treed5dbefde927b02bff10e29d8596a0bfab65d88f1 /alacritty_terminal
parentf7fb67f870943f3f760826b748c8463b8e434983 (diff)
downloadalacritty-1dacc99183373bffa3ba287aa3241f3b1da67016.tar.gz
alacritty-1dacc99183373bffa3ba287aa3241f3b1da67016.zip
Refactor Term/Grid separation
This commit aims to clear up the separation between Term and Grid to make way for implementing search. The `cursor` and `cursor_save` have been moved to the grid, since they're always bound to their specific grid and this makes updating easier. Since the selection is independent of the active grid, it has been moved to the `Term`.
Diffstat (limited to 'alacritty_terminal')
-rw-r--r--alacritty_terminal/src/ansi.rs45
-rw-r--r--alacritty_terminal/src/grid/mod.rs377
-rw-r--r--alacritty_terminal/src/grid/resize.rs274
-rw-r--r--alacritty_terminal/src/grid/row.rs12
-rw-r--r--alacritty_terminal/src/grid/storage.rs225
-rw-r--r--alacritty_terminal/src/grid/tests.rs22
-rw-r--r--alacritty_terminal/src/index.rs135
-rw-r--r--alacritty_terminal/src/selection.rs100
-rw-r--r--alacritty_terminal/src/term/cell.rs4
-rw-r--r--alacritty_terminal/src/term/mod.rs509
-rw-r--r--alacritty_terminal/src/vi_mode.rs9
-rw-r--r--alacritty_terminal/tests/ref.rs2
12 files changed, 912 insertions, 802 deletions
diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs
index 01f48358..0b196ad5 100644
--- a/alacritty_terminal/src/ansi.rs
+++ b/alacritty_terminal/src/ansi.rs
@@ -697,6 +697,51 @@ impl Default for StandardCharset {
}
}
+impl StandardCharset {
+ /// Switch/Map character to the active charset. Ascii is the common case and
+ /// for that we want to do as little as possible.
+ #[inline]
+ pub fn map(self, c: char) -> char {
+ match self {
+ StandardCharset::Ascii => c,
+ StandardCharset::SpecialCharacterAndLineDrawing => match c {
+ '`' => '◆',
+ 'a' => '▒',
+ 'b' => '\t',
+ 'c' => '\u{000c}',
+ 'd' => '\r',
+ 'e' => '\n',
+ 'f' => '°',
+ 'g' => '±',
+ 'h' => '\u{2424}',
+ 'i' => '\u{000b}',
+ 'j' => '┘',
+ 'k' => '┐',
+ 'l' => '┌',
+ 'm' => '└',
+ 'n' => '┼',
+ 'o' => '⎺',
+ 'p' => '⎻',
+ 'q' => '─',
+ 'r' => '⎼',
+ 's' => '⎽',
+ 't' => '├',
+ 'u' => '┤',
+ 'v' => '┴',
+ 'w' => '┬',
+ 'x' => '│',
+ 'y' => '≤',
+ 'z' => '≥',
+ '{' => 'π',
+ '|' => '≠',
+ '}' => '£',
+ '~' => '·',
+ _ => c,
+ },
+ }
+ }
+}
+
impl<'a, H, W> vte::Perform for Performer<'a, H, W>
where
H: Handler + TermInfo + 'a,
diff --git a/alacritty_terminal/src/grid/mod.rs b/alacritty_terminal/src/grid/mod.rs
index 71545dca..a136b1b5 100644
--- a/alacritty_terminal/src/grid/mod.rs
+++ b/alacritty_terminal/src/grid/mod.rs
@@ -14,22 +14,22 @@
//! A specialized 2D grid implementation optimized for use in a terminal.
-use std::cmp::{max, min, Ordering};
+use std::cmp::{max, min};
use std::ops::{Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeTo};
use serde::{Deserialize, Serialize};
+use crate::ansi::{CharsetIndex, StandardCharset};
use crate::index::{Column, IndexRange, Line, Point};
-use crate::selection::Selection;
-use crate::term::cell::Flags;
+use crate::term::cell::{Cell, Flags};
+pub mod resize;
mod row;
-pub use self::row::Row;
-
+mod storage;
#[cfg(test)]
mod tests;
-mod storage;
+pub use self::row::Row;
use self::storage::Storage;
/// Bidirectional iterator.
@@ -60,7 +60,6 @@ impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> {
&& self.cols.eq(&other.cols)
&& self.lines.eq(&other.lines)
&& self.display_offset.eq(&other.display_offset)
- && self.selection.eq(&other.selection)
}
}
@@ -76,7 +75,36 @@ pub trait GridCell {
fn fast_eq(&self, other: Self) -> bool;
}
-/// Represents the terminal display contents.
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
+pub struct Cursor {
+ /// The location of this cursor.
+ pub point: Point,
+
+ /// Template cell when using this cursor.
+ pub template: Cell,
+
+ /// Currently configured graphic character sets.
+ pub charsets: Charsets,
+}
+
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
+pub struct Charsets([StandardCharset; 4]);
+
+impl Index<CharsetIndex> for Charsets {
+ type Output = StandardCharset;
+
+ fn index(&self, index: CharsetIndex) -> &StandardCharset {
+ &self.0[index as usize]
+ }
+}
+
+impl IndexMut<CharsetIndex> for Charsets {
+ fn index_mut(&mut self, index: CharsetIndex) -> &mut StandardCharset {
+ &mut self.0[index as usize]
+ }
+}
+
+/// Grid based terminal content storage.
///
/// ```notrust
/// ┌─────────────────────────┐ <-- max_scroll_limit + lines
@@ -105,6 +133,14 @@ pub trait GridCell {
/// ```
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Grid<T> {
+ /// Current cursor for writing data.
+ #[serde(skip)]
+ pub cursor: Cursor,
+
+ /// Last saved cursor.
+ #[serde(skip)]
+ pub saved_cursor: Cursor,
+
/// Lines in the grid. Each row holds a list of cells corresponding to the
/// columns in that row.
raw: Storage<T>,
@@ -122,10 +158,6 @@ pub struct Grid<T> {
/// updates this offset accordingly.
display_offset: usize,
- /// Selected region.
- #[serde(skip)]
- pub selection: Option<Selection>,
-
/// Maximum number of lines in history.
max_scroll_limit: usize,
}
@@ -139,10 +171,17 @@ pub enum Scroll {
Bottom,
}
-impl<T: GridCell + PartialEq + Copy> Grid<T> {
- pub fn new(lines: Line, cols: Column, scrollback: usize, template: T) -> Grid<T> {
- let raw = Storage::with_capacity(lines, Row::new(cols, &template));
- Grid { raw, cols, lines, display_offset: 0, selection: None, max_scroll_limit: scrollback }
+impl<T: GridCell + Default + PartialEq + Copy> Grid<T> {
+ pub fn new(lines: Line, cols: Column, max_scroll_limit: usize, template: T) -> Grid<T> {
+ Grid {
+ raw: Storage::with_capacity(lines, Row::new(cols, template)),
+ max_scroll_limit,
+ display_offset: 0,
+ saved_cursor: Cursor::default(),
+ cursor: Cursor::default(),
+ lines,
+ cols,
+ }
}
/// Clamp a buffer point to the visible region.
@@ -191,33 +230,7 @@ impl<T: GridCell + PartialEq + Copy> Grid<T> {
}
}
- pub fn resize(
- &mut self,
- reflow: bool,
- lines: Line,
- cols: Column,
- cursor_pos: &mut Point,
- template: &T,
- ) {
- // Check that there's actually work to do and return early if not.
- if lines == self.lines && cols == self.cols {
- return;
- }
-
- match self.lines.cmp(&lines) {
- Ordering::Less => self.grow_lines(lines, cursor_pos, template),
- Ordering::Greater => self.shrink_lines(lines, cursor_pos, template),
- Ordering::Equal => (),
- }
-
- match self.cols.cmp(&cols) {
- Ordering::Less => self.grow_cols(reflow, cols, cursor_pos, template),
- Ordering::Greater => self.shrink_cols(reflow, cols, template),
- Ordering::Equal => (),
- }
- }
-
- fn increase_scroll_limit(&mut self, count: usize, template: &T) {
+ fn increase_scroll_limit(&mut self, count: usize, template: T) {
let count = min(count, self.max_scroll_limit - self.history_size());
if count != 0 {
self.raw.initialize(count, template, self.cols);
@@ -228,238 +241,12 @@ impl<T: GridCell + PartialEq + Copy> Grid<T> {
let count = min(count, self.history_size());
if count != 0 {
self.raw.shrink_lines(min(count, self.history_size()));
+ self.display_offset = min(self.display_offset, self.history_size());
}
}
- /// Add lines to the visible area.
- ///
- /// Alacritty keeps the cursor at the bottom of the terminal as long as there
- /// is scrollback available. Once scrollback is exhausted, new lines are
- /// simply added to the bottom of the screen.
- fn grow_lines(&mut self, new_line_count: Line, cursor_pos: &mut Point, template: &T) {
- let lines_added = new_line_count - self.lines;
-
- // Need to "resize" before updating buffer.
- self.raw.grow_visible_lines(new_line_count, Row::new(self.cols, template));
- self.lines = new_line_count;
-
- let history_size = self.history_size();
- let from_history = min(history_size, lines_added.0);
-
- // Move cursor down for all lines pulled from history.
- cursor_pos.line += from_history;
-
- if from_history != lines_added.0 {
- // Move existing lines up for every line that couldn't be pulled from history.
- self.scroll_up(&(Line(0)..new_line_count), lines_added - from_history, template);
- }
-
- self.decrease_scroll_limit(*lines_added);
- self.display_offset = self.display_offset.saturating_sub(*lines_added);
- }
-
- /// Grow number of columns in each row, reflowing if necessary.
- fn grow_cols(&mut self, reflow: bool, cols: Column, cursor_pos: &mut Point, template: &T) {
- // Check if a row needs to be wrapped.
- let should_reflow = |row: &Row<T>| -> bool {
- let len = Column(row.len());
- reflow && len < cols && row[len - 1].flags().contains(Flags::WRAPLINE)
- };
-
- let mut new_empty_lines = 0;
- let mut reversed: Vec<Row<T>> = Vec::with_capacity(self.raw.len());
- for (i, mut row) in self.raw.drain().enumerate().rev() {
- // Check if reflowing should be performed.
- let last_row = match reversed.last_mut() {
- Some(last_row) if should_reflow(last_row) => last_row,
- _ => {
- reversed.push(row);
- continue;
- },
- };
-
- // Remove wrap flag before appending additional cells.
- if let Some(cell) = last_row.last_mut() {
- cell.flags_mut().remove(Flags::WRAPLINE);
- }
-
- // Remove leading spacers when reflowing wide char to the previous line.
- let last_len = last_row.len();
- if last_len >= 2
- && !last_row[Column(last_len - 2)].flags().contains(Flags::WIDE_CHAR)
- && last_row[Column(last_len - 1)].flags().contains(Flags::WIDE_CHAR_SPACER)
- {
- last_row.shrink(Column(last_len - 1));
- }
-
- // Append as many cells from the next line as possible.
- let len = min(row.len(), cols.0 - last_row.len());
-
- // Insert leading spacer when there's not enough room for reflowing wide char.
- let mut cells = if row[Column(len - 1)].flags().contains(Flags::WIDE_CHAR) {
- let mut cells = row.front_split_off(len - 1);
-
- let mut spacer = *template;
- spacer.flags_mut().insert(Flags::WIDE_CHAR_SPACER);
- cells.push(spacer);
-
- cells
- } else {
- row.front_split_off(len)
- };
-
- last_row.append(&mut cells);
-
- if row.is_empty() {
- if i + reversed.len() < self.lines.0 {
- // Add new line and move lines up if we can't pull from history.
- cursor_pos.line = Line(cursor_pos.line.saturating_sub(1));
- new_empty_lines += 1;
- } else if i < self.display_offset {
- // Keep viewport in place if line is outside of the visible area.
- self.display_offset = self.display_offset.saturating_sub(1);
- }
-
- // Don't push line into the new buffer.
- continue;
- } else if let Some(cell) = last_row.last_mut() {
- // Set wrap flag if next line still has cells.
- cell.flags_mut().insert(Flags::WRAPLINE);
- }
-
- reversed.push(row);
- }
-
- // Add padding lines.
- reversed.append(&mut vec![Row::new(cols, template); new_empty_lines]);
-
- // Fill remaining cells and reverse iterator.
- let mut new_raw = Vec::with_capacity(reversed.len());
- for mut row in reversed.drain(..).rev() {
- if row.len() < cols.0 {
- row.grow(cols, template);
- }
- new_raw.push(row);
- }
-
- self.raw.replace_inner(new_raw);
-
- self.display_offset = min(self.display_offset, self.history_size());
- self.cols = cols;
- }
-
- /// Shrink number of columns in each row, reflowing if necessary.
- fn shrink_cols(&mut self, reflow: bool, cols: Column, template: &T) {
- let mut new_raw = Vec::with_capacity(self.raw.len());
- let mut buffered = None;
- for (i, mut row) in self.raw.drain().enumerate().rev() {
- // Append lines left over from previous row.
- if let Some(buffered) = buffered.take() {
- row.append_front(buffered);
- }
-
- loop {
- // Check if reflowing should be performed.
- let mut wrapped = match row.shrink(cols) {
- Some(wrapped) if reflow => wrapped,
- _ => {
- new_raw.push(row);
- break;
- },
- };
-
- // 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) {
- wrapped.insert(0, row[cols - 1]);
-
- let mut spacer = *template;
- spacer.flags_mut().insert(Flags::WIDE_CHAR_SPACER);
- row[cols - 1] = spacer;
- }
-
- // Remove wide char spacer before shrinking.
- let len = wrapped.len();
- if (len == 1 || (len >= 2 && !wrapped[len - 2].flags().contains(Flags::WIDE_CHAR)))
- && wrapped[len - 1].flags().contains(Flags::WIDE_CHAR_SPACER)
- {
- if len == 1 {
- row[cols - 1].flags_mut().insert(Flags::WRAPLINE);
- new_raw.push(row);
- break;
- } else {
- wrapped[len - 2].flags_mut().insert(Flags::WRAPLINE);
- wrapped.truncate(len - 1);
- }
- }
-
- new_raw.push(row);
-
- // Set line as wrapped if cells got removed.
- if let Some(cell) = new_raw.last_mut().and_then(|r| r.last_mut()) {
- cell.flags_mut().insert(Flags::WRAPLINE);
- }
-
- if wrapped
- .last()
- .map(|c| c.flags().contains(Flags::WRAPLINE) && i >= 1)
- .unwrap_or(false)
- && wrapped.len() < cols.0
- {
- // Make sure previous wrap flag doesn't linger around.
- if let Some(cell) = wrapped.last_mut() {
- cell.flags_mut().remove(Flags::WRAPLINE);
- }
-
- // Add removed cells to start of next row.
- buffered = Some(wrapped);
- break;
- } else {
- // Make sure viewport doesn't move if line is outside of the visible area.
- if i < self.display_offset {
- self.display_offset = min(self.display_offset + 1, self.max_scroll_limit);
- }
-
- // Make sure new row is at least as long as new width.
- let occ = wrapped.len();
- if occ < cols.0 {
- wrapped.append(&mut vec![*template; cols.0 - occ]);
- }
- row = Row::from_vec(wrapped, occ);
- }
- }
- }
-
- let mut reversed: Vec<Row<T>> = new_raw.drain(..).rev().collect();
- reversed.truncate(self.max_scroll_limit + self.lines.0);
- self.raw.replace_inner(reversed);
- self.cols = cols;
- }
-
- /// Remove lines from the visible area.
- ///
- /// The behavior in Terminal.app and iTerm.app is to keep the cursor at the
- /// bottom of the screen. This is achieved by pushing history "out the top"
- /// of the terminal window.
- ///
- /// Alacritty takes the same approach.
- fn shrink_lines(&mut self, target: Line, cursor_pos: &mut Point, template: &T) {
- // Scroll up to keep cursor inside the window.
- let required_scrolling = (cursor_pos.line + 1).saturating_sub(target.0);
- if required_scrolling > 0 {
- self.scroll_up(&(Line(0)..self.lines), Line(required_scrolling), template);
- }
-
- self.selection = None;
- self.raw.rotate((self.lines - target).0 as isize);
- self.raw.shrink_visible_lines(target);
- self.lines = target;
- }
-
#[inline]
- pub fn scroll_down(&mut self, region: &Range<Line>, positions: Line, template: &T) {
- let num_lines = self.num_lines().0;
- let num_cols = self.num_cols().0;
-
+ pub fn scroll_down(&mut self, region: &Range<Line>, positions: Line, template: T) {
// Whether or not there is a scrolling region active, as long as it
// starts at the top, we can do a full rotation which just involves
// changing the start index.
@@ -469,10 +256,6 @@ impl<T: GridCell + PartialEq + Copy> Grid<T> {
// Rotate the entire line buffer. If there's a scrolling region
// active, the bottom lines are restored in the next step.
self.raw.rotate_up(*positions);
- self.selection = self
- .selection
- .take()
- .and_then(|s| s.rotate(num_lines, num_cols, region, -(*positions as isize)));
self.decrease_scroll_limit(*positions);
@@ -484,22 +267,16 @@ impl<T: GridCell + PartialEq + Copy> Grid<T> {
// Finally, reset recycled lines.
for i in IndexRange(Line(0)..positions) {
- self.raw[i].reset(&template);
+ self.raw[i].reset(template);
}
} else {
- // Rotate selection to track content.
- self.selection = self
- .selection
- .take()
- .and_then(|s| s.rotate(num_lines, num_cols, region, -(*positions as isize)));
-
// Subregion rotation.
for line in IndexRange((region.start + positions)..region.end).rev() {
self.raw.swap_lines(line, line - positions);
}
for line in IndexRange(region.start..(region.start + positions)) {
- self.raw[line].reset(&template);
+ self.raw[line].reset(template);
}
}
}
@@ -507,9 +284,8 @@ impl<T: GridCell + PartialEq + Copy> Grid<T> {
/// Move lines at the bottom towards the top.
///
/// This is the performance-sensitive part of scrolling.
- pub fn scroll_up(&mut self, region: &Range<Line>, positions: Line, template: &T) {
+ pub fn scroll_up(&mut self, region: &Range<Line>, positions: Line, template: T) {
let num_lines = self.num_lines().0;
- let num_cols = self.num_cols().0;
if region.start == Line(0) {
// Update display offset when not pinned to active area.
@@ -522,10 +298,6 @@ impl<T: GridCell + PartialEq + Copy> Grid<T> {
// Rotate the entire line buffer. If there's a scrolling region
// active, the bottom lines are restored in the next step.
self.raw.rotate(-(*positions as isize));
- self.selection = self
- .selection
- .take()
- .and_then(|s| s.rotate(num_lines, num_cols, region, *positions as isize));
// This next loop swaps "fixed" lines outside of a scroll region
// back into place after the rotation. The work is done in buffer-
@@ -541,15 +313,9 @@ impl<T: GridCell + PartialEq + Copy> Grid<T> {
//
// Recycled lines are just above the end of the scrolling region.
for i in 0..*positions {
- self.raw[i + fixed_lines].reset(&template);
+ self.raw[i + fixed_lines].reset(template);
}
} else {
- // Rotate selection to track content.
- self.selection = self
- .selection
- .take()
- .and_then(|s| s.rotate(num_lines, num_cols, region, *positions as isize));
-
// Subregion rotation.
for line in IndexRange(region.start..(region.end - positions)) {
self.raw.swap_lines(line, line + positions);
@@ -557,12 +323,12 @@ impl<T: GridCell + PartialEq + Copy> Grid<T> {
// Clear reused lines.
for line in IndexRange((region.end - positions)..region.end) {
- self.raw[line].reset(&template);
+ self.raw[line].reset(template);
}
}
}
- pub fn clear_viewport(&mut self, template: &T) {
+ pub fn clear_viewport(&mut self, template: T) {
// Determine how many lines to scroll up by.
let end = Point { line: 0, col: self.num_cols() };
let mut iter = self.iter_from(end);
@@ -583,12 +349,12 @@ impl<T: GridCell + PartialEq + Copy> Grid<T> {
// Reset rotated lines.
for i in positions.0..self.lines.0 {
- self.raw[i].reset(&template);
+ self.raw[i].reset(template);
}
}
/// Completely reset the grid state.
- pub fn reset(&mut self, template: &T) {
+ pub fn reset(&mut self, template: T) {
self.clear_history();
// Reset all visible lines.
@@ -596,8 +362,9 @@ impl<T: GridCell + PartialEq + Copy> Grid<T> {
self.raw[row].reset(template);
}
+ self.saved_cursor = Cursor::default();
+ self.cursor = Cursor::default();
self.display_offset = 0;
- self.selection = None;
}
}
@@ -637,7 +404,7 @@ impl<T> Grid<T> {
/// This is used only for initializing after loading ref-tests.
#[inline]
- pub fn initialize_all(&mut self, template: &T)
+ pub fn initialize_all(&mut self, template: T)
where
T: Copy + GridCell,
{
@@ -663,6 +430,12 @@ impl<T> Grid<T> {
pub fn display_offset(&self) -> usize {
self.display_offset
}
+
+ #[inline]
+ pub fn cursor_cell(&mut self) -> &mut T {
+ let point = self.cursor.point;
+ &mut self[&point]
+ }
}
pub struct GridIterator<'a, T> {
diff --git a/alacritty_terminal/src/grid/resize.rs b/alacritty_terminal/src/grid/resize.rs
new file mode 100644
index 00000000..796c5859
--- /dev/null
+++ b/alacritty_terminal/src/grid/resize.rs
@@ -0,0 +1,274 @@
+//! Grid resize and reflow.
+
+use std::cmp::{min, Ordering};
+
+use crate::index::{Column, Line};
+use crate::term::cell::Flags;
+
+use crate::grid::row::Row;
+use crate::grid::{Grid, GridCell};
+
+impl<T: GridCell + Default + PartialEq + Copy> Grid<T> {
+ /// Resize the grid's width and/or height.
+ pub fn resize(&mut self, reflow: bool, lines: Line, cols: Column) {
+ match self.lines.cmp(&lines) {
+ Ordering::Less => self.grow_lines(lines),
+ Ordering::Greater => self.shrink_lines(lines),
+ Ordering::Equal => (),
+ }
+
+ match self.cols.cmp(&cols) {
+ Ordering::Less => self.grow_cols(cols, reflow),
+ Ordering::Greater => self.shrink_cols(cols, reflow),
+ Ordering::Equal => (),
+ }
+ }
+
+ /// Add lines to the visible area.
+ ///
+ /// Alacritty keeps the cursor at the bottom of the terminal as long as there
+ /// is scrollback available. Once scrollback is exhausted, new lines are
+ /// simply added to the bottom of the screen.
+ fn grow_lines(&mut self, new_line_count: Line) {
+ let lines_added = new_line_count - self.lines;
+
+ // Need to resize before updating buffer.
+ self.raw.grow_visible_lines(new_line_count, Row::new(self.cols, T::default()));
+ self.lines = new_line_count;
+
+ let history_size = self.history_size();
+ let from_history = min(history_size, lines_added.0);
+
+ // Move existing lines up for every line that couldn't be pulled from history.
+ if from_history != lines_added.0 {
+ let delta = lines_added - from_history;
+ self.scroll_up(&(Line(0)..new_line_count), delta, T::default());
+ }
+
+ // 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);
+ }
+
+ /// Remove lines from the visible area.
+ ///
+ /// The behavior in Terminal.app and iTerm.app is to keep the cursor at the
+ /// bottom of the screen. This is achieved by pushing history "out the top"
+ /// of the terminal window.
+ ///
+ /// Alacritty takes the same approach.
+ fn shrink_lines(&mut self, target: Line) {
+ // Scroll up to keep content inside the window.
+ let required_scrolling = (self.cursor.point.line + 1).saturating_sub(target.0);
+ if required_scrolling > 0 {
+ self.scroll_up(&(Line(0)..self.lines), Line(required_scrolling), T::default());
+
+ // Clamp cursors to the new viewport size.
+ self.saved_cursor.point.line = min(self.saved_cursor.point.line, target - 1);
+ self.cursor.point.line = min(self.cursor.point.line, target - 1);
+ }
+
+ self.raw.rotate((self.lines - target).0 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, cols: Column, reflow: bool) {
+ // Check if a row needs to be wrapped.
+ let should_reflow = |row: &Row<T>| -> bool {
+ let len = Column(row.len());
+ reflow && len < cols && row[len - 1].flags().contains(Flags::WRAPLINE)
+ };
+
+ self.cols = cols;
+
+ let mut reversed: Vec<Row<T>> = Vec::with_capacity(self.raw.len());
+ let mut new_empty_lines = 0;
+
+ let mut rows = self.raw.take_all();
+
+ for (i, mut row) in rows.drain(..).enumerate().rev() {
+ // Check if reflowing should be performed.
+ let last_row = match reversed.last_mut() {
+ Some(last_row) if should_reflow(last_row) => last_row,
+ _ => {
+ reversed.push(row);
+ continue;
+ },
+ };
+
+ // Remove wrap flag before appending additional cells.
+ if let Some(cell) = last_row.last_mut() {
+ cell.flags_mut().remove(Flags::WRAPLINE);
+ }
+
+ // Remove leading spacers when reflowing wide char to the previous line.
+ let mut last_len = last_row.len();
+ if last_len >= 2
+ && !last_row[Column(last_len - 2)].flags().contains(Flags::WIDE_CHAR)
+ && last_row[Column(last_len - 1)].flags().contains(Flags::WIDE_CHAR_SPACER)
+ {
+ last_row.shrink(Column(last_len - 1));
+ last_len -= 1;
+ }
+
+ // Don't try to pull more cells from the next line than available.
+ let len = min(row.len(), cols.0 - last_len);
+
+ // Insert leading spacer when there's not enough room for reflowing wide char.
+ let mut cells = if row[Column(len - 1)].flags().contains(Flags::WIDE_CHAR) {
+ let mut cells = row.front_split_off(len - 1);
+
+ let mut spacer = T::default();
+ spacer.flags_mut().insert(Flags::WIDE_CHAR_SPACER);
+ cells.push(spacer);
+
+ cells
+ } else {
+ row.front_split_off(len)
+ };
+
+ // Reflow cells to previous row.
+ last_row.append(&mut cells);
+
+ if row.is_empty() {
+ if i + reversed.len() < self.lines.0 {
+ // Add new line and move everything up if we can't pull from history.
+ self.saved_cursor.point.line.0 = self.saved_cursor.point.line.saturating_sub(1);
+ self.cursor.point.line.0 = self.cursor.point.line.saturating_sub(1);
+ new_empty_lines += 1;
+ } else {
+ // Since we removed a line, rotate down the viewport.
+ self.display_offset = self.display_offset.saturating_sub(1);
+ }
+
+ // Don't push line into the new buffer.
+ continue;
+ } else if let Some(cell) = last_row.last_mut() {
+ // Set wrap flag if next line still has cells.
+ cell.flags_mut().insert(Flags::WRAPLINE);
+ }
+
+ reversed.push(row);
+ }
+
+ // Add all new empty lines in one go.
+ reversed.append(&mut vec![Row::new(cols, T::default()); new_empty_lines]);
+
+ // 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, T::default());
+ }
+ new_raw.push(row);
+ }
+
+ self.raw.replace_inner(new_raw);
+
+ // Clamp display offset in case lines above it got merged.
+ self.display_offset = min(self.display_offset, self.history_size());
+ }
+
+ /// Shrink number of columns in each row, reflowing if necessary.
+ fn shrink_cols(&mut self, cols: Column, reflow: bool) {
+ self.cols = cols;
+
+ let mut rows = self.raw.take_all();
+
+ let mut new_raw = Vec::with_capacity(self.raw.len());
+ let mut buffered: Option<Vec<T>> = None;
+
+ for (i, mut row) in rows.drain(..).enumerate().rev() {
+ // Append lines left over from the previous row.
+ if let Some(buffered) = buffered.take() {
+ row.append_front(buffered);
+ }
+
+ loop {
+ // Remove all cells which require reflowing.
+ let mut wrapped = match row.shrink(cols) {
+ Some(wrapped) if reflow => wrapped,
+ _ => {
+ new_raw.push(row);
+ break;
+ },
+ };
+
+ // 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) {
+ wrapped.insert(0, row[cols - 1]);
+
+ let mut spacer = T::default();
+ spacer.flags_mut().insert(Flags::WIDE_CHAR_SPACER);
+ row[cols - 1] = spacer;
+ }
+
+ // Remove wide char spacer before shrinking.
+ let len = wrapped.len();
+ if (len == 1 || (len >= 2 && !wrapped[len - 2].flags().contains(Flags::WIDE_CHAR)))
+ && wrapped[len - 1].flags().contains(Flags::WIDE_CHAR_SPACER)
+ {
+ if len == 1 {
+ // Delete the wrapped content if it contains only a leading spacer.
+ row[cols - 1].flags_mut().insert(Flags::WRAPLINE);
+ new_raw.push(row);
+ break;
+ } else {
+ // Remove the leading spacer from the end of the wrapped row.
+ wrapped[len - 2].flags_mut().insert(Flags::WRAPLINE);
+ wrapped.truncate(len - 1);
+ }
+ }
+
+ new_raw.push(row);
+
+ // Set line as wrapped if cells got removed.
+ if let Some(cell) = new_raw.last_mut().and_then(|r| r.last_mut()) {
+ cell.flags_mut().insert(Flags::WRAPLINE);
+ }
+
+ if wrapped
+ .last()
+ .map(|c| c.flags().contains(Flags::WRAPLINE) && i >= 1)
+ .unwrap_or(false)
+ && wrapped.len() < cols.0
+ {
+ // Make sure previous wrap flag doesn't linger around.
+ if let Some(cell) = wrapped.last_mut() {
+ cell.flags_mut().remove(Flags::WRAPLINE);
+ }
+
+ // Add removed cells to start of next row.
+ buffered = Some(wrapped);
+ break;
+ } else {
+ // Since we added a line, rotate up the viewport.
+ if i < self.display_offset {
+ self.display_offset = min(self.display_offset + 1, self.max_scroll_limit);
+ }
+
+ // Make sure new row is at least as long as new width.
+ let occ = wrapped.len();
+ if occ < cols.0 {
+ wrapped.append(&mut vec![T::default(); cols.0 - occ]);
+ }
+ row = Row::from_vec(wrapped, occ);
+ }
+ }
+ }
+
+ // 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);
+ self.raw.replace_inner(reversed);
+
+ // Wrap content going beyond new width if necessary.
+ self.saved_cursor.point.col = min(self.saved_cursor.point.col, self.cols - 1);
+ self.cursor.point.col = min(self.cursor.point.col, self.cols - 1);
+ }
+}
diff --git a/alacritty_terminal/src/grid/row.rs b/alacritty_terminal/src/grid/row.rs
index 22a10625..b5248ad7 100644
--- a/alacritty_terminal/src/grid/row.rs
+++ b/alacritty_terminal/src/grid/row.rs
@@ -43,20 +43,20 @@ impl<T: PartialEq> PartialEq for Row<T> {
}
impl<T: Copy> Row<T> {
- pub fn new(columns: Column, template: &T) -> Row<T>
+ pub fn new(columns: Column, template: T) -> Row<T>
where
T: GridCell,
{
let occ = if template.is_empty() { 0 } else { columns.0 };
- Row { inner: vec![*template; columns.0], occ }
+ Row { inner: vec![template; columns.0], occ }
}
- pub fn grow(&mut self, cols: Column, template: &T) {
+ pub fn grow(&mut self, cols: Column, template: T) {
if self.inner.len() >= cols.0 {
return;
}
- self.inner.append(&mut vec![*template; cols.0 - self.len()]);
+ self.inner.append(&mut vec![template; cols.0 - self.len()]);
}
pub fn shrink(&mut self, cols: Column) -> Option<Vec<T>>
@@ -83,14 +83,12 @@ impl<T: Copy> Row<T> {
/// Reset all cells in the row to the `template` cell.
#[inline]
- pub fn reset(&mut self, template: &T)
+ pub fn reset(&mut self, template: T)
where
T: GridCell + PartialEq,
{
debug_assert!(!self.inner.is_empty());
- let template = *template;
-
// Mark all cells as dirty if template cell changed.
let len = self.inner.len();
if !self.inner[len - 1].fast_eq(template) {
diff --git a/alacritty_terminal/src/grid/storage.rs b/alacritty_terminal/src/grid/storage.rs
index 4820036f..4b7ca41a 100644
--- a/alacritty_terminal/src/grid/storage.rs
+++ b/alacritty_terminal/src/grid/storage.rs
@@ -1,6 +1,6 @@
use std::cmp::{max, PartialEq};
+use std::mem;
use std::ops::{Index, IndexMut};
-use std::vec::Drain;
use serde::{Deserialize, Serialize};
@@ -142,7 +142,7 @@ impl<T> Storage<T> {
/// Dynamically grow the storage buffer at runtime.
#[inline]
- pub fn initialize(&mut self, additional_rows: usize, template: &T, cols: Column)
+ pub fn initialize(&mut self, additional_rows: usize, template: T, cols: Column)
where
T: GridCell + Copy,
{
@@ -238,18 +238,27 @@ impl<T> Storage<T> {
self.zero = (self.zero + count) % self.inner.len();
}
- /// Drain all rows in the grid.
- pub fn drain(&mut self) -> Drain<'_, Row<T>> {
- self.truncate();
- self.inner.drain(..)
- }
-
/// Update the raw storage buffer.
+ #[inline]
pub fn replace_inner(&mut self, vec: Vec<Row<T>>) {
self.len = vec.len();
self.inner = vec;
self.zero = 0;
}
+
+ /// Remove all rows from storage.
+ #[inline]
+ pub fn take_all(&mut self) -> Vec<Row<T>> {
+ self.truncate();
+
+ let mut buffer = Vec::new();
+
+ mem::swap(&mut buffer, &mut self.inner);
+ self.zero = 0;
+ self.len = 0;
+
+ buffer
+ }
}
impl<T> Index<usize> for Storage<T> {
@@ -315,7 +324,7 @@ mod tests {
#[test]
fn with_capacity() {
- let storage = Storage::with_capacity(Line(3), Row::new(Column(0), &' '));
+ let storage = Storage::with_capacity(Line(3), Row::new(Column(0), ' '));
assert_eq!(storage.inner.len(), 3);
assert_eq!(storage.len, 3);
@@ -325,33 +334,33 @@ mod tests {
#[test]
fn indexing() {
- let mut storage = Storage::with_capacity(Line(3), Row::new(Column(0), &' '));
+ let mut storage = Storage::with_capacity(Line(3), Row::new(Column(0), ' '));
- storage[0] = Row::new(Column(1), &'0');
- storage[1] = Row::new(Column(1), &'1');
- storage[2] = Row::new(Column(1), &'2');
+ storage[0] = Row::new(Column(1), '0');
+ storage[1] = Row::new(Column(1), '1');
+ storage[2] = Row::new(Column(1), '2');
- assert_eq!(storage[0], Row::new(Column(1), &'0'));
- assert_eq!(storage[1], Row::new(Column(1), &'1'));
- assert_eq!(storage[2], Row::new(Column(1), &'2'));
+ assert_eq!(storage[0], Row::new(Column(1), '0'));
+ assert_eq!(storage[1], Row::new(Column(1), '1'));
+ assert_eq!(storage[2], Row::new(Column(1), '2'));
storage.zero += 1;
- assert_eq!(storage[0], Row::new(Column(1), &'1'));
- assert_eq!(storage[1], Row::new(Column(1), &'2'));
- assert_eq!(storage[2], Row::new(Column(1), &'0'));
+ assert_eq!(storage[0], Row::new(Column(1), '1'));
+ assert_eq!(storage[1], Row::new(Column(1), '2'));
+ assert_eq!(storage[2], Row::new(Column(1), '0'));
}
#[test]
#[should_panic]
fn indexing_above_inner_len() {
- let storage = Storage::with_capacity(Line(1), Row::new(Column(0), &' '));
+ let storage = Storage::with_capacity(Line(1), Row::new(Column(0), ' '));
let _ = &storage[2];
}
#[test]
fn rotate() {
- let mut storage = Storage::with_capacity(Line(3), Row::new(Column(0), &' '));
+ let mut storage = Storage::with_capacity(Line(3), Row::new(Column(0), ' '));
storage.rotate(2);
assert_eq!(storage.zero, 2);
storage.shrink_lines(2);
@@ -376,9 +385,9 @@ mod tests {
// Setup storage area
let mut storage = Storage {
inner: vec![
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'-'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '-'),
],
zero: 0,
visible_lines: Line(3),
@@ -386,15 +395,15 @@ mod tests {
};
// Grow buffer
- storage.grow_visible_lines(Line(4), Row::new(Column(1), &'-'));
+ storage.grow_visible_lines(Line(4), Row::new(Column(1), '-'));
// Make sure the result is correct
let expected = Storage {
inner: vec![
- Row::new(Column(1), &'-'),
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'-'),
+ Row::new(Column(1), '-'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '-'),
],
zero: 1,
visible_lines: Line(4),
@@ -422,9 +431,9 @@ mod tests {
// Setup storage area.
let mut storage = Storage {
inner: vec![
- Row::new(Column(1), &'-'),
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
+ Row::new(Column(1), '-'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
],
zero: 1,
visible_lines: Line(3),
@@ -432,15 +441,15 @@ mod tests {
};
// Grow buffer.
- storage.grow_visible_lines(Line(4), Row::new(Column(1), &'-'));
+ storage.grow_visible_lines(Line(4), Row::new(Column(1), '-'));
// Make sure the result is correct.
let expected = Storage {
inner: vec![
- Row::new(Column(1), &'-'),
- Row::new(Column(1), &'-'),
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
+ Row::new(Column(1), '-'),
+ Row::new(Column(1), '-'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
],
zero: 2,
visible_lines: Line(4),
@@ -467,9 +476,9 @@ mod tests {
// Setup storage area.
let mut storage = Storage {
inner: vec![
- Row::new(Column(1), &'2'),
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
+ Row::new(Column(1), '2'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
],
zero: 1,
visible_lines: Line(3),
@@ -482,9 +491,9 @@ mod tests {
// Make sure the result is correct.
let expected = Storage {
inner: vec![
- Row::new(Column(1), &'2'),
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
+ Row::new(Column(1), '2'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
],
zero: 1,
visible_lines: Line(2),
@@ -511,9 +520,9 @@ mod tests {
// Setup storage area.
let mut storage = Storage {
inner: vec![
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'2'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '2'),
],
zero: 0,
visible_lines: Line(3),
@@ -526,9 +535,9 @@ mod tests {
// Make sure the result is correct.
let expected = Storage {
inner: vec![
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'2'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '2'),
],
zero: 0,
visible_lines: Line(2),
@@ -561,12 +570,12 @@ mod tests {
// Setup storage area.
let mut storage = Storage {
inner: vec![
- Row::new(Column(1), &'4'),
- Row::new(Column(1), &'5'),
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'2'),
- Row::new(Column(1), &'3'),
+ Row::new(Column(1), '4'),
+ Row::new(Column(1), '5'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '2'),
+ Row::new(Column(1), '3'),
],
zero: 2,
visible_lines: Line(6),
@@ -579,12 +588,12 @@ mod tests {
// Make sure the result is correct.
let expected = Storage {
inner: vec![
- Row::new(Column(1), &'4'),
- Row::new(Column(1), &'5'),
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'2'),
- Row::new(Column(1), &'3'),
+ Row::new(Column(1), '4'),
+ Row::new(Column(1), '5'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '2'),
+ Row::new(Column(1), '3'),
],
zero: 2,
visible_lines: Line(2),
@@ -613,12 +622,12 @@ mod tests {
// Setup storage area.
let mut storage = Storage {
inner: vec![
- Row::new(Column(1), &'4'),
- Row::new(Column(1), &'5'),
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'2'),
- Row::new(Column(1), &'3'),
+ Row::new(Column(1), '4'),
+ Row::new(Column(1), '5'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '2'),
+ Row::new(Column(1), '3'),
],
zero: 2,
visible_lines: Line(1),
@@ -630,7 +639,7 @@ mod tests {
// Make sure the result is correct.
let expected = Storage {
- inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1')],
+ inner: vec![Row::new(Column(1), '0'), Row::new(Column(1), '1')],
zero: 0,
visible_lines: Line(1),
len: 2,
@@ -655,9 +664,9 @@ mod tests {
// Setup storage area.
let mut storage = Storage {
inner: vec![
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'2'),
- Row::new(Column(1), &'0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '2'),
+ Row::new(Column(1), '0'),
],
zero: 2,
visible_lines: Line(1),
@@ -669,7 +678,7 @@ mod tests {
// Make sure the result is correct.
let expected = Storage {
- inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1')],
+ inner: vec![Row::new(Column(1), '0'), Row::new(Column(1), '1')],
zero: 0,
visible_lines: Line(1),
len: 2,
@@ -709,12 +718,12 @@ mod tests {
// Setup storage area.
let mut storage = Storage {
inner: vec![
- Row::new(Column(1), &'4'),
- Row::new(Column(1), &'5'),
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'2'),
- Row::new(Column(1), &'3'),
+ Row::new(Column(1), '4'),
+ Row::new(Column(1), '5'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '2'),
+ Row::new(Column(1), '3'),
],
zero: 2,
visible_lines: Line(0),
@@ -727,12 +736,12 @@ mod tests {
// Make sure the result after shrinking is correct.
let shrinking_expected = Storage {
inner: vec![
- Row::new(Column(1), &'4'),
- Row::new(Column(1), &'5'),
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'2'),
- Row::new(Column(1), &'3'),
+ Row::new(Column(1), '4'),
+ Row::new(Column(1), '5'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '2'),
+ Row::new(Column(1), '3'),
],
zero: 2,
visible_lines: Line(0),
@@ -743,18 +752,18 @@ mod tests {
assert_eq!(storage.len, shrinking_expected.len);
// Grow buffer.
- storage.grow_lines(4, Row::new(Column(1), &'-'));
+ storage.grow_lines(4, Row::new(Column(1), '-'));
// Make sure the result after shrinking is correct.
let growing_expected = Storage {
inner: vec![
- Row::new(Column(1), &'4'),
- Row::new(Column(1), &'5'),
- Row::new(Column(1), &'-'),
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'2'),
- Row::new(Column(1), &'3'),
+ Row::new(Column(1), '4'),
+ Row::new(Column(1), '5'),
+ Row::new(Column(1), '-'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '2'),
+ Row::new(Column(1), '3'),
],
zero: 3,
visible_lines: Line(0),
@@ -770,12 +779,12 @@ mod tests {
// Setup storage area.
let mut storage = Storage {
inner: vec![
- Row::new(Column(1), &'4'),
- Row::new(Column(1), &'5'),
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'2'),
- Row::new(Column(1), &'3'),
+ Row::new(Column(1), '4'),
+ Row::new(Column(1), '5'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '2'),
+ Row::new(Column(1), '3'),
],
zero: 2,
visible_lines: Line(0),
@@ -784,18 +793,18 @@ mod tests {
// Initialize additional lines.
let init_size = 3;
- storage.initialize(init_size, &'-', Column(1));
+ storage.initialize(init_size, '-', Column(1));
// Make sure the lines are present and at the right location.
let expected_init_size = std::cmp::max(init_size, MAX_CACHE_SIZE);
- let mut expected_inner = vec![Row::new(Column(1), &'4'), Row::new(Column(1), &'5')];
- expected_inner.append(&mut vec![Row::new(Column(1), &'-'); expected_init_size]);
+ let mut expected_inner = vec![Row::new(Column(1), '4'), Row::new(Column(1), '5')];
+ expected_inner.append(&mut vec![Row::new(Column(1), '-'); expected_init_size]);
expected_inner.append(&mut vec![
- Row::new(Column(1), &'0'),
- Row::new(Column(1), &'1'),
- Row::new(Column(1), &'2'),
- Row::new(Column(1), &'3'),
+ Row::new(Column(1), '0'),
+ Row::new(Column(1), '1'),
+ Row::new(Column(1), '2'),
+ Row::new(Column(1), '3'),
]);
let expected_storage = Storage {
inner: expected_inner,
@@ -813,9 +822,9 @@ mod tests {
fn rotate_wrap_zero() {
let mut storage = Storage {
inner: vec![
- Row::new(Column(1), &'-'),
- Row::new(Column(1), &'-'),
- Row::new(Column(1), &'-'),
+ Row::new(Column(1), '-'),
+ Row::new(Column(1), '-'),
+ Row::new(Column(1), '-'),
],
zero: 2,
visible_lines: Line(0),
diff --git a/alacritty_terminal/src/grid/tests.rs b/alacritty_terminal/src/grid/tests.rs
index ef011d16..04400afc 100644
--- a/alacritty_terminal/src/grid/tests.rs
+++ b/alacritty_terminal/src/grid/tests.rs
@@ -79,7 +79,7 @@ fn scroll_up() {
grid[Line(i)][Column(0)] = i;
}
- grid.scroll_up(&(Line(0)..Line(10)), Line(2), &0);
+ grid.scroll_up(&(Line(0)..Line(10)), Line(2), 0);
assert_eq!(grid[Line(0)][Column(0)], 2);
assert_eq!(grid[Line(0)].occ, 1);
@@ -111,7 +111,7 @@ fn scroll_down() {
grid[Line(i)][Column(0)] = i;
}
- grid.scroll_down(&(Line(0)..Line(10)), Line(2), &0);
+ grid.scroll_down(&(Line(0)..Line(10)), Line(2), 0);
assert_eq!(grid[Line(0)][Column(0)], 0); // was 8.
assert_eq!(grid[Line(0)].occ, 0);
@@ -183,7 +183,7 @@ fn shrink_reflow() {
grid[Line(0)][Column(3)] = cell('4');
grid[Line(0)][Column(4)] = cell('5');
- grid.resize(true, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
+ grid.resize(true, Line(1), Column(2));
assert_eq!(grid.len(), 3);
@@ -209,8 +209,8 @@ fn shrink_reflow_twice() {
grid[Line(0)][Column(3)] = cell('4');
grid[Line(0)][Column(4)] = cell('5');
- grid.resize(true, Line(1), Column(4), &mut Point::new(Line(0), Column(0)), &Cell::default());
- grid.resize(true, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
+ grid.resize(true, Line(1), Column(4));
+ grid.resize(true, Line(1), Column(2));
assert_eq!(grid.len(), 3);
@@ -236,7 +236,7 @@ fn shrink_reflow_empty_cell_inside_line() {
grid[Line(0)][Column(3)] = cell('4');
grid[Line(0)][Column(4)] = Cell::default();
- grid.resize(true, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
+ grid.resize(true, Line(1), Column(2));
assert_eq!(grid.len(), 2);
@@ -248,7 +248,7 @@ fn shrink_reflow_empty_cell_inside_line() {
assert_eq!(grid[0][Column(0)], cell('3'));
assert_eq!(grid[0][Column(1)], cell('4'));
- grid.resize(true, Line(1), Column(1), &mut Point::new(Line(0), Column(0)), &Cell::default());
+ grid.resize(true, Line(1), Column(1));
assert_eq!(grid.len(), 4);
@@ -273,7 +273,7 @@ fn grow_reflow() {
grid[Line(1)][Column(0)] = cell('3');
grid[Line(1)][Column(1)] = Cell::default();
- grid.resize(true, Line(2), Column(3), &mut Point::new(Line(0), Column(0)), &Cell::default());
+ grid.resize(true, Line(2), Column(3));
assert_eq!(grid.len(), 2);
@@ -299,7 +299,7 @@ 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), &mut Point::new(Line(0), Column(0)), &Cell::default());
+ grid.resize(true, Line(3), Column(6));
assert_eq!(grid.len(), 3);
@@ -330,7 +330,7 @@ fn grow_reflow_disabled() {
grid[Line(1)][Column(0)] = cell('3');
grid[Line(1)][Column(1)] = Cell::default();
- grid.resize(false, Line(2), Column(3), &mut Point::new(Line(0), Column(0)), &Cell::default());
+ grid.resize(false, Line(2), Column(3));
assert_eq!(grid.len(), 2);
@@ -354,7 +354,7 @@ fn shrink_reflow_disabled() {
grid[Line(0)][Column(3)] = cell('4');
grid[Line(0)][Column(4)] = cell('5');
- grid.resize(false, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
+ grid.resize(false, Line(1), Column(2));
assert_eq!(grid.len(), 1);
diff --git a/alacritty_terminal/src/index.rs b/alacritty_terminal/src/index.rs
index 72e7158a..a5f2bc76 100644
--- a/alacritty_terminal/src/index.rs
+++ b/alacritty_terminal/src/index.rs
@@ -53,27 +53,28 @@ impl<L> Point<L> {
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
- pub fn sub(mut self, num_cols: usize, rhs: usize) -> Point<L>
+ 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 line_changes =
- (rhs.saturating_sub(self.col.0) as f32 / num_cols as f32).ceil() as usize;
- if self.line.into() > Line(line_changes) {
+ let num_cols = num_cols.0;
+ let line_changes = (rhs + num_cols - 1).saturating_sub(self.col.0) / num_cols;
+ if self.line.into() >= Line(line_changes) {
self.line = self.line - line_changes;
+ self.col = Column((num_cols + self.col.0 - rhs % num_cols) % num_cols);
+ self
} else {
- self.line = Default::default();
+ Point::new(L::default(), Column(0))
}
- self.col = Column((num_cols + self.col.0 - rhs % num_cols) % num_cols);
- self
}
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
- pub fn add(mut self, num_cols: usize, rhs: usize) -> Point<L>
+ pub fn add(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;
self.line = self.line + (rhs + self.col.0) / num_cols;
self.col = Column((self.col.0 + rhs) % num_cols);
self
@@ -81,30 +82,30 @@ impl<L> Point<L> {
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
- pub fn sub_absolute(mut self, num_cols: usize, rhs: usize) -> Point<L>
+ pub fn sub_absolute(mut self, num_cols: Column, rhs: usize) -> Point<L>
where
L: Copy + Default + Into<Line> + Add<usize, Output = L> + Sub<usize, Output = L>,
{
- self.line =
- self.line + (rhs.saturating_sub(self.col.0) as f32 / num_cols as f32).ceil() as usize;
+ let num_cols = num_cols.0;
+ self.line = self.line + ((rhs + num_cols - 1).saturating_sub(self.col.0) / num_cols);
self.col = Column((num_cols + self.col.0 - rhs % num_cols) % num_cols);
self
}
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
- pub fn add_absolute(mut self, num_cols: usize, rhs: usize) -> Point<L>
+ pub fn add_absolute(mut self, num_cols: Column, rhs: usize) -> Point<L>
where
L: Copy + Default + Into<Line> + Add<usize, Output = L> + Sub<usize, Output = L>,
{
- let line_changes = (rhs + self.col.0) / num_cols;
- if self.line.into() > Line(line_changes) {
+ let line_changes = (rhs + self.col.0) / num_cols.0;
+ if self.line.into() >= Line(line_changes) {
self.line = self.line - line_changes;
+ self.col = Column((self.col.0 + rhs) % num_cols.0);
+ self
} else {
- self.line = Default::default();
+ Point::new(L::default(), num_cols - 1)
}
- self.col = Column((self.col.0 + rhs) % num_cols);
- self
}
}
@@ -453,4 +454,104 @@ mod tests {
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)));
}
+
+ #[test]
+ fn sub() {
+ let num_cols = Column(42);
+ let point = Point::new(0, Column(13));
+
+ let result = point.sub(num_cols, 1);
+
+ assert_eq!(result, Point::new(0, point.col - 1));
+ }
+
+ #[test]
+ fn sub_wrap() {
+ let num_cols = Column(42);
+ let point = Point::new(1, Column(0));
+
+ let result = point.sub(num_cols, 1);
+
+ assert_eq!(result, Point::new(0, num_cols - 1));
+ }
+
+ #[test]
+ fn sub_clamp() {
+ let num_cols = Column(42);
+ let point = Point::new(0, Column(0));
+
+ let result = point.sub(num_cols, 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.col + 1));
+ }
+
+ #[test]
+ fn add_wrap() {
+ let num_cols = Column(42);
+ let point = Point::new(0, num_cols - 1);
+
+ let result = point.add(num_cols, 1);
+
+ assert_eq!(result, Point::new(1, Column(0)));
+ }
+
+ #[test]
+ fn add_absolute() {
+ let num_cols = Column(42);
+ let point = Point::new(0, Column(13));
+
+ let result = point.add_absolute(num_cols, 1);
+
+ assert_eq!(result, Point::new(0, point.col + 1));
+ }
+
+ #[test]
+ fn add_absolute_wrap() {
+ let num_cols = Column(42);
+ let point = Point::new(1, num_cols - 1);
+
+ let result = point.add_absolute(num_cols, 1);
+
+ assert_eq!(result, Point::new(0, Column(0)));
+ }
+
+ #[test]
+ fn add_absolute_clamp() {
+ let num_cols = Column(42);
+ let point = Point::new(0, num_cols - 1);
+
+ let result = point.add_absolute(num_cols, 1);
+
+ assert_eq!(result, point);
+ }
+
+ #[test]
+ fn sub_absolute() {
+ let num_cols = Column(42);
+ let point = Point::new(0, Column(13));
+
+ let result = point.sub_absolute(num_cols, 1);
+
+ assert_eq!(result, Point::new(0, point.col - 1));
+ }
+
+ #[test]
+ fn sub_absolute_wrap() {
+ let num_cols = Column(42);
+ let point = Point::new(0, Column(0));
+
+ let result = point.sub_absolute(num_cols, 1);
+
+ assert_eq!(result, Point::new(1, num_cols - 1));
+ }
}
diff --git a/alacritty_terminal/src/selection.rs b/alacritty_terminal/src/selection.rs
index 7f5c21ec..5b05310c 100644
--- a/alacritty_terminal/src/selection.rs
+++ b/alacritty_terminal/src/selection.rs
@@ -106,20 +106,22 @@ impl Selection {
}
}
+ /// Update the end of the selection.
pub fn update(&mut self, point: Point<usize>, side: Side) {
self.region.end = Anchor::new(point, side);
}
pub fn rotate(
mut self,
- num_lines: usize,
- num_cols: usize,
- scrolling_region: &Range<Line>,
- offset: isize,
+ num_lines: Line,
+ num_cols: Column,
+ range: &Range<Line>,
+ delta: isize,
) -> Option<Selection> {
- // Convert scrolling region from viewport to buffer coordinates.
- let region_start = num_lines - scrolling_region.start.0;
- let region_end = num_lines - scrolling_region.end.0;
+ let range_bottom = range.start.0;
+ let range_top = range.end.0;
+ let num_lines = num_lines.0;
+ let num_cols = num_cols.0;
let (mut start, mut end) = (&mut self.region.start, &mut self.region.end);
if Self::points_need_swap(start.point, end.point) {
@@ -127,31 +129,30 @@ impl Selection {
}
// Rotate start of selection.
- if (start.point.line < region_start || region_start == num_lines)
- && start.point.line >= region_end
+ 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 + offset).unwrap_or(0);
+ start.point.line = usize::try_from(start.point.line as isize + delta).unwrap_or(0);
// If end is within the same region, delete selection once start rotates out.
- if start.point.line < region_end && end.point.line >= region_end {
+ if start.point.line < range_bottom && end.point.line >= range_bottom {
return None;
}
// Clamp selection to start of region.
- if start.point.line >= region_start && region_start != num_lines {
+ if start.point.line >= range_top && range_top != num_lines {
if self.ty != SelectionType::Block {
start.point.col = Column(0);
start.side = Side::Left;
}
- start.point.line = region_start - 1;
+ start.point.line = range_top - 1;
}
}
// Rotate end of selection.
- if (end.point.line < region_start || region_start == num_lines)
- && end.point.line >= region_end
+ 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 + offset).unwrap_or(0);
+ end.point.line = usize::try_from(end.point.line as isize + delta).unwrap_or(0);
// Delete selection if end has overtaken the start.
if end.point.line > start.point.line {
@@ -159,12 +160,12 @@ impl Selection {
}
// Clamp selection to end of region.
- if end.point.line < region_end {
+ if end.point.line < range_bottom {
if self.ty != SelectionType::Block {
end.point.col = Column(num_cols - 1);
end.side = Side::Right;
}
- end.point.line = region_end;
+ end.point.line = range_bottom;
}
}
@@ -175,7 +176,7 @@ impl Selection {
match self.ty {
SelectionType::Simple => {
let (mut start, mut end) = (self.region.start, self.region.end);
- if Selection::points_need_swap(start.point, end.point) {
+ if Self::points_need_swap(start.point, end.point) {
mem::swap(&mut start, &mut end);
}
@@ -519,14 +520,14 @@ mod tests {
#[test]
fn line_selection() {
- let num_lines = 10;
- let num_cols = 5;
+ let num_lines = Line(10);
+ let num_cols = Column(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(num_lines, num_cols, &(Line(0)..Line(num_lines)), 7).unwrap();
+ selection = selection.rotate(num_lines, num_cols, &(Line(0)..num_lines), 7).unwrap();
- assert_eq!(selection.to_range(&term(num_cols, num_lines)).unwrap(), SelectionRange {
+ assert_eq!(selection.to_range(&term(num_cols.0, num_lines.0)).unwrap(), SelectionRange {
start: Point::new(9, Column(0)),
end: Point::new(7, Column(4)),
is_block: false,
@@ -535,14 +536,14 @@ mod tests {
#[test]
fn semantic_selection() {
- let num_lines = 10;
- let num_cols = 5;
+ let num_lines = Line(10);
+ let num_cols = Column(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(num_lines, num_cols, &(Line(0)..Line(num_lines)), 7).unwrap();
+ selection = selection.rotate(num_lines, num_cols, &(Line(0)..num_lines), 7).unwrap();
- assert_eq!(selection.to_range(&term(num_cols, num_lines)).unwrap(), SelectionRange {
+ assert_eq!(selection.to_range(&term(num_cols.0, num_lines.0)).unwrap(), SelectionRange {
start: Point::new(9, Column(0)),
end: Point::new(7, Column(3)),
is_block: false,
@@ -551,14 +552,14 @@ mod tests {
#[test]
fn simple_selection() {
- let num_lines = 10;
- let num_cols = 5;
+ let num_lines = Line(10);
+ let num_cols = Column(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(num_lines, num_cols, &(Line(0)..Line(num_lines)), 7).unwrap();
+ selection = selection.rotate(num_lines, num_cols, &(Line(0)..num_lines), 7).unwrap();
- assert_eq!(selection.to_range(&term(num_cols, num_lines)).unwrap(), SelectionRange {
+ assert_eq!(selection.to_range(&term(num_cols.0, num_lines.0)).unwrap(), SelectionRange {
start: Point::new(9, Column(0)),
end: Point::new(7, Column(3)),
is_block: false,
@@ -567,14 +568,14 @@ mod tests {
#[test]
fn block_selection() {
- let num_lines = 10;
- let num_cols = 5;
+ let num_lines = Line(10);
+ let num_cols = Column(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(num_lines, num_cols, &(Line(0)..Line(num_lines)), 7).unwrap();
+ selection = selection.rotate(num_lines, num_cols, &(Line(0)..num_lines), 7).unwrap();
- assert_eq!(selection.to_range(&term(num_cols, num_lines)).unwrap(), SelectionRange {
+ assert_eq!(selection.to_range(&term(num_cols.0, num_lines.0)).unwrap(), SelectionRange {
start: Point::new(9, Column(2)),
end: Point::new(7, Column(3)),
is_block: true
@@ -611,15 +612,14 @@ mod tests {
#[test]
fn rotate_in_region_up() {
- let num_lines = 10;
- let num_cols = 5;
+ let num_lines = Line(10);
+ let num_cols = Column(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(num_lines, num_cols, &(Line(1)..Line(num_lines - 1)), 4).unwrap();
+ selection = selection.rotate(num_lines, num_cols, &(Line(1)..(num_lines - 1)), 4).unwrap();
- assert_eq!(selection.to_range(&term(num_cols, num_lines)).unwrap(), SelectionRange {
+ assert_eq!(selection.to_range(&term(num_cols.0, num_lines.0)).unwrap(), SelectionRange {
start: Point::new(8, Column(0)),
end: Point::new(6, Column(3)),
is_block: false,
@@ -628,32 +628,30 @@ mod tests {
#[test]
fn rotate_in_region_down() {
- let num_lines = 10;
- let num_cols = 5;
+ let num_lines = Line(10);
+ let num_cols = Column(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(num_lines, num_cols, &(Line(1)..Line(num_lines - 1)), -5).unwrap();
+ selection = selection.rotate(num_lines, num_cols, &(Line(1)..(num_lines - 1)), -5).unwrap();
- assert_eq!(selection.to_range(&term(num_cols, num_lines)).unwrap(), SelectionRange {
+ assert_eq!(selection.to_range(&term(num_cols.0, num_lines.0)).unwrap(), SelectionRange {
start: Point::new(3, Column(1)),
- end: Point::new(1, Column(num_cols - 1)),
+ end: Point::new(1, num_cols - 1),
is_block: false,
});
}
#[test]
fn rotate_in_region_up_block() {
- let num_lines = 10;
- let num_cols = 5;
+ let num_lines = Line(10);
+ let num_cols = Column(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(num_lines, num_cols, &(Line(1)..Line(num_lines - 1)), 4).unwrap();
+ selection = selection.rotate(num_lines, num_cols, &(Line(1)..(num_lines - 1)), 4).unwrap();
- assert_eq!(selection.to_range(&term(num_cols, num_lines)).unwrap(), SelectionRange {
+ assert_eq!(selection.to_range(&term(num_cols.0, num_lines.0)).unwrap(), SelectionRange {
start: Point::new(8, Column(2)),
end: Point::new(6, Column(3)),
is_block: true,
diff --git a/alacritty_terminal/src/term/cell.rs b/alacritty_terminal/src/term/cell.rs
index c23b3599..f95ed0c3 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 template = Cell::default();
- let mut row = Row::new(Column(10), &template);
+ let mut row = Row::new(Column(10), template);
row[Column(5)].c = 'a';
assert_eq!(row.line_length(), Column(6));
@@ -187,7 +187,7 @@ mod tests {
#[test]
fn line_length_works_with_wrapline() {
let template = Cell::default();
- let mut row = Row::new(Column(10), &template);
+ let mut row = Row::new(Column(10), template);
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 d5759421..bcb4853f 100644
--- a/alacritty_terminal/src/term/mod.rs
+++ b/alacritty_terminal/src/term/mod.rs
@@ -49,6 +49,9 @@ const TITLE_STACK_MAX_DEPTH: usize = 4096;
/// Default tab interval, corresponding to terminfo `it` value.
const INITIAL_TABSTOPS: usize = 8;
+/// Minimum number of columns and lines.
+const MIN_SIZE: usize = 2;
+
/// A type that can expand a given point to a region.
///
/// Usually this is implemented for some 2-D array type since
@@ -290,7 +293,7 @@ impl<'a, C> RenderableCellsIter<'a, C> {
return true;
}
- let num_cols = self.grid.num_cols().0;
+ let num_cols = self.grid.num_cols();
let cell = self.grid[&point];
// Check if wide char's spacers are selected.
@@ -543,86 +546,6 @@ pub mod mode {
pub use crate::term::mode::TermMode;
-trait CharsetMapping {
- fn map(&self, c: char) -> char {
- c
- }
-}
-
-impl CharsetMapping for StandardCharset {
- /// Switch/Map character to the active charset. Ascii is the common case and
- /// for that we want to do as little as possible.
- #[inline]
- fn map(&self, c: char) -> char {
- match *self {
- StandardCharset::Ascii => c,
- StandardCharset::SpecialCharacterAndLineDrawing => match c {
- '`' => '◆',
- 'a' => '▒',
- 'b' => '\t',
- 'c' => '\u{000c}',
- 'd' => '\r',
- 'e' => '\n',
- 'f' => '°',
- 'g' => '±',
- 'h' => '\u{2424}',
- 'i' => '\u{000b}',
- 'j' => '┘',
- 'k' => '┐',
- 'l' => '┌',
- 'm' => '└',
- 'n' => '┼',
- 'o' => '⎺',
- 'p' => '⎻',
- 'q' => '─',
- 'r' => '⎼',
- 's' => '⎽',
- 't' => '├',
- 'u' => '┤',
- 'v' => '┴',
- 'w' => '┬',
- 'x' => '│',
- 'y' => '≤',
- 'z' => '≥',
- '{' => 'π',
- '|' => '≠',
- '}' => '£',
- '~' => '·',
- _ => c,
- },
- }
- }
-}
-
-#[derive(Default, Copy, Clone)]
-struct Charsets([StandardCharset; 4]);
-
-impl Index<CharsetIndex> for Charsets {
- type Output = StandardCharset;
-
- fn index(&self, index: CharsetIndex) -> &StandardCharset {
- &self.0[index as usize]
- }
-}
-
-impl IndexMut<CharsetIndex> for Charsets {
- fn index_mut(&mut self, index: CharsetIndex) -> &mut StandardCharset {
- &mut self.0[index as usize]
- }
-}
-
-#[derive(Default, Copy, Clone)]
-pub struct Cursor {
- /// The location of this cursor.
- pub point: Point,
-
- /// Template cell when using this cursor.
- template: Cell,
-
- /// Currently configured graphic character sets.
- charsets: Charsets,
-}
-
pub struct VisualBell {
/// Visual bell animation.
animation: VisualBellAnimation,
@@ -803,9 +726,20 @@ impl SizeInfo {
}
pub struct Term<T> {
- /// Terminal focus.
+ /// Terminal requires redraw.
+ pub dirty: bool,
+
+ /// Visual bell configuration and status.
+ pub visual_bell: VisualBell,
+
+ /// Terminal focus controlling the cursor shape.
pub is_focused: bool,
+ /// Cursor for keyboard selection.
+ pub vi_mode_cursor: ViModeCursor,
+
+ pub selection: Option<Selection>,
+
/// The grid.
grid: Grid<Cell>,
@@ -822,12 +756,6 @@ pub struct Term<T> {
/// Alt is active.
alt: bool,
- /// The cursor.
- cursor: Cursor,
-
- /// Cursor location for vi mode.
- pub vi_mode_cursor: ViModeCursor,
-
/// Index into `charsets`, pointing to what ASCII is currently being mapped to.
active_charset: CharsetIndex,
@@ -842,16 +770,6 @@ pub struct Term<T> {
/// Range going from top to bottom of the terminal, indexed from the top of the viewport.
scroll_region: Range<Line>,
- pub dirty: bool,
-
- pub visual_bell: VisualBell,
-
- /// Saved cursor from main grid.
- cursor_save: Cursor,
-
- /// Saved cursor from alt grid.
- cursor_save_alt: Cursor,
-
semantic_escape_chars: String,
/// Colors used for rendering.
@@ -893,14 +811,6 @@ pub struct Term<T> {
}
impl<T> Term<T> {
- pub fn selection(&self) -> &Option<Selection> {
- &self.grid.selection
- }
-
- pub fn selection_mut(&mut self) -> &mut Option<Selection> {
- &mut self.grid.selection
- }
-
#[inline]
pub fn scroll_display(&mut self, scroll: Scroll)
where
@@ -938,10 +848,7 @@ impl<T> Term<T> {
alt_grid: alt,
alt: false,
active_charset: Default::default(),
- cursor: Default::default(),
vi_mode_cursor: Default::default(),
- cursor_save: Default::default(),
- cursor_save_alt: Default::default(),
tabs,
mode: Default::default(),
scroll_region,
@@ -959,6 +866,7 @@ impl<T> Term<T> {
title: None,
default_title: config.window.title.clone(),
title_stack: Vec::new(),
+ selection: None,
}
}
@@ -1000,8 +908,8 @@ impl<T> Term<T> {
/// Convert the active selection to a String.
pub fn selection_to_string(&self) -> Option<String> {
- let selection = self.grid.selection.clone()?;
- let SelectionRange { start, end, is_block } = selection.to_range(self)?;
+ let selection_range = self.selection.as_ref().and_then(|s| s.to_range(self))?;
+ let SelectionRange { start, end, is_block } = selection_range;
let mut res = String::new();
@@ -1125,7 +1033,7 @@ impl<T> Term<T> {
/// background color. Cells with an alternate background color are
/// considered renderable as are cells with any text content.
pub fn renderable_cells<'b, C>(&'b self, config: &'b Config<C>) -> RenderableCellsIter<'_, C> {
- let selection = self.grid.selection.as_ref().and_then(|s| s.to_range(self));
+ let selection = self.selection.as_ref().and_then(|s| s.to_range(self));
RenderableCellsIter::new(&self, config, selection)
}
@@ -1134,52 +1042,31 @@ impl<T> Term<T> {
pub fn resize(&mut self, size: &SizeInfo) {
let old_cols = self.grid.num_cols();
let old_lines = self.grid.num_lines();
- let mut num_cols = size.cols();
- let mut num_lines = size.lines();
+ let num_cols = max(size.cols(), Column(MIN_SIZE));
+ let num_lines = max(size.lines(), Line(MIN_SIZE));
if old_cols == num_cols && old_lines == num_lines {
debug!("Term::resize dimensions unchanged");
return;
}
- self.grid.selection = None;
- self.alt_grid.selection = None;
-
- // Should not allow less than 2 cols, causes all sorts of checks to be required.
- if num_cols <= Column(1) {
- num_cols = Column(2);
- }
-
- // Should not allow less than 2 lines, causes all sorts of checks to be required.
- if num_lines <= Line(1) {
- num_lines = Line(2);
- }
-
debug!("New num_cols is {} and num_lines is {}", num_cols, num_lines);
let is_alt = self.mode.contains(TermMode::ALT_SCREEN);
- let alt_cursor_point =
- if is_alt { &mut self.cursor_save.point } else { &mut self.cursor_save_alt.point };
- // Resize grids to new size.
- self.grid.resize(!is_alt, num_lines, num_cols, &mut self.cursor.point, &Cell::default());
- self.alt_grid.resize(is_alt, num_lines, num_cols, alt_cursor_point, &Cell::default());
+ self.grid.resize(!is_alt, num_lines, num_cols);
+ self.alt_grid.resize(is_alt, num_lines, num_cols);
- // Reset scrolling region to new size.
- self.scroll_region = Line(0)..self.grid.num_lines();
-
- // Ensure cursors are in-bounds.
- self.cursor.point.col = min(self.cursor.point.col, num_cols - 1);
- self.cursor.point.line = min(self.cursor.point.line, num_lines - 1);
- self.cursor_save.point.col = min(self.cursor_save.point.col, num_cols - 1);
- self.cursor_save.point.line = min(self.cursor_save.point.line, num_lines - 1);
- self.cursor_save_alt.point.col = min(self.cursor_save_alt.point.col, num_cols - 1);
- self.cursor_save_alt.point.line = min(self.cursor_save_alt.point.line, num_lines - 1);
+ // Clamp vi cursor to viewport.
self.vi_mode_cursor.point.col = min(self.vi_mode_cursor.point.col, num_cols - 1);
self.vi_mode_cursor.point.line = min(self.vi_mode_cursor.point.line, num_lines - 1);
// Recreate tabs list.
self.tabs.resize(self.grid.num_cols());
+
+ // Reset scrolling region and selection.
+ self.scroll_region = Line(0)..self.grid.num_lines();
+ self.selection = None;
}
#[inline]
@@ -1187,20 +1074,16 @@ impl<T> Term<T> {
&self.mode
}
- #[inline]
- pub fn cursor(&self) -> &Cursor {
- &self.cursor
- }
-
pub fn swap_alt(&mut self) {
if self.alt {
- let template = self.cursor.template;
+ let template = self.grid.cursor.template;
self.grid.region_mut(..).each(|c| c.reset(&template));
}
- self.grid.selection = None;
self.alt = !self.alt;
mem::swap(&mut self.grid, &mut self.alt_grid);
+
+ self.selection = None;
}
/// Scroll screen down.
@@ -1210,12 +1093,25 @@ impl<T> Term<T> {
#[inline]
fn scroll_down_relative(&mut self, origin: Line, mut lines: Line) {
trace!("Scrolling down relative: origin={}, lines={}", origin, lines);
+
+ let num_lines = self.grid.num_lines();
+ let num_cols = self.grid.num_cols();
+
lines = min(lines, self.scroll_region.end - self.scroll_region.start);
lines = min(lines, self.scroll_region.end - origin);
+ 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(num_lines, num_cols, &absolute_region, -(lines.0 as isize)));
+
// Scroll between origin and bottom
- let template = Cell { bg: self.cursor.template.bg, ..Cell::default() };
- self.grid.scroll_down(&(origin..self.scroll_region.end), lines, &template);
+ let template = Cell { bg: self.grid.cursor.template.bg, ..Cell::default() };
+ self.grid.scroll_down(&region, lines, template);
}
/// Scroll screen up
@@ -1223,13 +1119,25 @@ 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, lines: Line) {
+ fn scroll_up_relative(&mut self, origin: Line, mut lines: Line) {
trace!("Scrolling up relative: origin={}, lines={}", origin, lines);
- let lines = min(lines, self.scroll_region.end - self.scroll_region.start);
+ let num_lines = self.grid.num_lines();
+ let num_cols = self.grid.num_cols();
+
+ lines = min(lines, self.scroll_region.end - self.scroll_region.start);
+
+ 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(num_lines, num_cols, &absolute_region, lines.0 as isize));
// Scroll from origin to bottom less number of lines.
- let template = Cell { bg: self.cursor.template.bg, ..Cell::default() };
- self.grid.scroll_up(&(origin..self.scroll_region.end), lines, &template);
+ let template = Cell { bg: self.grid.cursor.template.bg, ..Cell::default() };
+ self.grid.scroll_up(&region, lines, template);
}
fn deccolm(&mut self)
@@ -1241,7 +1149,7 @@ impl<T> Term<T> {
self.set_scrolling_region(1, self.grid.num_lines().0);
// Clear grid.
- let template = self.cursor.template;
+ let template = self.grid.cursor.template;
self.grid.region_mut(..).each(|c| c.reset(&template));
}
@@ -1267,12 +1175,13 @@ impl<T> Term<T> {
#[inline]
pub fn toggle_vi_mode(&mut self) {
self.mode ^= TermMode::VI;
- self.grid.selection = None;
+ self.selection = None;
// Reset vi mode cursor position to match primary cursor.
if self.mode.contains(TermMode::VI) {
- let line = min(self.cursor.point.line + self.grid.display_offset(), self.lines() - 1);
- self.vi_mode_cursor = ViModeCursor::new(Point::new(line, self.cursor.point.col));
+ let cursor = self.grid.cursor.point;
+ let line = min(cursor.line + self.grid.display_offset(), self.lines() - 1);
+ self.vi_mode_cursor = ViModeCursor::new(Point::new(line, cursor.col));
}
self.dirty = true;
@@ -1294,8 +1203,8 @@ impl<T> Term<T> {
// Update selection if one is active.
let viewport_point = self.visible_to_buffer(self.vi_mode_cursor.point);
- if let Some(selection) = &mut self.grid.selection {
- // Do not extend empty selections started by single mouse click.
+ if let Some(selection) = &mut self.selection {
+ // Do not extend empty selections started by a single mouse click.
if !selection.is_empty() {
selection.update(viewport_point, Side::Left);
selection.include_all();
@@ -1322,15 +1231,15 @@ impl<T> Term<T> {
trace!("Wrapping input");
- self.grid[&self.cursor.point].flags.insert(Flags::WRAPLINE);
+ self.grid.cursor_cell().flags.insert(Flags::WRAPLINE);
- if (self.cursor.point.line + 1) >= self.scroll_region.end {
+ if (self.grid.cursor.point.line + 1) >= self.scroll_region.end {
self.linefeed();
} else {
- self.cursor.point.line += 1;
+ self.grid.cursor.point.line += 1;
}
- self.cursor.point.col = Column(0);
+ self.grid.cursor.point.col = Column(0);
self.input_needs_wrap = false;
}
@@ -1340,10 +1249,13 @@ impl<T> Term<T> {
where
T: EventListener,
{
- let cell = &mut self.grid[&self.cursor.point];
- *cell = self.cursor.template;
- cell.c = self.cursor.charsets[self.active_charset].map(c);
- cell
+ let mut cell = self.grid.cursor.template;
+ cell.c = self.grid.cursor.charsets[self.active_charset].map(c);
+
+ let cursor_cell = self.grid.cursor_cell();
+ *cursor_cell = cell;
+
+ cursor_cell
}
/// Get rendering information about the active cursor.
@@ -1354,7 +1266,7 @@ impl<T> Term<T> {
let mut point = if vi_mode {
self.vi_mode_cursor.point
} else {
- let mut point = self.cursor.point;
+ let mut point = self.grid.cursor.point;
point.line += self.grid.display_offset();
point
};
@@ -1430,8 +1342,8 @@ impl<T: EventListener> Handler for Term<T> {
// Handle zero-width characters.
if width == 0 {
- let mut col = self.cursor.point.col.0.saturating_sub(1);
- let line = self.cursor.point.line;
+ let mut col = self.grid.cursor.point.col.0.saturating_sub(1);
+ let line = self.grid.cursor.point.line;
if self.grid[line][Column(col)].flags.contains(Flags::WIDE_CHAR_SPACER) {
col = col.saturating_sub(1);
}
@@ -1447,9 +1359,9 @@ impl<T: EventListener> Handler for Term<T> {
let num_cols = self.grid.num_cols();
// If in insert mode, first shift cells to the right.
- if self.mode.contains(TermMode::INSERT) && self.cursor.point.col + width < num_cols {
- let line = self.cursor.point.line;
- let col = self.cursor.point.col;
+ if self.mode.contains(TermMode::INSERT) && self.grid.cursor.point.col + width < num_cols {
+ let line = self.grid.cursor.point.line;
+ let col = self.grid.cursor.point.col;
let line = &mut self.grid[line];
let src = line[col..].as_ptr();
@@ -1462,7 +1374,7 @@ impl<T: EventListener> Handler for Term<T> {
if width == 1 {
self.write_at_cursor(c);
} else {
- if self.cursor.point.col + 1 >= num_cols {
+ if self.grid.cursor.point.col + 1 >= num_cols {
if self.mode.contains(TermMode::LINE_WRAP) {
// Insert placeholder before wide char if glyph does not fit in this row.
self.write_at_cursor(' ').flags.insert(Flags::WIDE_CHAR_SPACER);
@@ -1478,12 +1390,12 @@ impl<T: EventListener> Handler for Term<T> {
self.write_at_cursor(c).flags.insert(Flags::WIDE_CHAR);
// Write spacer to cell following the wide glyph.
- self.cursor.point.col += 1;
+ self.grid.cursor.point.col += 1;
self.write_at_cursor(' ').flags.insert(Flags::WIDE_CHAR_SPACER);
}
- if self.cursor.point.col + 1 < num_cols {
- self.cursor.point.col += 1;
+ if self.grid.cursor.point.col + 1 < num_cols {
+ self.grid.cursor.point.col += 1;
} else {
self.input_needs_wrap = true;
}
@@ -1506,34 +1418,35 @@ impl<T: EventListener> Handler for Term<T> {
(Line(0), self.grid.num_lines() - 1)
};
- self.cursor.point.line = min(line + y_offset, max_y);
- self.cursor.point.col = min(col, self.grid.num_cols() - 1);
+ self.grid.cursor.point.line = min(line + y_offset, max_y);
+ self.grid.cursor.point.col = min(col, self.grid.num_cols() - 1);
self.input_needs_wrap = false;
}
#[inline]
fn goto_line(&mut self, line: Line) {
trace!("Going to line: {}", line);
- self.goto(line, self.cursor.point.col)
+ self.goto(line, self.grid.cursor.point.col)
}
#[inline]
fn goto_col(&mut self, col: Column) {
trace!("Going to column: {}", col);
- self.goto(self.cursor.point.line, col)
+ self.goto(self.grid.cursor.point.line, col)
}
#[inline]
fn insert_blank(&mut self, count: Column) {
- // Ensure inserting within terminal bounds.
+ let cursor = self.grid.cursor;
- let count = min(count, self.grid.num_cols() - self.cursor.point.col);
+ // Ensure inserting within terminal bounds
+ let count = min(count, self.grid.num_cols() - cursor.point.col);
- let source = self.cursor.point.col;
- let destination = self.cursor.point.col + count;
+ let source = cursor.point.col;
+ let destination = cursor.point.col + count;
let num_cells = (self.grid.num_cols() - destination).0;
- let line = &mut self.grid[self.cursor.point.line];
+ let line = &mut self.grid[cursor.point.line];
unsafe {
let src = line[source..].as_ptr();
@@ -1545,35 +1458,36 @@ impl<T: EventListener> Handler for Term<T> {
// Cells were just moved out towards the end of the line; fill in
// between source and dest with blanks.
for c in &mut line[source..destination] {
- c.reset(&self.cursor.template);
+ c.reset(&cursor.template);
}
}
#[inline]
fn move_up(&mut self, lines: Line) {
trace!("Moving up: {}", lines);
- let move_to = Line(self.cursor.point.line.0.saturating_sub(lines.0));
- self.goto(move_to, self.cursor.point.col)
+ let move_to = Line(self.grid.cursor.point.line.0.saturating_sub(lines.0));
+ self.goto(move_to, self.grid.cursor.point.col)
}
#[inline]
fn move_down(&mut self, lines: Line) {
trace!("Moving down: {}", lines);
- let move_to = self.cursor.point.line + lines;
- self.goto(move_to, self.cursor.point.col)
+ let move_to = self.grid.cursor.point.line + lines;
+ self.goto(move_to, self.grid.cursor.point.col)
}
#[inline]
fn move_forward(&mut self, cols: Column) {
trace!("Moving forward: {}", cols);
- self.cursor.point.col = min(self.cursor.point.col + cols, self.grid.num_cols() - 1);
+ let num_cols = self.grid.num_cols();
+ self.grid.cursor.point.col = min(self.grid.cursor.point.col + cols, num_cols - 1);
self.input_needs_wrap = false;
}
#[inline]
fn move_backward(&mut self, cols: Column) {
trace!("Moving backward: {}", cols);
- self.cursor.point.col -= min(self.cursor.point.col, cols);
+ self.grid.cursor.point.col = Column(self.grid.cursor.point.col.saturating_sub(cols.0));
self.input_needs_wrap = false;
}
@@ -1591,7 +1505,7 @@ impl<T: EventListener> Handler for Term<T> {
let _ = writer.write_all(b"\x1b[0n");
},
6 => {
- let pos = self.cursor.point;
+ let pos = self.grid.cursor.point;
let response = format!("\x1b[{};{}R", pos.line + 1, pos.col + 1);
let _ = writer.write_all(response.as_bytes());
},
@@ -1602,14 +1516,14 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn move_down_and_cr(&mut self, lines: Line) {
trace!("Moving down and cr: {}", lines);
- let move_to = self.cursor.point.line + lines;
+ let move_to = self.grid.cursor.point.line + lines;
self.goto(move_to, Column(0))
}
#[inline]
fn move_up_and_cr(&mut self, lines: Line) {
trace!("Moving up and cr: {}", lines);
- let move_to = Line(self.cursor.point.line.0.saturating_sub(lines.0));
+ let move_to = Line(self.grid.cursor.point.line.0.saturating_sub(lines.0));
self.goto(move_to, Column(0))
}
@@ -1622,22 +1536,23 @@ impl<T: EventListener> Handler for Term<T> {
return;
}
- while self.cursor.point.col < self.grid.num_cols() && count != 0 {
+ while self.grid.cursor.point.col < self.grid.num_cols() && count != 0 {
count -= 1;
- let cell = &mut self.grid[&self.cursor.point];
+ let c = self.grid.cursor.charsets[self.active_charset].map('\t');
+ let cell = self.grid.cursor_cell();
if cell.c == ' ' {
- cell.c = self.cursor.charsets[self.active_charset].map('\t');
+ cell.c = c;
}
loop {
- if (self.cursor.point.col + 1) == self.grid.num_cols() {
+ if (self.grid.cursor.point.col + 1) == self.grid.num_cols() {
break;
}
- self.cursor.point.col += 1;
+ self.grid.cursor.point.col += 1;
- if self.tabs[self.cursor.point.col] {
+ if self.tabs[self.grid.cursor.point.col] {
break;
}
}
@@ -1648,8 +1563,9 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn backspace(&mut self) {
trace!("Backspace");
- if self.cursor.point.col > Column(0) {
- self.cursor.point.col -= 1;
+
+ if self.grid.cursor.point.col > Column(0) {
+ self.grid.cursor.point.col -= 1;
self.input_needs_wrap = false;
}
}
@@ -1658,7 +1574,7 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn carriage_return(&mut self) {
trace!("Carriage return");
- self.cursor.point.col = Column(0);
+ self.grid.cursor.point.col = Column(0);
self.input_needs_wrap = false;
}
@@ -1666,11 +1582,11 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn linefeed(&mut self) {
trace!("Linefeed");
- let next = self.cursor.point.line + 1;
+ let next = self.grid.cursor.point.line + 1;
if next == self.scroll_region.end {
self.scroll_up(Line(1));
} else if next < self.grid.num_lines() {
- self.cursor.point.line += 1;
+ self.grid.cursor.point.line += 1;
}
}
@@ -1721,8 +1637,7 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn set_horizontal_tabstop(&mut self) {
trace!("Setting horizontal tabstop");
- let column = self.cursor.point.col;
- self.tabs[column] = true;
+ self.tabs[self.grid.cursor.point.col] = true;
}
#[inline]
@@ -1740,49 +1655,54 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn insert_blank_lines(&mut self, lines: Line) {
trace!("Inserting blank {} lines", lines);
- if self.scroll_region.contains(&self.cursor.point.line) {
- let origin = self.cursor.point.line;
+
+ let origin = self.grid.cursor.point.line;
+ if self.scroll_region.contains(&origin) {
self.scroll_down_relative(origin, lines);
}
}
#[inline]
fn delete_lines(&mut self, lines: Line) {
- let origin = self.cursor.point.line;
+ let origin = self.grid.cursor.point.line;
let lines = min(self.lines() - origin, lines);
trace!("Deleting {} lines", lines);
- if lines.0 > 0 && self.scroll_region.contains(&self.cursor.point.line) {
+ if lines.0 > 0 && self.scroll_region.contains(&self.grid.cursor.point.line) {
self.scroll_up_relative(origin, lines);
}
}
#[inline]
fn erase_chars(&mut self, count: Column) {
- trace!("Erasing chars: count={}, col={}", count, self.cursor.point.col);
- let start = self.cursor.point.col;
+ let cursor = self.grid.cursor;
+
+ trace!("Erasing chars: count={}, col={}", count, cursor.point.col);
+
+ let start = cursor.point.col;
let end = min(start + count, self.grid.num_cols());
- let row = &mut self.grid[self.cursor.point.line];
// Cleared cells have current background color set.
+ let row = &mut self.grid[cursor.point.line];
for c in &mut row[start..end] {
- c.reset(&self.cursor.template);
+ c.reset(&cursor.template);
}
}
#[inline]
fn delete_chars(&mut self, count: Column) {
let cols = self.grid.num_cols();
+ let cursor = self.grid.cursor;
// Ensure deleting within terminal bounds.
let count = min(count, cols);
- let start = self.cursor.point.col;
+ let start = cursor.point.col;
let end = min(start + count, cols - 1);
let n = (cols - end).0;
- let line = &mut self.grid[self.cursor.point.line];
+ let line = &mut self.grid[cursor.point.line];
unsafe {
let src = line[end..].as_ptr();
@@ -1795,7 +1715,7 @@ impl<T: EventListener> Handler for Term<T> {
// 1 cell.
let end = cols - count;
for c in &mut line[end..] {
- c.reset(&self.cursor.template);
+ c.reset(&cursor.template);
}
}
@@ -1804,14 +1724,14 @@ impl<T: EventListener> Handler for Term<T> {
trace!("Moving backward {} tabs", count);
for _ in 0..count {
- let mut col = self.cursor.point.col;
+ let mut col = self.grid.cursor.point.col;
for i in (0..(col.0)).rev() {
if self.tabs[index::Column(i)] {
col = index::Column(i);
break;
}
}
- self.cursor.point.col = col;
+ self.grid.cursor.point.col = col;
}
}
@@ -1823,44 +1743,39 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn save_cursor_position(&mut self) {
trace!("Saving cursor position");
- let cursor = if self.alt { &mut self.cursor_save_alt } else { &mut self.cursor_save };
- *cursor = self.cursor;
+ self.grid.saved_cursor = self.grid.cursor;
}
#[inline]
fn restore_cursor_position(&mut self) {
trace!("Restoring cursor position");
- let source = if self.alt { &self.cursor_save_alt } else { &self.cursor_save };
- self.cursor = *source;
- self.cursor.point.line = min(self.cursor.point.line, self.grid.num_lines() - 1);
- self.cursor.point.col = min(self.cursor.point.col, self.grid.num_cols() - 1);
+ self.grid.cursor = self.grid.saved_cursor;
}
#[inline]
fn clear_line(&mut self, mode: ansi::LineClearMode) {
trace!("Clearing line: {:?}", mode);
- let col = self.cursor.point.col;
-
+ let cursor = self.grid.cursor;
match mode {
ansi::LineClearMode::Right => {
- let row = &mut self.grid[self.cursor.point.line];
- for cell in &mut row[col..] {
- cell.reset(&self.cursor.template);
+ let row = &mut self.grid[cursor.point.line];
+ for cell in &mut row[cursor.point.col..] {
+ cell.reset(&cursor.template);
}
},
ansi::LineClearMode::Left => {
- let row = &mut self.grid[self.cursor.point.line];
- for cell in &mut row[..=col] {
- cell.reset(&self.cursor.template);
+ let row = &mut self.grid[cursor.point.line];
+ for cell in &mut row[..=cursor.point.col] {
+ cell.reset(&cursor.template);
}
},
ansi::LineClearMode::All => {
- let row = &mut self.grid[self.cursor.point.line];
+ let row = &mut self.grid[cursor.point.line];
for cell in &mut row[..] {
- cell.reset(&self.cursor.template);
+ cell.reset(&cursor.template);
}
},
}
@@ -1934,34 +1849,34 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn clear_screen(&mut self, mode: ansi::ClearMode) {
trace!("Clearing screen: {:?}", mode);
- let template = self.cursor.template;
+ let template = self.grid.cursor.template;
// Remove active selections.
- self.grid.selection = None;
+ self.selection = None;
match mode {
ansi::ClearMode::Above => {
+ let cursor = self.grid.cursor.point;
+
// If clearing more than one line.
- if self.cursor.point.line > Line(1) {
+ if cursor.line > Line(1) {
// Fully clear all lines before the current line.
- self.grid
- .region_mut(..self.cursor.point.line)
- .each(|cell| cell.reset(&template));
+ self.grid.region_mut(..cursor.line).each(|cell| cell.reset(&template));
}
+
// Clear up to the current column in the current line.
- let end = min(self.cursor.point.col + 1, self.grid.num_cols());
- for cell in &mut self.grid[self.cursor.point.line][..end] {
+ let end = min(cursor.col + 1, self.grid.num_cols());
+ for cell in &mut self.grid[cursor.line][..end] {
cell.reset(&template);
}
},
ansi::ClearMode::Below => {
- for cell in &mut self.grid[self.cursor.point.line][self.cursor.point.col..] {
+ let cursor = self.grid.cursor.point;
+ for cell in &mut self.grid[cursor.line][cursor.col..] {
cell.reset(&template);
}
- if self.cursor.point.line < self.grid.num_lines() - 1 {
- self.grid
- .region_mut((self.cursor.point.line + 1)..)
- .each(|cell| cell.reset(&template));
+ if cursor.line < self.grid.num_lines() - 1 {
+ self.grid.region_mut((cursor.line + 1)..).each(|cell| cell.reset(&template));
}
},
ansi::ClearMode::All => {
@@ -1969,7 +1884,7 @@ impl<T: EventListener> Handler for Term<T> {
self.grid.region_mut(..).each(|c| c.reset(&template));
} else {
let template = Cell { bg: template.bg, ..Cell::default() };
- self.grid.clear_viewport(&template);
+ self.grid.clear_viewport(template);
}
},
ansi::ClearMode::Saved => self.grid.clear_history(),
@@ -1981,8 +1896,7 @@ impl<T: EventListener> Handler for Term<T> {
trace!("Clearing tabs: {:?}", mode);
match mode {
ansi::TabulationClearMode::Current => {
- let column = self.cursor.point.col;
- self.tabs[column] = false;
+ self.tabs[self.grid.cursor.point.col] = false;
},
ansi::TabulationClearMode::All => {
self.tabs.clear_all();
@@ -1997,30 +1911,28 @@ impl<T: EventListener> Handler for Term<T> {
self.swap_alt();
}
self.input_needs_wrap = false;
- self.cursor = Default::default();
self.active_charset = Default::default();
self.mode = Default::default();
- self.cursor_save = Default::default();
- self.cursor_save_alt = Default::default();
self.colors = self.original_colors;
self.color_modified = [false; color::COUNT];
self.cursor_style = None;
- self.grid.reset(&Cell::default());
- self.alt_grid.reset(&Cell::default());
+ self.grid.reset(Cell::default());
+ self.alt_grid.reset(Cell::default());
self.scroll_region = Line(0)..self.grid.num_lines();
self.tabs = TabStops::new(self.grid.num_cols());
self.title_stack = Vec::new();
self.title = None;
+ self.selection = None;
}
#[inline]
fn reverse_index(&mut self) {
trace!("Reversing index");
- // If cursor is at the top.
- if self.cursor.point.line == self.scroll_region.start {
+
+ if self.grid.cursor.point.line == self.scroll_region.start {
self.scroll_down(Line(1));
} else {
- self.cursor.point.line -= min(self.cursor.point.line, Line(1));
+ self.grid.cursor.point.line = Line(self.grid.cursor.point.line.saturating_sub(1));
}
}
@@ -2028,28 +1940,29 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn terminal_attribute(&mut self, attr: Attr) {
trace!("Setting attribute: {:?}", attr);
+ let cursor = &mut self.grid.cursor;
match attr {
- Attr::Foreground(color) => self.cursor.template.fg = color,
- Attr::Background(color) => self.cursor.template.bg = color,
+ Attr::Foreground(color) => cursor.template.fg = color,
+ Attr::Background(color) => cursor.template.bg = color,
Attr::Reset => {
- self.cursor.template.fg = Color::Named(NamedColor::Foreground);
- self.cursor.template.bg = Color::Named(NamedColor::Background);
- self.cursor.template.flags = Flags::empty();
+ cursor.template.fg = Color::Named(NamedColor::Foreground);
+ cursor.template.bg = Color::Named(NamedColor::Background);
+ cursor.template.flags = Flags::empty();
},
- Attr::Reverse => self.cursor.template.flags.insert(Flags::INVERSE),
- Attr::CancelReverse => self.cursor.template.flags.remove(Flags::INVERSE),
- Attr::Bold => self.cursor.template.flags.insert(Flags::BOLD),
- Attr::CancelBold => self.cursor.template.flags.remove(Flags::BOLD),
- Attr::Dim => self.cursor.template.flags.insert(Flags::DIM),
- Attr::CancelBoldDim => self.cursor.template.flags.remove(Flags::BOLD | Flags::DIM),
- Attr::Italic => self.cursor.template.flags.insert(Flags::ITALIC),
- Attr::CancelItalic => self.cursor.template.flags.remove(Flags::ITALIC),
- Attr::Underline => self.cursor.template.flags.insert(Flags::UNDERLINE),
- Attr::CancelUnderline => self.cursor.template.flags.remove(Flags::UNDERLINE),
- Attr::Hidden => self.cursor.template.flags.insert(Flags::HIDDEN),
- Attr::CancelHidden => self.cursor.template.flags.remove(Flags::HIDDEN),
- Attr::Strike => self.cursor.template.flags.insert(Flags::STRIKEOUT),
- Attr::CancelStrike => self.cursor.template.flags.remove(Flags::STRIKEOUT),
+ Attr::Reverse => cursor.template.flags.insert(Flags::INVERSE),
+ Attr::CancelReverse => cursor.template.flags.remove(Flags::INVERSE),
+ Attr::Bold => cursor.template.flags.insert(Flags::BOLD),
+ Attr::CancelBold => cursor.template.flags.remove(Flags::BOLD),
+ Attr::Dim => cursor.template.flags.insert(Flags::DIM),
+ Attr::CancelBoldDim => cursor.template.flags.remove(Flags::BOLD | Flags::DIM),
+ Attr::Italic => cursor.template.flags.insert(Flags::ITALIC),
+ Attr::CancelItalic => cursor.template.flags.remove(Flags::ITALIC),
+ Attr::Underline => cursor.template.flags.insert(Flags::UNDERLINE),
+ Attr::CancelUnderline => cursor.template.flags.remove(Flags::UNDERLINE),
+ Attr::Hidden => cursor.template.flags.insert(Flags::HIDDEN),
+ Attr::CancelHidden => cursor.template.flags.remove(Flags::HIDDEN),
+ Attr::Strike => cursor.template.flags.insert(Flags::STRIKEOUT),
+ Attr::CancelStrike => cursor.template.flags.remove(Flags::STRIKEOUT),
_ => {
debug!("Term got unhandled attr: {:?}", attr);
},
@@ -2187,7 +2100,7 @@ impl<T: EventListener> Handler for Term<T> {
#[inline]
fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) {
trace!("Configuring charset {:?} as {:?}", index, charset);
- self.cursor.charsets[index] = charset;
+ self.grid.cursor.charsets[index] = charset;
}
#[inline]
@@ -2337,7 +2250,7 @@ mod tests {
mem::swap(&mut term.semantic_escape_chars, &mut escape_chars);
{
- *term.selection_mut() = Some(Selection::new(
+ term.selection = Some(Selection::new(
SelectionType::Semantic,
Point { line: 2, col: Column(1) },
Side::Left,
@@ -2346,7 +2259,7 @@ mod tests {
}
{
- *term.selection_mut() = Some(Selection::new(
+ term.selection = Some(Selection::new(
SelectionType::Semantic,
Point { line: 2, col: Column(4) },
Side::Left,
@@ -2355,7 +2268,7 @@ mod tests {
}
{
- *term.selection_mut() = Some(Selection::new(
+ term.selection = Some(Selection::new(
SelectionType::Semantic,
Point { line: 1, col: Column(1) },
Side::Left,
@@ -2385,7 +2298,7 @@ mod tests {
mem::swap(&mut term.grid, &mut grid);
- *term.selection_mut() = Some(Selection::new(
+ term.selection = Some(Selection::new(
SelectionType::Lines,
Point { line: 0, col: Column(3) },
Side::Left,
@@ -2419,7 +2332,7 @@ mod tests {
let mut selection =
Selection::new(SelectionType::Simple, Point { line: 2, col: Column(0) }, Side::Left);
selection.update(Point { line: 0, col: Column(2) }, Side::Right);
- *term.selection_mut() = Some(selection);
+ term.selection = Some(selection);
assert_eq!(term.selection_to_string(), Some("aaa\n\naaa\n".into()));
}
@@ -2471,7 +2384,7 @@ mod tests {
let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
// Add one line of scrollback.
- term.grid.scroll_up(&(Line(0)..Line(1)), Line(1), &Cell::default());
+ term.grid.scroll_up(&(Line(0)..Line(1)), Line(1), Cell::default());
// Clear the history.
term.clear_screen(ansi::ClearMode::Saved);
@@ -2505,14 +2418,14 @@ mod tests {
term.newline();
}
assert_eq!(term.grid.history_size(), 10);
- assert_eq!(term.cursor.point, Point::new(Line(9), Column(0)));
+ assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0)));
// Increase visible lines.
size.height = 30.;
term.resize(&size);
assert_eq!(term.grid.history_size(), 0);
- assert_eq!(term.cursor.point, Point::new(Line(19), Column(0)));
+ assert_eq!(term.grid.cursor.point, Point::new(Line(19), Column(0)));
}
#[test]
@@ -2533,7 +2446,7 @@ mod tests {
term.newline();
}
assert_eq!(term.grid.history_size(), 10);
- assert_eq!(term.cursor.point, Point::new(Line(9), Column(0)));
+ assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0)));
// Enter alt screen.
term.set_mode(ansi::Mode::SwapScreenAndSetRestoreCursor);
@@ -2546,7 +2459,7 @@ mod tests {
term.unset_mode(ansi::Mode::SwapScreenAndSetRestoreCursor);
assert_eq!(term.grid().history_size(), 0);
- assert_eq!(term.cursor.point, Point::new(Line(19), Column(0)));
+ assert_eq!(term.grid.cursor.point, Point::new(Line(19), Column(0)));
}
#[test]
@@ -2567,14 +2480,14 @@ mod tests {
term.newline();
}
assert_eq!(term.grid.history_size(), 10);
- assert_eq!(term.cursor.point, Point::new(Line(9), Column(0)));
+ assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0)));
// Increase visible lines.
size.height = 5.;
term.resize(&size);
assert_eq!(term.grid().history_size(), 15);
- assert_eq!(term.cursor.point, Point::new(Line(4), Column(0)));
+ assert_eq!(term.grid.cursor.point, Point::new(Line(4), Column(0)));
}
#[test]
@@ -2595,7 +2508,7 @@ mod tests {
term.newline();
}
assert_eq!(term.grid.history_size(), 10);
- assert_eq!(term.cursor.point, Point::new(Line(9), Column(0)));
+ assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0)));
// Enter alt screen.
term.set_mode(ansi::Mode::SwapScreenAndSetRestoreCursor);
@@ -2608,7 +2521,7 @@ mod tests {
term.unset_mode(ansi::Mode::SwapScreenAndSetRestoreCursor);
assert_eq!(term.grid().history_size(), 15);
- assert_eq!(term.cursor.point, Point::new(Line(4), Column(0)));
+ assert_eq!(term.grid.cursor.point, Point::new(Line(4), Column(0)));
}
#[test]
diff --git a/alacritty_terminal/src/vi_mode.rs b/alacritty_terminal/src/vi_mode.rs
index 7fa6c148..650c3a8a 100644
--- a/alacritty_terminal/src/vi_mode.rs
+++ b/alacritty_terminal/src/vi_mode.rs
@@ -385,11 +385,10 @@ fn last_occupied_in_line<T>(term: &Term<T>, line: usize) -> Option<Point<usize>>
/// Advance point based on direction.
fn advance<T>(term: &Term<T>, point: Point<usize>, left: bool) -> Point<usize> {
- let cols = term.grid().num_cols();
if left {
- point.sub_absolute(cols.0, 1)
+ point.sub_absolute(term.grid().num_cols(), 1)
} else {
- point.add_absolute(cols.0, 1)
+ point.add_absolute(term.grid().num_cols(), 1)
}
}
@@ -679,7 +678,7 @@ mod tests {
#[test]
fn scroll_semantic() {
let mut term = term();
- term.grid_mut().scroll_up(&(Line(0)..Line(20)), Line(5), &Default::default());
+ term.grid_mut().scroll_up(&(Line(0)..Line(20)), Line(5), Default::default());
let mut cursor = ViModeCursor::new(Point::new(Line(0), Column(0)));
@@ -755,7 +754,7 @@ mod tests {
#[test]
fn scroll_word() {
let mut term = term();
- term.grid_mut().scroll_up(&(Line(0)..Line(20)), Line(5), &Default::default());
+ term.grid_mut().scroll_up(&(Line(0)..Line(20)), Line(5), Default::default());
let mut cursor = ViModeCursor::new(Point::new(Line(0), Column(0)));
diff --git a/alacritty_terminal/tests/ref.rs b/alacritty_terminal/tests/ref.rs
index 99a9db54..41959b2f 100644
--- a/alacritty_terminal/tests/ref.rs
+++ b/alacritty_terminal/tests/ref.rs
@@ -107,7 +107,7 @@ fn ref_test(dir: &Path) {
// Truncate invisible lines from the grid.
let mut term_grid = terminal.grid().clone();
- term_grid.initialize_all(&Cell::default());
+ term_grid.initialize_all(Cell::default());
term_grid.truncate();
if grid != term_grid {