Allow changing scrollback history size at runtime

Making use of the changes that have been introduced in #1234 and #1284,
this allows changing the size of the scrollback buffer at runtime.

This simply changes the size of the raw inner buffer making use of the
optimized mutation algorithms introduced in #1284. As a result,
shrinking the scrollback history size at runtime should be basically
free and growing will only introduce a performance cost when there are
no more buffered lines. However, as a result there will not be any
memory freed when shrinking the scrollback history size at runtime.

As discussed in #1234 a potential solution for this could be to truncate
the raw buffer whenever more than X lines are deleted, however this
issue should not be very significant PR and if a solution is desired a
separate issue/PR should be opened.

This fixes #1235.
This commit is contained in:
Christian Duerr 2018-05-30 10:20:47 +02:00 committed by Joe Wilm
parent 24c50fa0a7
commit 4631ca4db5
3 changed files with 138 additions and 17 deletions

View File

@ -155,6 +155,13 @@ impl<T: Copy + Clone> Grid<T> {
self.line_to_offset(line) + self.display_offset
}
/// Update the size of the scrollback history
pub fn update_history(&mut self, history_size: usize, template: &T)
{
self.raw.update_history(history_size, Row::new(self.cols, &template));
self.scroll_limit = min(self.scroll_limit, history_size);
}
pub fn scroll_display(&mut self, scroll: Scroll) {
match scroll {
Scroll::Lines(count) => {

View File

@ -97,48 +97,75 @@ impl<T> Storage<T> {
}
}
/// Update the size of the scrollback history
pub fn update_history(&mut self, history_size: usize, template_row: Row<T>)
where
T: Clone,
{
let current_history = self.len - (self.visible_lines.0 + 1);
if history_size > current_history {
self.grow_lines(history_size - current_history, template_row);
} else if history_size < current_history {
self.shrink_lines(current_history - history_size);
}
}
/// Increase the number of lines in the buffer
pub fn grow_visible_lines(&mut self, next: Line, template_row: Row<T>)
where
T: Clone,
{
// Number of lines the buffer needs to grow
let lines_to_grow = (next - (self.visible_lines + 1)).0;
let growage = (next - (self.visible_lines + 1)).0;
self.grow_lines(growage, template_row);
// Update visible lines
self.visible_lines = next - 1;
}
/// Grow the number of lines in the buffer, filling new lines with the template
pub fn grow_lines(&mut self, growage: usize, template_row: Row<T>)
where
T: Clone,
{
// Get the position of the start of the buffer
let offset = self.zero % self.inner.len();
// Only grow if there are not enough lines still hidden
if lines_to_grow > (self.inner.len() - self.len) {
let mut new_growage = 0;
if growage > (self.inner.len() - self.len) {
// Lines to grow additionally to invisible lines
let new_lines_to_grow = lines_to_grow - (self.inner.len() - self.len);
// Get the position of the start of the buffer
let offset = self.zero % self.inner.len();
new_growage = growage - (self.inner.len() - self.len);
// Split off the beginning of the raw inner buffer
let mut start_buffer = self.inner.split_off(offset);
// Insert new template rows at the end of the raw inner buffer
let mut new_lines = vec![template_row; new_lines_to_grow];
let mut new_lines = vec![template_row; new_growage];
self.inner.append(&mut new_lines);
// Add the start to the raw inner buffer again
self.inner.append(&mut start_buffer);
// Update the zero to after the lines we just inserted
self.zero = offset + lines_to_grow;
}
// Update visible lines and raw buffer length
self.len += lines_to_grow;
self.visible_lines = next - 1;
// Update raw buffer length and zero offset
self.zero = offset + new_growage;
self.len += growage;
}
/// Decrease the number of lines in the buffer
pub fn shrink_visible_lines(&mut self, next: Line) {
// Shrink the size without removing any lines
self.len -= (self.visible_lines - (next - 1)).0;
let shrinkage = (self.visible_lines - (next - 1)).0;
self.shrink_lines(shrinkage);
// Update visible lines
self.visible_lines = next - 1;
}
// Shrink the number of lines in the buffer
fn shrink_lines(&mut self, shrinkage: usize) {
self.len -= shrinkage;
// Free memory
if self.inner.len() > self.len() + TRUNCATE_STEP {
@ -537,3 +564,88 @@ fn truncate_invisible_lines_beginning() {
assert_eq!(storage.zero, expected.zero);
assert_eq!(storage.len, expected.len);
}
/// First shrink the buffer and then grow it again
///
/// Before:
/// 0: 4
/// 1: 5
/// 2: 0 <- Zero
/// 3: 1
/// 4: 2
/// 5: 3
/// After Shrinking:
/// 0: 4 <- Hidden
/// 1: 5 <- Hidden
/// 2: 0 <- Zero
/// 3: 1
/// 4: 2
/// 5: 3 <- Hidden
/// After Growing:
/// 0: 4
/// 1: 5
/// 2: -
/// 3: 0 <- Zero
/// 4: 1
/// 5: 2
/// 6: 3
#[test]
fn shrink_then_grow() {
// 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,
};
// Shrink buffer
storage.shrink_lines(3);
// 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'),
],
zero: 2,
visible_lines: Line(0),
len: 3,
};
assert_eq!(storage.inner, shrinking_expected.inner);
assert_eq!(storage.zero, shrinking_expected.zero);
assert_eq!(storage.len, shrinking_expected.len);
// Grow buffer
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'),
],
zero: 3,
visible_lines: Line(0),
len: 7,
};
assert_eq!(storage.inner, growing_expected.inner);
assert_eq!(storage.zero, growing_expected.zero);
assert_eq!(storage.len, growing_expected.len);
}

View File

@ -908,6 +908,8 @@ impl Term {
self.default_cursor_style = config.cursor_style();
self.dynamic_title = config.dynamic_title();
self.auto_scroll = config.scrolling().auto_scroll;
self.grid
.update_history(config.scrolling().history as usize, &self.cursor.template);
}
#[inline]
@ -2020,17 +2022,17 @@ mod tests {
mem::swap(&mut term.semantic_escape_chars, &mut escape_chars);
{
*term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(1) }, &term));
*term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(1) }));
assert_eq!(term.selection_to_string(), Some(String::from("aa")));
}
{
*term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(4) }, &term));
*term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(4) }));
assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
}
{
*term.selection_mut() = Some(Selection::semantic(Point { line: 1, col: Column(1) }, &term));
*term.selection_mut() = Some(Selection::semantic(Point { line: 1, col: Column(1) }));
assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
}
}