summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2018-04-22 14:39:55 +0200
committerJoe Wilm <jwilm@users.noreply.github.com>2018-04-24 09:59:52 -0700
commit298c5694a3c1259520b30e9d07a2dfb6a5a684d2 (patch)
tree1872fe402a683d609328d2308390041344176a1e
parente99e40059dd6527568279f885d682dcf0568d620 (diff)
Fix order of lines after resize
There was an issue where the lines would be messed up when the terminal was resized, this was because lines were just added/removed at the end of the buffer instead of the actual end of the terminal (since the end of the terminal might be in the middle of the buffer). This has been fixed by relying on `self.zero` to determine the position of the start of the terminal and then calculating where lines have to be inserted/removed. Some tests have also been added with documentation that should make it a little easier to understand how the process works and how the raw buffer is layed out. This should all work no matter how big the scrollback history and even when the currenty viewport is not at the bottom of the terminal output.
-rw-r--r--src/grid/mod.rs11
-rw-r--r--src/grid/storage.rs214
2 files changed, 207 insertions, 18 deletions
diff --git a/src/grid/mod.rs b/src/grid/mod.rs
index e42e5cfa..bc288e3b 100644
--- a/src/grid/mod.rs
+++ b/src/grid/mod.rs
@@ -240,16 +240,9 @@ impl<T: Copy + Clone> Grid<T> {
let lines_added = new_line_count - self.lines;
// Need to "resize" before updating buffer
- self.raw.set_visible_lines(new_line_count);
+ self.raw.grow_visible_lines(new_line_count, Row::new(self.cols, template));
self.lines = new_line_count;
- // Fill up the history with empty lines
- if self.raw.len() < self.raw.capacity() {
- for _ in self.raw.len()..self.raw.capacity() {
- self.raw.push(Row::new(self.cols, &template));
- }
- }
-
// Add new lines to bottom
self.scroll_up(&(Line(0)..new_line_count), lines_added, template);
@@ -277,7 +270,7 @@ impl<T: Copy + Clone> Grid<T> {
self.selection = None;
self.raw.rotate(*prev as isize - *target as isize);
- self.raw.set_visible_lines(target);
+ self.raw.shrink_visible_lines(target);
self.lines = target;
}
diff --git a/src/grid/storage.rs b/src/grid/storage.rs
index cc32d6d1..f6fcc8f3 100644
--- a/src/grid/storage.rs
+++ b/src/grid/storage.rs
@@ -47,15 +47,49 @@ impl<T> Storage<T> {
self.inner.capacity()
}
- pub fn set_visible_lines(&mut self, next: Line) {
- // Change capacity to fit scrollback + screen size
- if next > self.visible_lines + 1 {
- self.inner.reserve_exact((next - (self.visible_lines + 1)).0);
- } else if next < self.visible_lines + 1 {
- let shrinkage = (self.visible_lines + 1 - next).0;
- let new_size = self.inner.capacity() - shrinkage;
- self.inner.truncate(new_size);
- self.inner.shrink_to_fit();
+ /// Increase the number of lines in the buffer
+ pub fn grow_visible_lines(&mut self, next: Line, template_row: T)
+ where
+ T: Clone,
+ {
+ // Calculate insert position (before the first line)
+ let offset = self.zero % self.inner.len();
+
+ // Insert new template row for every line grown
+ let lines_to_grow = (next - (self.visible_lines + 1)).0;
+ for _ in 0..lines_to_grow {
+ self.inner.insert(offset, template_row.clone());
+ }
+
+ // Set zero to old zero + lines grown
+ self.zero = offset + lines_to_grow;
+
+ // Update visible lines
+ self.visible_lines = next - 1;
+ }
+
+ /// Decrease the number of lines in the buffer
+ pub fn shrink_visible_lines(&mut self, next: Line) {
+ // Calculate shrinkage and last line of buffer
+ let shrinkage = (self.visible_lines + 1 - next).0;
+ let offset = (self.zero + self.inner.len() - 1) % self.inner.len();
+
+ // Generate range of lines that have to be deleted before the zero line
+ let start = offset.saturating_sub(shrinkage - 1);
+ let shrink_before = start..=offset;
+
+ // Generate range of lines that have to be deleted after the zero line
+ let shrink_after = (self.inner.len() + offset + 1 - shrinkage)..self.inner.len();
+
+ // Delete all lines in reverse order
+ for i in shrink_before.chain(shrink_after).rev() {
+ self.inner.remove(i);
+ }
+
+ // Check if zero has moved (not the first line in the buffer)
+ if self.zero % (self.inner.len() + shrinkage) != 0 {
+ // Set zero to the first deleted line in the buffer
+ self.zero = start;
}
// Update visible lines
@@ -159,3 +193,165 @@ impl<'a, T: 'a> Iterator for IterMut<'a, T> {
}
}
}
+
+/// Grow the buffer one line at the end of the buffer
+///
+/// Before:
+/// 0: 0 <- Zero
+/// 1: 1
+/// 2: -
+/// After:
+/// 0: -
+/// 1: 0 <- Zero
+/// 2: 1
+/// 3: -
+#[test]
+fn grow_after_zero() {
+ // Setup storage area
+ let mut storage = Storage {
+ inner: vec!["0", "1", "-"],
+ zero: 0,
+ visible_lines: Line(2),
+ };
+
+ // Grow buffer
+ storage.grow_visible_lines(Line(4), "-");
+
+ // Make sure the result is correct
+ let expected = Storage {
+ inner: vec!["-", "0", "1", "-"],
+ zero: 1,
+ visible_lines: Line(0),
+ };
+ assert_eq!(storage.inner, expected.inner);
+ assert_eq!(storage.zero, expected.zero);
+}
+
+/// Grow the buffer one line at the start of the buffer
+///
+/// Before:
+/// 0: -
+/// 1: 0 <- Zero
+/// 2: 1
+/// After:
+/// 0: -
+/// 1: -
+/// 2: 0 <- Zero
+/// 3: 1
+#[test]
+fn grow_before_zero() {
+ // Setup storage area
+ let mut storage = Storage {
+ inner: vec!["-", "0", "1"],
+ zero: 1,
+ visible_lines: Line(2),
+ };
+
+ // Grow buffer
+ storage.grow_visible_lines(Line(4), "-");
+
+ // Make sure the result is correct
+ let expected = Storage {
+ inner: vec!["-", "-", "0", "1"],
+ zero: 2,
+ visible_lines: Line(0),
+ };
+ assert_eq!(storage.inner, expected.inner);
+ assert_eq!(storage.zero, expected.zero);
+}
+
+/// Shrink the buffer one line at the start of the buffer
+///
+/// Before:
+/// 0: 2
+/// 1: 0 <- Zero
+/// 2: 1
+/// After:
+/// 0: 0 <- Zero
+/// 1: 1
+#[test]
+fn shrink_before_zero() {
+ // Setup storage area
+ let mut storage = Storage {
+ inner: vec!["2", "0", "1"],
+ zero: 1,
+ visible_lines: Line(2),
+ };
+
+ // Shrink buffer
+ storage.shrink_visible_lines(Line(2));
+
+ // Make sure the result is correct
+ let expected = Storage {
+ inner: vec!["0", "1"],
+ zero: 0,
+ visible_lines: Line(0),
+ };
+ assert_eq!(storage.inner, expected.inner);
+ assert_eq!(storage.zero, expected.zero);
+}
+
+/// Shrink the buffer one line at the end of the buffer
+///
+/// Before:
+/// 0: 0 <- Zero
+/// 1: 1
+/// 2: 2
+/// After:
+/// 0: 0 <- Zero
+/// 1: 1
+#[test]
+fn shrink_after_zero() {
+ // Setup storage area
+ let mut storage = Storage {
+ inner: vec!["0", "1", "2"],
+ zero: 0,
+ visible_lines: Line(2),
+ };
+
+ // Shrink buffer
+ storage.shrink_visible_lines(Line(2));
+
+ // Make sure the result is correct
+ let expected = Storage {
+ inner: vec!["0", "1"],
+ zero: 0,
+ visible_lines: Line(0),
+ };
+ assert_eq!(storage.inner, expected.inner);
+ assert_eq!(storage.zero, expected.zero);
+}
+
+/// Shrink the buffer at the start and end of the buffer
+///
+/// Before:
+/// 0: 4
+/// 1: 5
+/// 2: 0 <- Zero
+/// 3: 1
+/// 4: 2
+/// 5: 3
+/// After:
+/// 0: 0 <- Zero
+/// 1: 1
+#[test]
+fn shrink_before_and_after_zero() {
+ // Setup storage area
+ let mut storage = Storage {
+ inner: vec!["4", "5", "0", "1", "2", "3"],
+ zero: 2,
+ visible_lines: Line(5),
+ };
+
+ // Shrink buffer
+ storage.shrink_visible_lines(Line(2));
+
+ // Make sure the result is correct
+ let expected = Storage {
+ inner: vec!["0", "1"],
+ zero: 0,
+ visible_lines: Line(0),
+ };
+ assert_eq!(storage.inner, expected.inner);
+ assert_eq!(storage.zero, expected.zero);
+}