diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs index 05ba64d1..c094701f 100644 --- a/zellij-server/src/panes/grid.rs +++ b/zellij-server/src/panes/grid.rs @@ -106,7 +106,7 @@ fn transfer_rows_from_lines_above_to_viewport( viewport: &mut Vec, count: usize, max_viewport_width: usize, -) { +) -> usize { let mut next_lines: Vec = vec![]; let mut lines_added_to_viewport: isize = 0; loop { @@ -137,6 +137,10 @@ fn transfer_rows_from_lines_above_to_viewport( let excess_row = Row::from_rows(next_lines, 0); bounded_push(lines_above, excess_row); } + match usize::try_from(lines_added_to_viewport) { + Ok(n) => n, + _ => 0, + } } fn transfer_rows_from_viewport_to_lines_above( @@ -144,12 +148,15 @@ fn transfer_rows_from_viewport_to_lines_above( lines_above: &mut VecDeque, count: usize, max_viewport_width: usize, -) { +) -> isize { let mut next_lines: Vec = vec![]; + let mut transferred_rows_count: isize = 0; for _ in 0..count { if next_lines.is_empty() { if !viewport.is_empty() { let next_line = viewport.remove(0); + transferred_rows_count += + calculate_row_display_height(next_line.width(), max_viewport_width) as isize; if !next_line.is_canonical { let mut bottom_canonical_row_and_wraps_in_dst = get_lines_above_bottom_canonical_row_and_wraps(lines_above); @@ -161,7 +168,11 @@ fn transfer_rows_from_viewport_to_lines_above( break; // no more rows } } - bounded_push(lines_above, next_lines.remove(0)); + let dropped_line_width = bounded_push(lines_above, next_lines.remove(0)); + if let Some(width) = dropped_line_width { + transferred_rows_count -= + calculate_row_display_height(width, max_viewport_width) as isize; + } } if !next_lines.is_empty() { let excess_rows = Row::from_rows(next_lines, max_viewport_width) @@ -170,6 +181,7 @@ fn transfer_rows_from_viewport_to_lines_above( viewport.insert(0, row); } } + transferred_rows_count } fn transfer_rows_from_lines_below_to_viewport( @@ -212,11 +224,16 @@ fn transfer_rows_from_lines_below_to_viewport( } } -fn bounded_push(vec: &mut VecDeque, value: Row) { +fn bounded_push(vec: &mut VecDeque, value: Row) -> Option { + let mut dropped_line_width = None; if vec.len() >= SCROLL_BACK { - vec.pop_front(); + let line = vec.pop_front(); + if let Some(line) = line { + dropped_line_width = Some(line.width()); + } } - vec.push_back(value) + vec.push_back(value); + dropped_line_width } pub fn create_horizontal_tabstops(columns: usize) -> BTreeSet { @@ -232,6 +249,21 @@ pub fn create_horizontal_tabstops(columns: usize) -> BTreeSet { horizontal_tabstops } +fn calculate_row_display_height(row_width: usize, viewport_width: usize) -> usize { + if row_width <= viewport_width { + return 1; + } + (row_width as f64 / viewport_width as f64).ceil() as usize +} + +fn subtract_isize_from_usize(u: usize, i: isize) -> usize { + if i.is_negative() { + u - i.abs() as usize + } else { + u + i as usize + } +} + #[derive(Debug)] pub struct CharacterChunk { pub terminal_characters: Vec, @@ -362,6 +394,7 @@ pub struct Grid { pub selection: Selection, pub title: Option, pub is_scrolled: bool, + scrollback_buffer_lines: usize, } impl Debug for Grid { @@ -407,6 +440,7 @@ impl Grid { title: None, changed_colors: None, is_scrolled: false, + scrollback_buffer_lines: 0, } } pub fn render_full_viewport(&mut self) { @@ -453,23 +487,28 @@ impl Grid { pub fn cursor_shape(&self) -> CursorShape { self.cursor.get_shape() } - pub fn scrollback_position_and_length(&self) -> (usize, usize) { + pub fn scrollback_position_and_length(&mut self) -> (usize, usize) { // (position, length) + ( + self.lines_below.len(), + (self.scrollback_buffer_lines + self.lines_below.len()), + ) + } + + fn recalculate_scrollback_buffer_count(&self) -> usize { let mut scrollback_buffer_count = 0; for row in self.lines_above.iter() { let row_width = row.width(); // rows in lines_above are unwrapped, so we need to account for that if row_width > self.width { - scrollback_buffer_count += (row_width as f64 / self.width as f64).ceil() as usize; + scrollback_buffer_count += calculate_row_display_height(row_width, self.width); } else { scrollback_buffer_count += 1; } } - ( - self.lines_below.len(), - (scrollback_buffer_count + self.lines_below.len()), - ) + scrollback_buffer_count } + fn set_horizontal_tabstop(&mut self) { self.horizontal_tabstops.insert(self.cursor.x); } @@ -543,12 +582,15 @@ impl Grid { let line_to_push_down = self.viewport.pop().unwrap(); self.lines_below.insert(0, line_to_push_down); - transfer_rows_from_lines_above_to_viewport( + let transferred_rows_height = transfer_rows_from_lines_above_to_viewport( &mut self.lines_above, &mut self.viewport, 1, self.width, ); + self.scrollback_buffer_lines = self + .scrollback_buffer_lines + .saturating_sub(transferred_rows_height); self.selection.move_down(1); } @@ -557,12 +599,25 @@ impl Grid { pub fn scroll_down_one_line(&mut self) { if !self.lines_below.is_empty() && self.viewport.len() == self.height { let mut line_to_push_up = self.viewport.remove(0); - if line_to_push_up.is_canonical { - bounded_push(&mut self.lines_above, line_to_push_up); + + self.scrollback_buffer_lines += + calculate_row_display_height(line_to_push_up.width(), self.width); + + let line_to_push_up = if line_to_push_up.is_canonical { + line_to_push_up } else { let mut last_line_above = self.lines_above.pop_back().unwrap(); last_line_above.append(&mut line_to_push_up.columns); - bounded_push(&mut self.lines_above, last_line_above); + last_line_above + }; + + let dropped_line_width = bounded_push(&mut self.lines_above, line_to_push_up); + if let Some(width) = dropped_line_width { + let dropped_line_height = calculate_row_display_height(width, self.width); + + self.scrollback_buffer_lines = self + .scrollback_buffer_lines + .saturating_sub(dropped_line_height); } transfer_rows_from_lines_below_to_viewport( @@ -753,6 +808,7 @@ impl Grid { if self.scroll_region.is_some() { self.set_scroll_region_to_viewport_size(); } + self.scrollback_buffer_lines = self.recalculate_scrollback_buffer_count(); self.output_buffer.update_all_lines(); } pub fn as_character_lines(&self) -> Vec> { @@ -846,12 +902,16 @@ impl Grid { } pub fn fill_viewport(&mut self, character: TerminalCharacter) { let row_count_to_transfer = self.viewport.len(); - transfer_rows_from_viewport_to_lines_above( + let transferred_rows_count = transfer_rows_from_viewport_to_lines_above( &mut self.viewport, &mut self.lines_above, row_count_to_transfer, self.width, ); + + self.scrollback_buffer_lines = + subtract_isize_from_usize(self.scrollback_buffer_lines, transferred_rows_count); + for _ in 0..self.height { let columns = VecDeque::from(vec![character; self.width]); self.viewport.push(Row::from_columns(columns).canonical()); @@ -893,12 +953,15 @@ impl Grid { if self.cursor.y == self.height - 1 { if self.scroll_region.is_none() { let row_count_to_transfer = 1; - transfer_rows_from_viewport_to_lines_above( + let transferred_rows_count = transfer_rows_from_viewport_to_lines_above( &mut self.viewport, &mut self.lines_above, row_count_to_transfer, self.width, ); + self.scrollback_buffer_lines = + subtract_isize_from_usize(self.scrollback_buffer_lines, transferred_rows_count); + self.selection.move_up(1); } self.output_buffer.update_all_lines(); @@ -968,12 +1031,14 @@ impl Grid { self.cursor.x = 0; if self.cursor.y == self.height - 1 { let row_count_to_transfer = 1; - transfer_rows_from_viewport_to_lines_above( + let transferred_rows_count = transfer_rows_from_viewport_to_lines_above( &mut self.viewport, &mut self.lines_above, row_count_to_transfer, self.width, ); + self.scrollback_buffer_lines = + subtract_isize_from_usize(self.scrollback_buffer_lines, transferred_rows_count); let wrapped_row = Row::new(self.width); self.viewport.push(wrapped_row); self.selection.move_up(1); @@ -1264,6 +1329,7 @@ impl Grid { self.cursor.change_shape(CursorShape::Initial); self.output_buffer.update_all_lines(); self.changed_colors = None; + self.scrollback_buffer_lines = 0; } fn set_preceding_character(&mut self, terminal_character: TerminalCharacter) { self.preceding_char = Some(terminal_character);