summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--src/cli.rs4
-rw-r--r--src/config/mod.rs6
-rw-r--r--src/event.rs2
-rw-r--r--src/grid/mod.rs134
-rw-r--r--src/grid/row.rs93
-rw-r--r--src/grid/storage.rs207
-rw-r--r--src/grid/tests.rs174
-rw-r--r--src/main.rs4
-rw-r--r--src/message_bar.rs2
-rw-r--r--src/term/cell.rs36
-rw-r--r--src/term/mod.rs14
12 files changed, 608 insertions, 70 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5aa786bb..8c8cb0a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ability to specify starting position with the `--position` flag
- New configuration field `window.position` allows specifying the starting position
- Added the ability to change the selection color
+- Text will reflow instead of truncating when resizing Alacritty
### Fixed
@@ -34,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- FreeBSD: SpawnNewInstance will now open new instances in the shell's current
working directory as long as linprocfs(5) is mounted on `/compat/linux/proc`
- Fix lingering Alacritty window after child process has exited
+- Growing the terminal while scrolled up will no longer move the content down
## Version 0.2.9
diff --git a/src/cli.rs b/src/cli.rs
index 12d3ade0..1997a6bb 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -164,8 +164,8 @@ impl Options {
}
}
- options.class = matches.value_of("class").map(|c| c.to_owned());
- options.title = matches.value_of("title").map(|t| t.to_owned());
+ options.class = matches.value_of("class").map(ToOwned::to_owned);
+ options.title = matches.value_of("title").map(ToOwned::to_owned);
match matches.occurrences_of("q") {
0 => {},
diff --git a/src/config/mod.rs b/src/config/mod.rs
index b8dd9f82..2fa60dad 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -170,7 +170,7 @@ impl Default for Url {
fn deserialize_modifiers<'a, D>(deserializer: D) -> ::std::result::Result<ModifiersState, D::Error>
where D: de::Deserializer<'a>
{
- ModsWrapper::deserialize(deserializer).map(|wrapper| wrapper.into_inner())
+ ModsWrapper::deserialize(deserializer).map(ModsWrapper::into_inner)
}
/// `VisualBellAnimations` are modeled after a subset of CSS transitions and Robert
@@ -1665,7 +1665,7 @@ impl Config {
}
None
})
- .map(|path| path.into())
+ .map(Into::into)
}
// TODO: Remove old configuration location warning (Deprecated 03/12/2018)
@@ -1810,7 +1810,7 @@ impl Config {
pub fn path(&self) -> Option<&Path> {
self.config_path
.as_ref()
- .map(|p| p.as_path())
+ .map(PathBuf::as_path)
}
pub fn shell(&self) -> Option<&Shell<'_>> {
diff --git a/src/event.rs b/src/event.rs
index f8044609..121c0c42 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -86,7 +86,7 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> {
}
fn selection_is_empty(&self) -> bool {
- self.terminal.selection().as_ref().map(|s| s.is_empty()).unwrap_or(true)
+ self.terminal.selection().as_ref().map(Selection::is_empty).unwrap_or(true)
}
fn clear_selection(&mut self) {
diff --git a/src/grid/mod.rs b/src/grid/mod.rs
index 80289cd6..837d9b0e 100644
--- a/src/grid/mod.rs
+++ b/src/grid/mod.rs
@@ -64,6 +64,12 @@ impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> {
}
}
+pub trait GridCell {
+ fn is_empty(&self) -> bool;
+ fn is_wrap(&self) -> bool;
+ fn set_wrap(&mut self, wrap: bool);
+}
+
/// Represents the terminal display contents
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Grid<T> {
@@ -123,7 +129,7 @@ pub enum ViewportPosition {
Below,
}
-impl<T: Copy + Clone> Grid<T> {
+impl<T: GridCell + Copy + Clone> Grid<T> {
pub fn new(lines: index::Line, cols: index::Column, scrollback: usize, template: T) -> Grid<T> {
let raw = Storage::with_capacity(lines, Row::new(cols, &template));
Grid {
@@ -197,6 +203,7 @@ impl<T: Copy + Clone> Grid<T> {
&mut self,
lines: index::Line,
cols: index::Column,
+ cursor_pos: &mut Point,
template: &T,
) {
// Check that there's actually work to do and return early if not
@@ -211,8 +218,8 @@ impl<T: Copy + Clone> Grid<T> {
}
match self.cols.cmp(&cols) {
- Ordering::Less => self.grow_cols(cols, template),
- Ordering::Greater => self.shrink_cols(cols),
+ Ordering::Less => self.grow_cols(cols, cursor_pos, template),
+ Ordering::Greater => self.shrink_cols(cols, cursor_pos, template),
Ordering::Equal => (),
}
}
@@ -259,20 +266,125 @@ impl<T: Copy + Clone> Grid<T> {
}
self.scroll_limit = self.scroll_limit.saturating_sub(*lines_added);
- }
+ self.display_offset = self.display_offset.saturating_sub(*lines_added);
+ }
+
+ fn grow_cols(&mut self, cols: index::Column, cursor_pos: &mut Point, template: &T) {
+ // Truncate all buffered lines
+ self.raw.grow_hidden(cols, template);
+
+ let max_lines = self.lines.0 + self.max_scroll_limit;
+
+ // Iterate backwards with indices for mutation during iteration
+ let mut i = self.raw.len();
+ while i > 0 {
+ i -= 1;
+
+ // Grow the current line if there's wrapped content available
+ while i >= 1
+ && self.raw[i].len() < cols.0
+ && self.raw[i].last().map(GridCell::is_wrap) == Some(true)
+ {
+ // Remove wrap flag before appending additional cells
+ if let Some(cell) = self.raw[i].last_mut() {
+ cell.set_wrap(false);
+ }
+
+ // Append as many cells from the next line as possible
+ let len = min(self.raw[i - 1].len(), cols.0 - self.raw[i].len());
+ let mut cells = self.raw[i - 1].front_split_off(len);
+ self.raw[i].append(&mut cells);
+
+ if self.raw[i - 1].is_empty() {
+ // Remove following line if all cells have been drained
+ self.raw.remove(i - 1);
+
+ if self.raw.len() < self.lines.0 || self.scroll_limit == 0 {
+ // Add new line and move lines up if we can't pull from history
+ self.raw.insert(0, Row::new(cols, template), max_lines);
+ cursor_pos.line = Line(cursor_pos.line.saturating_sub(1));
+ } else {
+ // Make sure viewport doesn't move if line is outside of the visible area
+ if i < self.display_offset {
+ self.display_offset = self.display_offset.saturating_sub(1);
+ }
+
+ // Remove one line from scrollback, since we just moved it to the viewport
+ self.scroll_limit = self.scroll_limit.saturating_sub(1);
+ self.display_offset = min(self.display_offset, self.scroll_limit);
+ i -= 1;
+ }
+ } else if let Some(cell) = self.raw[i].last_mut() {
+ // Set wrap flag if next line still has cells
+ cell.set_wrap(true);
+ }
+ }
- fn grow_cols(&mut self, cols: index::Column, template: &T) {
- for row in self.raw.iter_mut_raw() {
- row.grow(cols, template);
+ // Fill remaining cells
+ if self.raw[i].len() < cols.0 {
+ self.raw[i].grow(cols, template);
+ }
}
- // Update self cols
self.cols = cols;
}
- fn shrink_cols(&mut self, cols: index::Column) {
- for row in self.raw.iter_mut_raw() {
- row.shrink(cols);
+ fn shrink_cols(&mut self, cols: index::Column, cursor_pos: &mut Point, template: &T) {
+ // Truncate all buffered lines
+ self.raw.shrink_hidden(cols);
+
+ let max_lines = self.lines.0 + self.max_scroll_limit;
+
+ // Iterate backwards with indices for mutation during iteration
+ let mut i = self.raw.len();
+ while i > 0 {
+ i -= 1;
+
+ if let Some(mut new_row) = self.raw[i].shrink(cols) {
+ // Set line as wrapped if cells got removed
+ if let Some(cell) = self.raw[i].last_mut() {
+ cell.set_wrap(true);
+ }
+
+ if Some(true) == new_row.last().map(|c| c.is_wrap() && i >= 1)
+ && new_row.len() < cols.0
+ {
+ // Make sure previous wrap flag doesn't linger around
+ if let Some(cell) = new_row.last_mut() {
+ cell.set_wrap(false);
+ }
+
+ // Add removed cells to start of next row
+ self.raw[i - 1].append_front(new_row);
+ } else {
+ // Make sure viewport doesn't move if line is outside of the visible area
+ if i < self.display_offset {
+ self.display_offset = min(self.display_offset + 1, self.max_scroll_limit);
+ }
+
+ // Make sure new row is at least as long as new width
+ let occ = new_row.len();
+ if occ < cols.0 {
+ new_row.append(&mut vec![*template; cols.0 - occ]);
+ }
+ let row = Row::from_vec(new_row, occ);
+
+ // Add new row with all removed cells
+ self.raw.insert(i, row, max_lines);
+
+ if cursor_pos.line >= self.lines - 1 {
+ // Increase scrollback history
+ self.scroll_limit = min(self.scroll_limit + 1, self.max_scroll_limit);
+
+ // Since inserted might exceed cols, we need to check the same line again
+ i += 1;
+ } else {
+ // Pull content down if cursor is not at the bottom
+ self.raw.rotate(1);
+ cursor_pos.line += 1;
+ }
+ }
+ }
}
self.cols = cols;
diff --git a/src/grid/row.rs b/src/grid/row.rs
index 72c79b02..ef27f040 100644
--- a/src/grid/row.rs
+++ b/src/grid/row.rs
@@ -16,9 +16,10 @@
use std::ops::{Index, IndexMut};
use std::ops::{Range, RangeTo, RangeFrom, RangeFull, RangeToInclusive};
-use std::cmp::{max, min};
+use std::cmp::{min, max};
use std::slice;
+use crate::grid::GridCell;
use crate::index::Column;
/// A row in the grid
@@ -43,7 +44,7 @@ impl<T: PartialEq> PartialEq for Row<T> {
}
}
-impl<T: Copy + Clone> Row<T> {
+impl<T: Copy> Row<T> {
pub fn new(columns: Column, template: &T) -> Row<T> {
Row {
inner: vec![*template; *columns],
@@ -52,52 +53,102 @@ impl<T: Copy + Clone> Row<T> {
}
pub fn grow(&mut self, cols: Column, template: &T) {
- assert!(self.len() < * cols);
+ if self.inner.len() >= cols.0 {
+ return;
+ }
+
+ self.inner.append(&mut vec![*template; cols.0 - self.len()]);
+ }
+
+ pub fn shrink(&mut self, cols: Column) -> Option<Vec<T>>
+ where
+ T: GridCell
+ {
+ if self.inner.len() <= cols.0 {
+ return None;
+ }
+
+ // Split off cells for a new row
+ let mut new_row = self.inner.split_off(cols.0);
+ let index = new_row.iter().rposition(|c| !c.is_empty()).map(|i| i + 1).unwrap_or(0);
+ new_row.truncate(index);
+
+ self.occ = min(self.occ, *cols);
- while self.len() != *cols {
- self.inner.push(*template);
+ if new_row.is_empty() {
+ None
+ } else {
+ Some(new_row)
}
}
/// Resets contents to the contents of `other`
#[inline(never)]
pub fn reset(&mut self, other: &T) {
- let occ = self.occ;
- for item in &mut self.inner[..occ] {
+ for item in &mut self.inner[..self.occ] {
*item = *other;
}
-
self.occ = 0;
}
}
#[allow(clippy::len_without_is_empty)]
impl<T> Row<T> {
- pub fn shrink(&mut self, cols: Column) {
- while self.len() != *cols {
- self.inner.pop();
+ #[inline]
+ pub fn from_vec(vec: Vec<T>, occ: usize) -> Row<T> {
+ Row {
+ inner: vec,
+ occ,
}
-
- self.occ = min(self.occ, *cols);
}
+ #[inline]
pub fn len(&self) -> usize {
self.inner.len()
}
- pub fn iter(&self) -> slice::Iter<'_, T> {
- self.inner.iter()
+ #[inline]
+ pub fn last(&self) -> Option<&T> {
+ self.inner.last()
}
-}
+ #[inline]
+ pub fn last_mut(&mut self) -> Option<&mut T> {
+ self.occ = self.inner.len();
+ self.inner.last_mut()
+ }
-impl<'a, T> IntoIterator for &'a Row<T> {
- type Item = &'a T;
- type IntoIter = slice::Iter<'a, T>;
+ #[inline]
+ pub fn append(&mut self, vec: &mut Vec<T>)
+ where
+ T: GridCell
+ {
+ self.inner.append(vec);
+ self.occ = self.inner.iter().rposition(|c| !c.is_empty()).map(|i| i + 1).unwrap_or(0);
+ }
#[inline]
- fn into_iter(self) -> slice::Iter<'a, T> {
- self.iter()
+ pub fn append_front(&mut self, mut vec: Vec<T>) {
+ self.occ += vec.len();
+ vec.append(&mut self.inner);
+ self.inner = vec;
+ }
+
+ #[inline]
+ pub fn is_empty(&self) -> bool
+ where
+ T: GridCell
+ {
+ self.inner.iter().all(|c| c.is_empty())
+ }
+
+ #[inline]
+ pub fn front_split_off(&mut self, at: usize) -> Vec<T> {
+ self.occ = self.occ.saturating_sub(at);
+
+ let mut split = self.inner.split_off(at);
+ std::mem::swap(&mut split, &mut self.inner);
+ split
}
}
diff --git a/src/grid/storage.rs b/src/grid/storage.rs
index 87a129d0..6a119ead 100644
--- a/src/grid/storage.rs
+++ b/src/grid/storage.rs
@@ -12,11 +12,11 @@
/// implementation is provided. Anything from Vec that should be exposed must be
/// done so manually.
use std::ops::{Index, IndexMut};
-use std::slice;
use static_assertions::assert_eq_size;
-use crate::index::Line;
+use crate::index::{Column, Line};
+use crate::grid::GridCell;
use super::Row;
/// Maximum number of invisible lines before buffer is resized
@@ -196,6 +196,7 @@ impl<T> Storage<T> {
self.len
}
+ #[inline]
/// Compute actual index in underlying storage given the requested index.
fn compute_index(&self, requested: usize) -> usize {
debug_assert!(requested < self.len);
@@ -250,18 +251,7 @@ impl<T> Storage<T> {
}
}
- /// Iterate over *all* entries in the underlying buffer
- ///
- /// This includes hidden entries.
- ///
- /// XXX This suggests that Storage is a leaky abstraction. Ultimately, this
- /// is needed because of the grow lines functionality implemented on
- /// this type, and maybe that's where the leak is necessitating this
- /// accessor.
- pub fn iter_mut_raw<'a>(&'a mut self) -> slice::IterMut<'a, Row<T>> {
- self.inner.iter_mut()
- }
-
+ #[inline]
pub fn rotate(&mut self, count: isize) {
debug_assert!(count.abs() as usize <= self.inner.len());
@@ -270,9 +260,75 @@ impl<T> Storage<T> {
}
// Fast path
+ #[inline]
pub fn rotate_up(&mut self, count: usize) {
self.zero = (self.zero + count) % self.inner.len();
}
+
+ #[inline]
+ pub fn insert(&mut self, index: usize, row: Row<T>, max_lines: usize) {
+ let index = self.compute_index(index);
+ self.inner.insert(index, row);
+
+ if index < self.zero {
+ self.zero += 1;
+ }
+
+ if self.len < max_lines {
+ self.len += 1;
+ }
+ }
+
+ #[inline]
+ pub fn remove(&mut self, index: usize) -> Row<T> {
+ let index = self.compute_index(index);
+ if index < self.zero {
+ self.zero -= 1;
+ }
+ self.len -= 1;
+
+ self.inner.remove(index)
+ }
+
+ /// Shrink columns of hidden buffered lines.
+ ///
+ /// XXX This suggests that Storage is a leaky abstraction. Ultimately, this
+ /// is needed because of the grow/shrink lines functionality.
+ #[inline]
+ pub fn shrink_hidden(&mut self, cols: Column)
+ where
+ T: GridCell + Copy
+ {
+ let start = self.zero + self.len;
+ let end = self.zero + self.inner.len();
+ for mut i in start..end {
+ if i >= self.inner.len() {
+ i -= self.inner.len();
+ }
+
+ self.inner[i].shrink(cols);
+ }
+ }
+
+ /// Grow columns of hidden buffered lines.
+ ///
+ /// XXX This suggests that Storage is a leaky abstraction. Ultimately, this
+ /// is needed because of the grow/shrink lines functionality.
+ #[inline]
+ pub fn grow_hidden(&mut self, cols: Column, template: &T)
+ where
+ T: Copy + Clone
+ {
+ let start = self.zero + self.len;
+ let end = self.zero + self.inner.len();
+ for mut i in start..end {
+ if i >= self.inner.len() {
+ i -= self.inner.len();
+ }
+
+ self.inner[i].grow(cols, template);
+ }
+ }
}
impl<T> Index<usize> for Storage<T> {
@@ -308,9 +364,6 @@ impl<T> IndexMut<Line> for Storage<T> {
}
}
-#[cfg(test)]
-use crate::index::Column;
-
/// Grow the buffer one line at the end of the buffer
///
/// Before:
@@ -693,3 +746,123 @@ fn initialize() {
assert_eq!(storage.zero, shrinking_expected.zero);
assert_eq!(storage.len, shrinking_expected.len);
}
+
+#[test]
+fn insert() {
+ // Setup storage area
+ let mut storage = Storage {
+ inner: vec![
+ Row::new(Column(1), &'4'),
+ Row::new(Column(1), &'5'),
+ Row::new(Column(1), &'0'),
+ Row::new(Column(1), &'1'),
+ Row::new(Column(1), &'2'),
+ Row::new(Column(1), &'3'),
+ ],
+ zero: 2,
+ visible_lines: Line(0),
+ len: 6,
+ };
+
+ // Initialize additional lines
+ storage.insert(2, Row::new(Column(1), &'-'), 100);
+
+ // Make sure the lines are present and at the right location
+ let shrinking_expected = Storage {
+ inner: vec![
+ Row::new(Column(1), &'4'),
+ Row::new(Column(1), &'5'),
+ Row::new(Column(1), &'0'),
+ Row::new(Column(1), &'1'),
+ Row::new(Column(1), &'-'),
+ Row::new(Column(1), &'2'),
+ Row::new(Column(1), &'3'),
+ ],
+ zero: 2,
+ visible_lines: Line(0),
+ len: 7,
+ };
+ assert_eq!(storage.inner, shrinking_expected.inner);
+ assert_eq!(storage.zero, shrinking_expected.zero);
+ assert_eq!(storage.len, shrinking_expected.len);
+}
+
+#[test]
+fn insert_truncate_max() {
+ // Setup storage area
+ let mut storage = Storage {
+ inner: vec![
+ Row::new(Column(1), &'4'),
+ Row::new(Column(1), &'5'),
+ Row::new(Column(1), &'0'),
+ Row::new(Column(1), &'1'),
+ Row::new(Column(1), &'2'),
+ Row::new(Column(1), &'3'),
+ ],
+ zero: 2,
+ visible_lines: Line(0),
+ len: 6,
+ };
+
+ // Initialize additional lines
+ storage.insert(2, Row::new(Column(1), &'-'), 6);
+
+ // Make sure the lines are present and at the right location
+ let shrinking_expected = Storage {
+ inner: vec![
+ Row::new(Column(1), &'4'),
+ Row::new(Column(1), &'5'),
+ Row::new(Column(1), &'0'),
+ Row::new(Column(1), &'1'),
+ Row::new(Column(1), &'-'),
+ Row::new(Column(1), &'2'),
+ Row::new(Column(1), &'3'),
+ ],
+ zero: 2,
+ visible_lines: Line(0),
+ len: 6,
+ };
+ assert_eq!(storage.inner, shrinking_expected.inner);
+ assert_eq!(storage.zero, shrinking_expected.zero);
+ assert_eq!(storage.len, shrinking_expected.len);
+}
+
+#[test]
+fn insert_at_zero() {
+ // Setup storage area
+ let mut storage = Storage {
+ inner: vec![
+ Row::new(Column(1), &'4'),
+ Row::new(Column(1), &'5'),
+ Row::new(Column(1), &'0'),
+ Row::new(Column(1), &'1'),
+ Row::new(Column(1), &'2'),
+ Row::new(Column(1), &'3'),
+ ],
+ zero: 2,
+ visible_lines: Line(0),
+ len: 6,
+ };
+
+ // Initialize additional lines
+ storage.insert(0, Row::new(Column(1), &'-'), 6);
+
+ // Make sure the lines are present and at the right location
+ let shrinking_expected = Storage {
+ inner: vec![
+ Row::new(Column(1), &'4'),
+ Row::new(Column(1), &'5'),
+ Row::new(Column(1), &'-'),
+ Row::new(Column(1), &'0'),
+ Row::new(Column(1), &'1'),
+ Row::new(Column(1), &'2'),
+ Row::new(Column(1), &'3'),
+ ],
+ zero: 2,
+ visible_lines: Line(0),
+ len: 6,
+ };
+ assert_eq!(storage.inner, shrinking_expected.inner);
+ assert_eq!(storage.zero, shrinking_expected.zero);
+ assert_eq!(storage.len, shrinking_expected.len);
+}
diff --git a/src/grid/tests.rs b/src/grid/tests.rs
index 560bb0fe..82edda69 100644
--- a/src/grid/tests.rs
+++ b/src/grid/tests.rs
@@ -16,6 +16,20 @@
use super::{Grid, BidirectionalIterator};
use crate::index::{Point, Line, Column};
+use crate::term::cell::{Cell, Flags};
+use crate::grid::GridCell;
+
+impl GridCell for usize {
+ fn is_empty(&self) -> bool {
+ false
+ }
+
+ fn is_wrap(&self) -> bool {
+ false
+ }
+
+ fn set_wrap(&mut self, _wrap: bool) {}
+}
// Scroll up moves lines upwards
#[test]
@@ -123,3 +137,163 @@ fn test_iter() {
assert_eq!(None, final_iter.next());
assert_eq!(Some(&23), final_iter.prev());
}
+
+#[test]
+fn shrink_reflow() {
+ let mut grid = Grid::new(Line(1), Column(5), 2, cell('x'));
+ grid[Line(0)][Column(0)] = cell('1');
+ grid[Line(0)][Column(1)] = cell('2');
+ grid[Line(0)][Column(2)] = cell('3');
+ grid[Line(0)][Column(3)] = cell('4');
+ grid[Line(0)][Column(4)] = cell('5');
+
+ grid.resize(Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
+
+ assert_eq!(grid.len(), 3);
+
+ assert_eq!(grid[2].len(), 2);
+ assert_eq!(grid[2][Column(0)], cell('1'));
+ assert_eq!(grid[2][Column(1)], wrap_cell('2'));
+
+ assert_eq!(grid[1].len(), 2);
+ assert_eq!(grid[1][Column(0)], cell('3'));
+ assert_eq!(grid[1][Column(1)], wrap_cell('4'));
+
+ assert_eq!(grid[0].len(), 2);
+ assert_eq!(grid[0][Column(0)], cell('5'));
+ assert_eq!(grid[0][Column(1)], Cell::default());
+}
+
+#[test]
+fn shrink_reflow_twice() {
+ let mut grid = Grid::new(Line(1), Column(5), 2, cell('x'));
+ grid[Line(0)][Column(0)] = cell('1');
+ grid[Line(0)][Column(1)] = cell('2');
+ grid[Line(0)][Column(2)] = cell('3');
+ grid[Line(0)][Column(3)] = cell('4');
+ grid[Line(0)][Column(4)] = cell('5');
+
+ grid.resize(Line(1), Column(4), &mut Point::new(Line(0), Column(0)), &Cell::default());
+ grid.resize(Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
+
+ assert_eq!(grid.len(), 3);
+
+ assert_eq!(grid[2].len(), 2);
+ assert_eq!(grid[2][Column(0)], cell('1'));
+ assert_eq!(grid[2][Column(1)], wrap_cell('2'));
+
+ assert_eq!(grid[1].len(), 2);
+ assert_eq!(grid[1][Column(0)], cell('3'));
+ assert_eq!(grid[1][Column(1)], wrap_cell('4'));
+
+ assert_eq!(grid[0].len(), 2);
+ assert_eq!(grid[0][Column(0)], cell('5'));
+ assert_eq!(grid[0][Column(1)], Cell::default());
+}
+
+#[test]
+fn shrink_reflow_empty_cell_inside_line() {
+ let mut grid = Grid::new(Line(1), Column(5), 3, cell('x'));
+ grid[Line(0)][Column(0)] = cell('1');
+ grid[Line(0)][Column(1)] = Cell::default();
+ grid[Line(0)][Column(2)] = cell('3');
+ grid[Line(0)][Column(3)] = cell('4');
+ grid[Line(0)][Column(4)] = Cell::default();
+
+ grid.resize(Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
+
+ assert_eq!(grid.len(), 2);
+
+ assert_eq!(grid[1].len(), 2);
+ assert_eq!(grid[1][Column(0)], cell('1'));
+ assert_eq!(grid[1][Column(1)], wrap_cell(' '));
+
+ assert_eq!(grid[0].len(), 2);
+ assert_eq!(grid[0][Column(0)], cell('3'));
+ assert_eq!(grid[0][Column(1)], cell('4'));
+
+ grid.resize(Line(1), Column(1), &mut Point::new(Line(0), Column(0)), &Cell::default());
+
+ assert_eq!(grid.len(), 4);
+
+ assert_eq!(grid[3].len(), 1);
+ assert_eq!(grid[3][Column(0)], wrap_cell('1'));
+
+ assert_eq!(grid[2].len(), 1);
+ assert_eq!(grid[2][Column(0)], wrap_cell(' '));
+
+ assert_eq!(grid[1].len(), 1);
+ assert_eq!(grid[1][Column(0)], wrap_cell('3'));
+
+ assert_eq!(grid[0].len(), 1);
+ assert_eq!(grid[0][Column(0)], cell('4'));
+}
+
+#[test]
+fn grow_reflow() {
+ let mut grid = Grid::new(Line(2), Column(2), 0, cell('x'));
+ grid[Line(0)][Column(0)] = cell('1');
+ grid[Line(0)][Column(1)] = wrap_cell('2');
+ grid[Line(1)][Column(0)] = cell('3');
+ grid[Line(1)][Column(1)] = Cell::default();
+
+ grid.resize(Line(2), Column(3), &mut Point::new(Line(0), Column(0)), &Cell::default());
+
+ assert_eq!(grid.len(), 2);
+
+ assert_eq!(grid[1].len(), 3);
+ assert_eq!(grid[1][Column(0)], cell('1'));
+ assert_eq!(grid[1][Column(1)], cell('2'));
+ assert_eq!(grid[1][Column(2)], cell('3'));
+
+ // Make sure rest of grid is empty
+ assert_eq!(grid[0].len(), 3);
+ assert_eq!(grid[0][Column(0)], Cell::default());
+ assert_eq!(grid[0][Column(1)], Cell::default());
+ assert_eq!(grid[0][Column(2)], Cell::default());
+}
+
+#[test]
+fn grow_reflow_multiline() {
+ let mut grid = Grid::new(Line(3), Column(2), 0, cell('x'));
+ grid[Line(0)][Column(0)] = cell('1');
+ grid[Line(0)][Column(1)] = wrap_cell('2');
+ grid[Line(1)][Column(0)] = cell('3');
+ grid[Line(1)][Column(1)] = wrap_cell('4');
+ grid[Line(2)][Column(0)] = cell('5');
+ grid[Line(2)][Column(1)] = cell('6');
+
+ grid.resize(Line(3), Column(6), &mut Point::new(Line(0), Column(0)), &Cell::default());
+
+ assert_eq!(grid.len(), 3);
+
+ assert_eq!(grid[2].len(), 6);
+ assert_eq!(grid[2][Column(0)], cell('1'));
+ assert_eq!(grid[2][Column(1)], cell('2'));
+ assert_eq!(grid[2][Column(2)], cell('3'));
+ assert_eq!(grid[2][Column(3)], cell('4'));
+ assert_eq!(grid[2][Column(4)], cell('5'));
+ assert_eq!(grid[2][Column(5)], cell('6'));
+
+ // Make sure rest of grid is empty
+ // https://github.com/rust-lang/rust-clippy/issues/3788
+ #[allow(clippy::needless_range_loop)]
+ for r in 0..2 {
+ assert_eq!(grid[r].len(), 6);
+ for c in 0..6 {
+ assert_eq!(grid[r][Column(c)], Cell::default());
+ }
+ }
+}
+
+fn cell(c: char) -> Cell {
+ let mut cell = Cell::default();
+ cell.c = c;
+ cell
+}
+
+fn wrap_cell(c: char) -> Cell {
+ let mut cell = cell(c);
+ cell.flags.insert(Flags::WRAPLINE);
+ cell
+}
diff --git a/src/main.rs b/src/main.rs
index 4f569cf5..d5142dbe 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -45,7 +45,7 @@ use std::os::unix::io::AsRawFd;
#[cfg(target_os = "macos")]
use alacritty::locale;
use alacritty::{cli, event, die};
-use alacritty::config::{self, Config};
+use alacritty::config::{self, Config, Monitor};
use alacritty::display::Display;
use alacritty::event_loop::{self, EventLoop, Msg};
use alacritty::logging;
@@ -221,7 +221,7 @@ fn run(
let mut terminal_lock = processor.process_events(&terminal, display.window());
// Handle config reloads
- if let Some(ref path) = config_monitor.as_ref().and_then(|monitor| monitor.pending()) {
+ if let Some(ref path) = config_monitor.as_ref().and_then(Monitor::pending) {
// Clear old config messages from bar
terminal_lock.message_buffer_mut().remove_topic(config::SOURCE_FILE_PATH);
diff --git a/src/message_bar.rs b/src/message_bar.rs
index bbd705aa..d7f27e7e 100644
--- a/src/message_bar.rs
+++ b/src/message_bar.rs
@@ -190,7 +190,7 @@ impl MessageBuffer {
.messages
.try_iter()
.take(self.messages.len())
- .filter(|m| m.topic().map(|s| s.as_str()) != Some(topic))
+ .filter(|m| m.topic().map(String::as_str) != Some(topic))
{
let _ = self.tx.send(msg);
}
diff --git a/src/term/cell.rs b/src/term/cell.rs
index 5d3b7036..88f9d7a1 100644
--- a/src/term/cell.rs
+++ b/src/term/cell.rs
@@ -14,7 +14,7 @@
use bitflags::bitflags;
use crate::ansi::{NamedColor, Color};
-use crate::grid;
+use crate::grid::{self, GridCell};
use crate::index::Column;
// Maximum number of zerowidth characters which will be stored per cell.
@@ -62,6 +62,32 @@ impl Default for Cell {
}
+impl GridCell for Cell {
+ #[inline]
+ fn is_empty(&self) -> bool {
+ (self.c == ' ' || self.c == '\t')
+ && self.extra[0] == ' '
+ && self.bg == Color::Named(NamedColor::Background)
+ && !self
+ .flags
+ .intersects(Flags::INVERSE | Flags::UNDERLINE | Flags::STRIKEOUT | Flags::WRAPLINE)
+ }
+
+ #[inline]
+ fn is_wrap(&self) -> bool {
+ self.flags.contains(Flags::WRAPLINE)
+ }
+
+ #[inline]
+ fn set_wrap(&mut self, wrap: bool) {
+ if wrap {
+ self.flags.insert(Flags::WRAPLINE);
+ } else {
+ self.flags.remove(Flags::WRAPLINE);
+ }
+ }
+}
+
/// Get the length of occupied cells in a line
pub trait LineLength {
/// Calculate the occupied line length
@@ -114,14 +140,6 @@ impl Cell {
}
#[inline]
- pub fn is_empty(&self) -> bool {
- (self.c == ' ' || self.c == '\t')
- && self.extra[0] == ' '
- && self.bg == Color::Named(NamedColor::Background)
- && !self.flags.intersects(Flags::INVERSE | Flags::UNDERLINE | Flags::STRIKEOUT)
- }
-
- #[inline]
pub fn reset(&mut self, template: &Cell) {
// memcpy template to self
*self = *template;
diff --git a/src/term/mod.rs b/src/term/mod.rs
index f48ad699..f2c0b18b 100644
--- a/src/term/mod.rs
+++ b/src/term/mod.rs
@@ -23,7 +23,10 @@ use unicode_width::UnicodeWidthChar;
use font::{self, Size};
use crate::ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset, CursorStyle};
-use crate::grid::{BidirectionalIterator, Grid, Indexed, IndexRegion, DisplayIter, Scroll, ViewportPosition};
+use crate::grid::{
+ BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll,
+ ViewportPosition,
+};
use crate::index::{self, Point, Column, Line, IndexRange, Contains, RangeInclusive, Linear};
use crate::selection::{self, Selection, Locations};
use crate::config::{Config, VisualBellAnimation};
@@ -1246,8 +1249,13 @@ impl Term {
debug!("New num_cols is {} and num_lines is {}", num_cols, num_lines);
// Resize grids to new size
- self.grid.resize(num_lines, num_cols, &Cell::default());
- self.alt_grid.resize(num_lines, num_cols, &Cell::default());
+ let alt_cursor_point = if self.mode.contains(TermMode::ALT_SCREEN) {
+ &mut self.cursor_save.point
+ } else {
+ &mut self.cursor_save_alt.point
+ };
+ self.grid.resize(num_lines, num_cols, &mut self.cursor.point, &Cell::default());
+ self.alt_grid.resize(num_lines, num_cols, alt_cursor_point, &Cell::default());
// Reset scrolling region to new size
self.scroll_region = Line(0)..self.grid.num_lines();