From a56cb3c9ad200c1f71855f6fd4649c21fed5cab7 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 4 Dec 2020 10:02:51 +0100 Subject: [PATCH] fix(compatibility): htop horizontal scrolling (#81) * fix(compatibility): htop horizontal scrolling * style(format): make rustfmt happy --- src/terminal_pane/scroll.rs | 110 +++++++++++++----- src/terminal_pane/terminal_pane.rs | 10 +- src/tests/fixtures/htop_right_scrolling | Bin 0 -> 15949 bytes src/tests/integration/compatibility.rs | 23 ++++ ...compatibility__htop_right_scrolling-2.snap | 32 +++++ ...__compatibility__htop_right_scrolling.snap | 32 +++++ 6 files changed, 179 insertions(+), 28 deletions(-) create mode 100644 src/tests/fixtures/htop_right_scrolling create mode 100644 src/tests/integration/snapshots/mosaic__tests__integration__compatibility__htop_right_scrolling-2.snap create mode 100644 src/tests/integration/snapshots/mosaic__tests__integration__compatibility__htop_right_scrolling.snap 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 0000000000000000000000000000000000000000..f0bcfa5b7da219d2979e96bc67e30c326b3d27e4 GIT binary patch literal 15949 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 literal 0 HcmV?d00001 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