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.
This commit is contained in:
Christian Duerr 2018-04-22 14:39:55 +02:00 committed by Joe Wilm
parent e615d112fb
commit fe749cf0ad
2 changed files with 207 additions and 18 deletions

View File

@ -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;
}

View File

@ -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);
}