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:
parent
24c50fa0a7
commit
4631ca4db5
|
@ -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) => {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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")));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue