diff --git a/src/terminal_pane/scroll.rs b/src/terminal_pane/scroll.rs index d66c022e..c85cece2 100644 --- a/src/terminal_pane/scroll.rs +++ b/src/terminal_pane/scroll.rs @@ -40,33 +40,9 @@ impl CanonicalLine { self.wrapped_fragments.push(new_fragment); } pub fn change_width(&mut self, new_width: usize) { - let mut characters: Vec = self - .wrapped_fragments - .iter() - .fold( - Vec::with_capacity(self.wrapped_fragments.len()), - |mut characters, wrapped_fragment| { - characters.push(wrapped_fragment.characters.iter().copied()); - characters - }, - ) - .into_iter() - .flatten() - .collect(); - let mut wrapped_fragments = Vec::with_capacity(characters.len() / new_width); - - while !characters.is_empty() { - if characters.len() > new_width { - wrapped_fragments.push(WrappedFragment::from_vec( - characters.drain(..new_width).collect(), - )); - } else { - wrapped_fragments.push(WrappedFragment::from_vec(characters.drain(..).collect())); - } - } - if wrapped_fragments.is_empty() { - wrapped_fragments.push(WrappedFragment::new()); - } + let characters = self.flattened_characters(); + let wrapped_fragments = + CanonicalLine::fill_fragments_up_to_width(characters, new_width, None); self.wrapped_fragments = wrapped_fragments; } pub fn clear_after(&mut self, fragment_index: usize, column_index: usize) { @@ -113,6 +89,28 @@ impl CanonicalLine { } } } + pub fn erase_chars( + &mut self, + fragment_index: usize, + from_col: usize, + count: usize, + style_of_empty_space: CharacterStyles, + ) { + let mut empty_character = EMPTY_TERMINAL_CHARACTER; + empty_character.styles = style_of_empty_space; + let current_width = self.wrapped_fragments.get(0).unwrap().characters.len(); + let mut characters = self.flattened_characters(); + let absolute_position_of_character = fragment_index * current_width + from_col; + for _ in 0..count { + characters.remove(absolute_position_of_character); + } + let wrapped_fragments = CanonicalLine::fill_fragments_up_to_width( + characters, + current_width, + Some(empty_character), + ); + self.wrapped_fragments = wrapped_fragments; + } pub fn replace_with_empty_chars_after_cursor( &mut self, fragment_index: usize, @@ -157,6 +155,49 @@ impl CanonicalLine { current_fragment.add_character(empty_char_character, i); } } + fn flattened_characters(&self) -> Vec { + self.wrapped_fragments + .iter() + .fold( + Vec::with_capacity(self.wrapped_fragments.len()), + |mut characters, wrapped_fragment| { + characters.push(wrapped_fragment.characters.iter().copied()); + characters + }, + ) + .into_iter() + .flatten() + .collect() + } + fn fill_fragments_up_to_width( + mut characters: Vec, + width: usize, + padding: Option, + ) -> Vec { + let mut wrapped_fragments = vec![]; + while !characters.is_empty() { + if characters.len() > width { + wrapped_fragments.push(WrappedFragment::from_vec( + characters.drain(..width).collect(), + )); + } else { + let mut last_fragment = WrappedFragment::from_vec(characters.drain(..).collect()); + let last_fragment_len = last_fragment.characters.len(); + if let Some(empty_char_character) = padding { + if last_fragment_len < width { + for _ in last_fragment_len..width { + last_fragment.characters.push(empty_char_character); + } + } + } + wrapped_fragments.push(last_fragment); + } + } + if wrapped_fragments.is_empty() { + wrapped_fragments.push(WrappedFragment::new()); + } + wrapped_fragments + } } impl Debug for CanonicalLine { @@ -538,6 +579,21 @@ impl Scroll { style_of_empty_space, ); } + pub fn erase_characters(&mut self, count: usize, style_of_empty_space: CharacterStyles) { + let (current_canonical_line_index, current_line_wrap_position) = + self.cursor_position.line_index; + let current_cursor_column_position = self.cursor_position.column_index; + let current_canonical_line = self + .canonical_lines + .get_mut(current_canonical_line_index) + .expect("cursor out of bounds"); + current_canonical_line.erase_chars( + current_line_wrap_position, + current_cursor_column_position, + count, + style_of_empty_space, + ); + } pub fn clear_all(&mut self) { self.canonical_lines.clear(); self.canonical_lines.push(CanonicalLine::new()); diff --git a/src/terminal_pane/terminal_pane.rs b/src/terminal_pane/terminal_pane.rs index afa4ca9c..e0007537 100644 --- a/src/terminal_pane/terminal_pane.rs +++ b/src/terminal_pane/terminal_pane.rs @@ -868,13 +868,21 @@ impl vte::Perform for TerminalPane { params[0] as usize }; self.scroll.move_cursor_to_line(line); - } else if c == 'X' || c == 'P' { + } else if c == 'P' { // erase characters let count = if params[0] == 0 { 1 } else { params[0] as usize }; + self.scroll.erase_characters(count, self.pending_styles); + } else if c == 'X' { + // erase characters and replace with empty characters of current style + let count = if params[0] == 0 { + 1 + } else { + params[0] as usize + }; self.scroll .replace_with_empty_chars(count, self.pending_styles); } else if c == 'T' { diff --git a/src/tests/fixtures/htop_right_scrolling b/src/tests/fixtures/htop_right_scrolling new file mode 100644 index 00000000..f0bcfa5b Binary files /dev/null and b/src/tests/fixtures/htop_right_scrolling differ diff --git a/src/tests/integration/compatibility.rs b/src/tests/integration/compatibility.rs index cb378ef5..114c10d2 100644 --- a/src/tests/integration/compatibility.rs +++ b/src/tests/integration/compatibility.rs @@ -237,3 +237,26 @@ pub fn htop_scrolling() { assert_snapshot!(snapshot); } } + +#[test] +pub fn htop_right_scrolling() { + let fake_win_size = PositionAndSize { + columns: 116, + rows: 28, + x: 0, + y: 0, + }; + let fixture_name = "htop_right_scrolling"; + let mut fake_input_output = get_fake_os_input(&fake_win_size, fixture_name); + fake_input_output.add_terminal_input(&[&COMMAND_TOGGLE, &COMMAND_TOGGLE, &QUIT]); + start(Box::new(fake_input_output.clone()), Opt::default()); + let output_frames = fake_input_output + .stdout_writer + .output_frames + .lock() + .unwrap(); + let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size); + for snapshot in snapshots { + assert_snapshot!(snapshot); + } +} diff --git a/src/tests/integration/snapshots/mosaic__tests__integration__compatibility__htop_right_scrolling-2.snap b/src/tests/integration/snapshots/mosaic__tests__integration__compatibility__htop_right_scrolling-2.snap new file mode 100644 index 00000000..f32d8c01 --- /dev/null +++ b/src/tests/integration/snapshots/mosaic__tests__integration__compatibility__htop_right_scrolling-2.snap @@ -0,0 +1,32 @@ +--- +source: src/tests/integration/compatibility.rs +expression: snapshot +--- + 1 [|||||||||| 16.9%] Tasks: 110, 512 thr; 1 running + 2 [|||||||||| 17.1%] Load average: 1.04 1.30 1.29 + 3 [||||||| 13.9%] Uptime: 6 days, 07:01:39 + 4 [||||||||| 14.9%] + Mem[|||||||||||||||||||||||||||||||||||||8.80G/15.3G] + Swp[| 2.82M/16.0G] + + PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command + 20 0 8576 4508 3356 R 1.3 0.0 0:00.11 htop + 20 0 171M 10868 7804 S 0.0 0.1 2:08.38 /sbin/init + 20 0 107M 29364 28092 S 0.0 0.2 0:03.42 /usr/lib/systemd/systemd-journald + 20 0 32648 9568 6616 S 0.0 0.1 0:01.87 /usr/lib/systemd/systemd-udevd + 20 0 78060 992 856 S 0.0 0.0 0:00.00 /usr/bin/lvmetad -f + 20 0 6952 4356 3492 S 0.0 0.0 0:57.67 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidf + 20 0 14824 7552 5984 S 0.0 0.0 0:12.93 /usr/bin/connmand -n --nodnsproxy + 20 0 17696 7940 6696 S 0.0 0.0 0:01.25 /usr/lib/systemd/systemd-logind + 20 0 1635M 56148 20460 S 0.0 0.3 1:27.24 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 0:38.37 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 0:00.01 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 0:00.00 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 0:00.00 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 0:00.00 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 1:47.55 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 1:26.19 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 1:40.77 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 1:47.26 /usr/bin/dockerd -H fd:// +F1Help F2Setup F3SearchF4FilterF5Tree F6SortByF7Nice -F8Nice +F9Kill F10Quit +Bye from Mosaic!█ diff --git a/src/tests/integration/snapshots/mosaic__tests__integration__compatibility__htop_right_scrolling.snap b/src/tests/integration/snapshots/mosaic__tests__integration__compatibility__htop_right_scrolling.snap new file mode 100644 index 00000000..c0babf82 --- /dev/null +++ b/src/tests/integration/snapshots/mosaic__tests__integration__compatibility__htop_right_scrolling.snap @@ -0,0 +1,32 @@ +--- +source: src/tests/integration/compatibility.rs +expression: snapshot +--- + + 1 [|||||||||| 16.9%] Tasks: 110, 512 thr; 1 running + 2 [|||||||||| 17.1%] Load average: 1.04 1.30 1.29 + 3 [||||||| 13.9%] Uptime: 6 days, 07:01:39 + 4 [||||||||| 14.9%] + Mem[|||||||||||||||||||||||||||||||||||||8.80G/15.3G] + Swp[| 2.82M/16.0G] + + PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command + 20 0 8576 4508 3356 R 1.3 0.0 0:00.11 htop + 20 0 171M 10868 7804 S 0.0 0.1 2:08.38 /sbin/init + 20 0 107M 29364 28092 S 0.0 0.2 0:03.42 /usr/lib/systemd/systemd-journald + 20 0 32648 9568 6616 S 0.0 0.1 0:01.87 /usr/lib/systemd/systemd-udevd + 20 0 78060 992 856 S 0.0 0.0 0:00.00 /usr/bin/lvmetad -f + 20 0 6952 4356 3492 S 0.0 0.0 0:57.67 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidf + 20 0 14824 7552 5984 S 0.0 0.0 0:12.93 /usr/bin/connmand -n --nodnsproxy + 20 0 17696 7940 6696 S 0.0 0.0 0:01.25 /usr/lib/systemd/systemd-logind + 20 0 1635M 56148 20460 S 0.0 0.3 1:27.24 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 0:38.37 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 0:00.01 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 0:00.00 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 0:00.00 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 0:00.00 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 1:47.55 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 1:26.19 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 1:40.77 /usr/bin/dockerd -H fd:// + 20 0 1635M 56148 20460 S 0.0 0.3 1:47.26 /usr/bin/dockerd -H fd:// +F1Help F2Setup F3SearchF4FilterF5Tree F6SortByF7Nice -F8Nice +F9Kill F10Quit