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
|
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) {
|
pub fn scroll_display(&mut self, scroll: Scroll) {
|
||||||
match scroll {
|
match scroll {
|
||||||
Scroll::Lines(count) => {
|
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
|
/// Increase the number of lines in the buffer
|
||||||
pub fn grow_visible_lines(&mut self, next: Line, template_row: Row<T>)
|
pub fn grow_visible_lines(&mut self, next: Line, template_row: Row<T>)
|
||||||
where
|
where
|
||||||
T: Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
// Number of lines the buffer needs to grow
|
// 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);
|
||||||
|
|
||||||
// Only grow if there are not enough lines still hidden
|
// Update visible lines
|
||||||
if lines_to_grow > (self.inner.len() - self.len) {
|
self.visible_lines = next - 1;
|
||||||
// Lines to grow additionally to invisible lines
|
}
|
||||||
let new_lines_to_grow = lines_to_grow - (self.inner.len() - self.len);
|
|
||||||
|
|
||||||
|
/// 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
|
// Get the position of the start of the buffer
|
||||||
let offset = self.zero % self.inner.len();
|
let offset = self.zero % self.inner.len();
|
||||||
|
|
||||||
|
// Only grow if there are not enough lines still hidden
|
||||||
|
let mut new_growage = 0;
|
||||||
|
if growage > (self.inner.len() - self.len) {
|
||||||
|
// Lines to grow additionally to invisible lines
|
||||||
|
new_growage = growage - (self.inner.len() - self.len);
|
||||||
|
|
||||||
// Split off the beginning of the raw inner buffer
|
// Split off the beginning of the raw inner buffer
|
||||||
let mut start_buffer = self.inner.split_off(offset);
|
let mut start_buffer = self.inner.split_off(offset);
|
||||||
|
|
||||||
// Insert new template rows at the end of the raw inner buffer
|
// 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);
|
self.inner.append(&mut new_lines);
|
||||||
|
|
||||||
// Add the start to the raw inner buffer again
|
// Add the start to the raw inner buffer again
|
||||||
self.inner.append(&mut start_buffer);
|
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
|
// Update raw buffer length and zero offset
|
||||||
self.len += lines_to_grow;
|
self.zero = offset + new_growage;
|
||||||
self.visible_lines = next - 1;
|
self.len += growage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrease the number of lines in the buffer
|
/// Decrease the number of lines in the buffer
|
||||||
pub fn shrink_visible_lines(&mut self, next: Line) {
|
pub fn shrink_visible_lines(&mut self, next: Line) {
|
||||||
// Shrink the size without removing any lines
|
// 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
|
// Update visible lines
|
||||||
self.visible_lines = next - 1;
|
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
|
// Free memory
|
||||||
if self.inner.len() > self.len() + TRUNCATE_STEP {
|
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.zero, expected.zero);
|
||||||
assert_eq!(storage.len, expected.len);
|
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.default_cursor_style = config.cursor_style();
|
||||||
self.dynamic_title = config.dynamic_title();
|
self.dynamic_title = config.dynamic_title();
|
||||||
self.auto_scroll = config.scrolling().auto_scroll;
|
self.auto_scroll = config.scrolling().auto_scroll;
|
||||||
|
self.grid
|
||||||
|
.update_history(config.scrolling().history as usize, &self.cursor.template);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -2020,17 +2022,17 @@ mod tests {
|
||||||
mem::swap(&mut term.semantic_escape_chars, &mut escape_chars);
|
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")));
|
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")));
|
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")));
|
assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue