Skip to content

Commit

Permalink
Fix scrolling (#650)
Browse files Browse the repository at this point in the history
* fix(grid): scroll up on empty line

* fix(grid): line wrapping while scrolling

* style(grid): fix names

* docs(changelog): document fix
  • Loading branch information
imsnif authored Aug 19, 2021
1 parent fde38dc commit e889891
Show file tree
Hide file tree
Showing 10 changed files with 347 additions and 78 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* New pane UI: draw pane frames - can be disabled with ctrl-p + z, or through configuration (https://github.com/zellij-org/zellij/pull/643)
* Terminal compatibility: support changing index colors through OSC 4 and similar (https://github.com/zellij-org/zellij/pull/646)
* Fix various shells (eg. nushell) unexpectedly exiting when the user presses ctrl-c (https://github.com/zellij-org/zellij/pull/648)
* Fix line wrapping while scrolling (https://github.com/zellij-org/zellij/pull/650)

## [0.15.0] - 2021-07-19
* Kill children properly (https://github.com/zellij-org/zellij/pull/601)
Expand Down
20 changes: 20 additions & 0 deletions src/tests/fixtures/scrolling
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
line 1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 5aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 6aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 7aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 9aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 10aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 11aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 12aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 13aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 14aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 15aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 16aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 17aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 18aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 19aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line 20aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Expand Down
212 changes: 136 additions & 76 deletions zellij-server/src/panes/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ fn get_top_non_canonical_rows(rows: &mut Vec<Row>) -> Vec<Row> {
}
}

fn get_bottom_canonical_row_and_wraps(rows: &mut VecDeque<Row>) -> Vec<Row> {
fn get_lines_above_bottom_canonical_row_and_wraps(rows: &mut VecDeque<Row>) -> Vec<Row> {
let mut index_of_last_non_canonical_row = None;
for (i, row) in rows.iter().enumerate().rev() {
index_of_last_non_canonical_row = Some(i);
Expand All @@ -58,101 +58,158 @@ fn get_bottom_canonical_row_and_wraps(rows: &mut VecDeque<Row>) -> Vec<Row> {
}
}

fn transfer_rows_down(
source: &mut VecDeque<Row>,
destination: &mut Vec<Row>,
fn get_viewport_bottom_canonical_row_and_wraps(viewport: &mut Vec<Row>) -> Vec<Row> {
let mut index_of_last_non_canonical_row = None;
for (i, row) in viewport.iter().enumerate().rev() {
index_of_last_non_canonical_row = Some(i);
if row.is_canonical {
break;
}
}
match index_of_last_non_canonical_row {
Some(index_of_last_non_canonical_row) => {
viewport.drain(index_of_last_non_canonical_row..).collect()
}
None => vec![],
}
}

fn get_top_canonical_row_and_wraps(rows: &mut Vec<Row>) -> Vec<Row> {
let mut index_of_first_non_canonical_row = None;
let mut end_index_of_first_canonical_line = None;
for (i, row) in rows.iter().enumerate() {
if row.is_canonical && end_index_of_first_canonical_line.is_none() {
index_of_first_non_canonical_row = Some(i);
end_index_of_first_canonical_line = Some(i);
continue;
}
if row.is_canonical && end_index_of_first_canonical_line.is_some() {
break;
}
if index_of_first_non_canonical_row.is_some() {
end_index_of_first_canonical_line = Some(i);
continue;
}
}
match (
index_of_first_non_canonical_row,
end_index_of_first_canonical_line,
) {
(Some(first_index), Some(last_index)) => rows.drain(first_index..=last_index).collect(),
(Some(first_index), None) => rows.drain(first_index..).collect(),
_ => vec![],
}
}

fn transfer_rows_from_lines_above_to_viewport(
lines_above: &mut VecDeque<Row>,
viewport: &mut Vec<Row>,
count: usize,
max_src_width: Option<usize>,
max_dst_width: Option<usize>,
max_viewport_width: usize,
) {
let mut next_lines: Vec<Row> = vec![];
let mut lines_added_to_destination: isize = 0;
let mut lines_added_to_viewport: isize = 0;
loop {
if lines_added_to_destination as usize == count {
if lines_added_to_viewport as usize == count {
break;
}
if next_lines.is_empty() {
match source.pop_back() {
match lines_above.pop_back() {
Some(next_line) => {
let mut top_non_canonical_rows_in_dst = get_top_non_canonical_rows(destination);
lines_added_to_destination -= top_non_canonical_rows_in_dst.len() as isize;
let mut top_non_canonical_rows_in_dst = get_top_non_canonical_rows(viewport);
lines_added_to_viewport -= top_non_canonical_rows_in_dst.len() as isize;
next_lines.push(next_line);
next_lines.append(&mut top_non_canonical_rows_in_dst);
next_lines = match max_dst_width {
Some(max_row_width) => Row::from_rows(next_lines, max_row_width)
.split_to_rows_of_length(max_row_width),
None => vec![Row::from_rows(next_lines, 0)],
};
next_lines = Row::from_rows(next_lines, max_viewport_width)
.split_to_rows_of_length(max_viewport_width);
if next_lines.is_empty() {
// no more lines at source, the line we popped was probably empty
// no more lines at lines_above, the line we popped was probably empty
break;
}
}
None => break, // no more rows
}
}
destination.insert(0, next_lines.pop().unwrap());
lines_added_to_destination += 1;
viewport.insert(0, next_lines.pop().unwrap());
lines_added_to_viewport += 1;
}
if !next_lines.is_empty() {
match max_src_width {
Some(max_row_width) => {
let excess_rows = Row::from_rows(next_lines, max_row_width)
.split_to_rows_of_length(max_row_width);
source.extend(excess_rows);
}
None => {
let excess_row = Row::from_rows(next_lines, 0);
bounded_push(source, excess_row);
}
}
let excess_row = Row::from_rows(next_lines, 0);
bounded_push(lines_above, excess_row);
}
}

fn transfer_rows_up(
source: &mut Vec<Row>,
destination: &mut VecDeque<Row>,
fn transfer_rows_from_viewport_to_lines_above(
viewport: &mut Vec<Row>,
lines_above: &mut VecDeque<Row>,
count: usize,
max_src_width: Option<usize>,
max_dst_width: Option<usize>,
max_viewport_width: usize,
) {
let mut next_lines: Vec<Row> = vec![];
for _ in 0..count {
if next_lines.is_empty() {
if !source.is_empty() {
let next_line = source.remove(0);
if !viewport.is_empty() {
let next_line = viewport.remove(0);
if !next_line.is_canonical {
let mut bottom_canonical_row_and_wraps_in_dst =
get_bottom_canonical_row_and_wraps(destination);
get_lines_above_bottom_canonical_row_and_wraps(lines_above);
next_lines.append(&mut bottom_canonical_row_and_wraps_in_dst);
}
next_lines.push(next_line);
next_lines = match max_dst_width {
Some(max_row_width) => Row::from_rows(next_lines, max_row_width)
.split_to_rows_of_length(max_row_width),
None => vec![Row::from_rows(next_lines, 0)],
};
next_lines = vec![Row::from_rows(next_lines, 0)];
} else {
break; // no more rows
}
}
bounded_push(destination, next_lines.remove(0));
bounded_push(lines_above, next_lines.remove(0));
}
if !next_lines.is_empty() {
match max_src_width {
Some(max_row_width) => {
let excess_rows = Row::from_rows(next_lines, max_row_width)
.split_to_rows_of_length(max_row_width);
for row in excess_rows {
source.insert(0, row);
let excess_rows = Row::from_rows(next_lines, max_viewport_width)
.split_to_rows_of_length(max_viewport_width);
for row in excess_rows {
viewport.insert(0, row);
}
}
}

fn transfer_rows_from_lines_below_to_viewport(
lines_below: &mut Vec<Row>,
viewport: &mut Vec<Row>,
count: usize,
max_viewport_width: usize,
) {
let mut next_lines: Vec<Row> = vec![];
for _ in 0..count {
let mut lines_pulled_from_viewport = 0;
if next_lines.is_empty() {
if !lines_below.is_empty() {
let mut top_non_canonical_rows_in_lines_below =
get_top_non_canonical_rows(lines_below);
if !top_non_canonical_rows_in_lines_below.is_empty() {
let mut canonical_line = get_viewport_bottom_canonical_row_and_wraps(viewport);
lines_pulled_from_viewport += canonical_line.len();
canonical_line.append(&mut top_non_canonical_rows_in_lines_below);
next_lines = Row::from_rows(canonical_line, max_viewport_width)
.split_to_rows_of_length(max_viewport_width);
} else {
let canonical_row = get_top_canonical_row_and_wraps(lines_below);
next_lines = Row::from_rows(canonical_row, max_viewport_width)
.split_to_rows_of_length(max_viewport_width);
}
} else {
break; // no more rows
}
None => {
let excess_row = Row::from_rows(next_lines, 0);
source.insert(0, excess_row);
}
for _ in 0..(lines_pulled_from_viewport + 1) {
if !next_lines.is_empty() {
viewport.push(next_lines.remove(0));
}
}
}
if !next_lines.is_empty() {
let excess_row = Row::from_rows(next_lines, 0);
lines_below.insert(0, excess_row);
}
}

fn bounded_push(vec: &mut VecDeque<Row>, value: Row) {
Expand Down Expand Up @@ -481,12 +538,11 @@ impl Grid {
let line_to_push_down = self.viewport.pop().unwrap();
self.lines_below.insert(0, line_to_push_down);

transfer_rows_down(
transfer_rows_from_lines_above_to_viewport(
&mut self.lines_above,
&mut self.viewport,
1,
None,
Some(self.width),
self.width,
);

self.selection.move_down(1);
Expand All @@ -503,8 +559,14 @@ impl Grid {
last_line_above.append(&mut line_to_push_up.columns);
bounded_push(&mut self.lines_above, last_line_above);
}
let line_to_insert_at_viewport_bottom = self.lines_below.remove(0);
self.viewport.push(line_to_insert_at_viewport_bottom);

transfer_rows_from_lines_below_to_viewport(
&mut self.lines_below,
&mut self.viewport,
1,
self.width,
);

self.selection.move_up(1);
self.output_buffer.update_all_lines();
}
Expand Down Expand Up @@ -593,12 +655,12 @@ impl Grid {
match current_viewport_row_count.cmp(&self.height) {
Ordering::Less => {
let row_count_to_transfer = self.height - current_viewport_row_count;
transfer_rows_down(

transfer_rows_from_lines_above_to_viewport(
&mut self.lines_above,
&mut self.viewport,
row_count_to_transfer,
None,
Some(new_columns),
new_columns,
);
let rows_pulled = self.viewport.len() - current_viewport_row_count;
new_cursor_y += rows_pulled;
Expand All @@ -610,12 +672,11 @@ impl Grid {
} else {
new_cursor_y -= row_count_to_transfer;
}
transfer_rows_up(
transfer_rows_from_viewport_to_lines_above(
&mut self.viewport,
&mut self.lines_above,
row_count_to_transfer,
Some(new_columns),
None,
new_columns,
);
}
Ordering::Equal => {}
Expand All @@ -628,12 +689,11 @@ impl Grid {
match current_viewport_row_count.cmp(&new_rows) {
Ordering::Less => {
let row_count_to_transfer = new_rows - current_viewport_row_count;
transfer_rows_down(
transfer_rows_from_lines_above_to_viewport(
&mut self.lines_above,
&mut self.viewport,
row_count_to_transfer,
None,
Some(new_columns),
new_columns,
);
let rows_pulled = self.viewport.len() - current_viewport_row_count;
self.cursor.y += rows_pulled;
Expand All @@ -645,12 +705,11 @@ impl Grid {
} else {
self.cursor.y -= row_count_to_transfer;
}
transfer_rows_up(
transfer_rows_from_viewport_to_lines_above(
&mut self.viewport,
&mut self.lines_above,
row_count_to_transfer,
Some(new_columns),
None,
new_columns,
);
}
Ordering::Equal => {}
Expand Down Expand Up @@ -794,12 +853,11 @@ impl Grid {
}
if self.cursor.y == self.height - 1 {
let row_count_to_transfer = 1;
transfer_rows_up(
transfer_rows_from_viewport_to_lines_above(
&mut self.viewport,
&mut self.lines_above,
row_count_to_transfer,
Some(self.width),
None,
self.width,
);
self.selection.move_up(1);
self.output_buffer.update_all_lines();
Expand Down Expand Up @@ -869,12 +927,11 @@ impl Grid {
self.cursor.x = 0;
if self.cursor.y == self.height - 1 {
let row_count_to_transfer = 1;
transfer_rows_up(
transfer_rows_from_viewport_to_lines_above(
&mut self.viewport,
&mut self.lines_above,
row_count_to_transfer,
Some(self.width),
None,
self.width,
);
let wrapped_row = Row::new(self.width);
self.viewport.push(wrapped_row);
Expand Down Expand Up @@ -2165,6 +2222,9 @@ impl Row {
if !parts.is_empty() && self.is_canonical {
parts.get_mut(0).unwrap().is_canonical = true;
}
if parts.is_empty() {
parts.push(self.clone());
}
parts
}
}
Expand Down
Loading

0 comments on commit e889891

Please sign in to comment.