mirror of
https://github.com/zellij-org/zellij.git
synced 2024-11-27 06:25:42 +03:00
fix(compatibility): various htop issues (#66)
* fix(compatibility): various htop issues * style(format): make rustfmt happy * fix(logging): do not delete log dir on startup * fix(tests): update htop with command toggle * chore(ci): reduce test concurrency to 1
This commit is contained in:
parent
fd1f1ce697
commit
f88abe6ad8
2
.github/workflows/rust.yml
vendored
2
.github/workflows/rust.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
run: cargo test -j 1 --verbose
|
||||
fmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -97,9 +97,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||
|
||||
let command_is_executing = CommandIsExecuting::new();
|
||||
|
||||
delete_log_dir().unwrap();
|
||||
delete_log_file().unwrap();
|
||||
|
||||
let full_screen_ws = os_input.get_terminal_size_using_fd(0);
|
||||
os_input.into_raw_mode(0);
|
||||
let (send_screen_instructions, receive_screen_instructions): (
|
||||
|
@ -1,7 +1,10 @@
|
||||
use ::std::collections::VecDeque;
|
||||
use ::std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use crate::terminal_pane::terminal_character::{TerminalCharacter, EMPTY_TERMINAL_CHARACTER};
|
||||
use crate::terminal_pane::terminal_character::{
|
||||
CharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
|
||||
};
|
||||
use crate::utils::logging::{debug_log_to_file, debug_log_to_file_pid_0};
|
||||
|
||||
/*
|
||||
* Scroll
|
||||
@ -75,6 +78,64 @@ impl CanonicalLine {
|
||||
fragment_to_clear.clear_after_and_including(column_index);
|
||||
self.wrapped_fragments.truncate(fragment_index + 1);
|
||||
}
|
||||
pub fn replace_with_empty_chars(
|
||||
&mut self,
|
||||
fragment_index: usize,
|
||||
from_col: usize,
|
||||
count: usize,
|
||||
style_of_empty_space: CharacterStyles,
|
||||
) {
|
||||
let mut characters_replaced = 0;
|
||||
let mut column_position_in_fragment = from_col;
|
||||
let mut current_fragment = fragment_index;
|
||||
let mut empty_space_character = EMPTY_TERMINAL_CHARACTER;
|
||||
empty_space_character.styles = style_of_empty_space;
|
||||
loop {
|
||||
if characters_replaced == count {
|
||||
break;
|
||||
}
|
||||
match self.wrapped_fragments.get_mut(current_fragment) {
|
||||
Some(fragment_to_clear) => {
|
||||
let fragment_characters_count = fragment_to_clear.characters.len();
|
||||
if fragment_characters_count >= column_position_in_fragment {
|
||||
fragment_to_clear
|
||||
.add_character(empty_space_character, column_position_in_fragment);
|
||||
column_position_in_fragment += 1;
|
||||
characters_replaced += 1;
|
||||
} else {
|
||||
current_fragment += 1;
|
||||
column_position_in_fragment = 0;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// end of line, nothing more to clear
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn replace_with_empty_chars_after_cursor(
|
||||
&mut self,
|
||||
fragment_index: usize,
|
||||
from_col: usize,
|
||||
total_columns: usize,
|
||||
style_of_empty_space: CharacterStyles,
|
||||
) {
|
||||
let mut empty_char_character = EMPTY_TERMINAL_CHARACTER;
|
||||
empty_char_character.styles = style_of_empty_space;
|
||||
let current_fragment = self.wrapped_fragments.get_mut(fragment_index).unwrap();
|
||||
let fragment_characters_count = current_fragment.characters.len();
|
||||
|
||||
for i in from_col..fragment_characters_count {
|
||||
current_fragment.add_character(empty_char_character, i);
|
||||
}
|
||||
|
||||
for i in fragment_characters_count..total_columns {
|
||||
current_fragment.add_character(empty_char_character, i);
|
||||
}
|
||||
|
||||
self.wrapped_fragments.truncate(fragment_index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for CanonicalLine {
|
||||
@ -283,7 +344,7 @@ impl Scroll {
|
||||
// scroll region should be ignored if the cursor is hidden
|
||||
let scroll_region_top_index = scroll_region_top - 1;
|
||||
let scroll_region_bottom_index = scroll_region_bottom - 1;
|
||||
if current_canonical_line_index == scroll_region_bottom_index {
|
||||
if current_canonical_line_index == scroll_region_bottom_index + 1 {
|
||||
// end of scroll region
|
||||
// when we have a scroll region set and we're at its bottom
|
||||
// we need to delete its first line, thus shifting all lines in it upwards
|
||||
@ -292,7 +353,7 @@ impl Scroll {
|
||||
// scroll buffer, but that's not something we control)
|
||||
self.canonical_lines.remove(scroll_region_top_index);
|
||||
self.canonical_lines
|
||||
.insert(scroll_region_bottom_index, CanonicalLine::new());
|
||||
.insert(scroll_region_bottom_index + 1, CanonicalLine::new());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -405,7 +466,7 @@ impl Scroll {
|
||||
self.lines_in_view = lines;
|
||||
self.total_columns = columns;
|
||||
}
|
||||
pub fn clear_canonical_line_right_of_cursor(&mut self) {
|
||||
pub fn clear_canonical_line_right_of_cursor(&mut self, 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;
|
||||
@ -413,8 +474,12 @@ impl Scroll {
|
||||
.canonical_lines
|
||||
.get_mut(current_canonical_line_index)
|
||||
.expect("cursor out of bounds");
|
||||
current_canonical_line
|
||||
.clear_after(current_line_wrap_position, current_cursor_column_position);
|
||||
current_canonical_line.replace_with_empty_chars_after_cursor(
|
||||
current_line_wrap_position,
|
||||
current_cursor_column_position,
|
||||
self.total_columns,
|
||||
style_of_empty_space,
|
||||
);
|
||||
}
|
||||
pub fn clear_all_after_cursor(&mut self) {
|
||||
let (current_canonical_line_index, current_line_wrap_position) =
|
||||
@ -429,6 +494,25 @@ impl Scroll {
|
||||
self.canonical_lines
|
||||
.truncate(current_canonical_line_index + 1);
|
||||
}
|
||||
pub fn replace_with_empty_chars(
|
||||
&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.replace_with_empty_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());
|
||||
@ -458,6 +542,14 @@ impl Scroll {
|
||||
}
|
||||
self.cursor_position.move_to_column(col);
|
||||
}
|
||||
pub fn move_cursor_to_column(&mut self, col: usize) {
|
||||
let current_canonical_line = self.cursor_position.line_index.0;
|
||||
self.move_cursor_to(current_canonical_line, col);
|
||||
}
|
||||
pub fn move_cursor_to_line(&mut self, line: usize) {
|
||||
let current_column = self.cursor_position.column_index;
|
||||
self.move_cursor_to(line, current_column);
|
||||
}
|
||||
pub fn set_scroll_region(&mut self, top_line: usize, bottom_line: usize) {
|
||||
self.scroll_region = Some((top_line, bottom_line));
|
||||
// TODO: clear linewraps in scroll region?
|
||||
@ -481,7 +573,7 @@ impl Scroll {
|
||||
for _ in 0..count {
|
||||
self.canonical_lines.remove(current_canonical_line_index);
|
||||
self.canonical_lines
|
||||
.insert(scroll_region_bottom_index, CanonicalLine::new());
|
||||
.insert(scroll_region_bottom_index + 1, CanonicalLine::new());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -500,7 +592,7 @@ impl Scroll {
|
||||
// so we add an empty line where the cursor currently is, and delete the last line
|
||||
// of the scroll region
|
||||
for _ in 0..count {
|
||||
self.canonical_lines.remove(scroll_region_bottom_index);
|
||||
self.canonical_lines.remove(scroll_region_bottom_index + 1);
|
||||
self.canonical_lines
|
||||
.insert(current_canonical_line_index, CanonicalLine::new());
|
||||
}
|
||||
|
@ -335,8 +335,31 @@ impl vte::Perform for TerminalPane {
|
||||
if params.is_empty() || params[0] == 0 {
|
||||
// reset all
|
||||
self.pending_styles.reset_all();
|
||||
if let Some(param1) = params.get(1) {
|
||||
// TODO: this is a case currently found in eg. htop where we get two different
|
||||
// csi 'm' codes in one event.
|
||||
// We should understand why these are happening and then make a more generic
|
||||
// solution for them
|
||||
if *param1 == 1 {
|
||||
// bold
|
||||
self.pending_styles = self
|
||||
.pending_styles
|
||||
.bold(Some(AnsiCode::Code((Some(*param1 as u16), None))));
|
||||
}
|
||||
}
|
||||
} else if params[0] == 39 {
|
||||
self.pending_styles = self.pending_styles.foreground(Some(AnsiCode::Reset));
|
||||
if let Some(param1) = params.get(1) {
|
||||
// TODO: this is a case currently found in eg. htop where we get two different
|
||||
// csi 'm' codes in one event.
|
||||
// We should understand why these are happening and then make a more generic
|
||||
// solution for them
|
||||
if *param1 == 49 {
|
||||
// TODO: if we need this to fix the bug, we need to make collecting the
|
||||
// second argument in such cases generic
|
||||
self.pending_styles = self.pending_styles.background(Some(AnsiCode::Reset));
|
||||
}
|
||||
}
|
||||
} else if params[0] == 49 {
|
||||
self.pending_styles = self.pending_styles.background(Some(AnsiCode::Reset));
|
||||
} else if params[0] == 21 {
|
||||
@ -655,7 +678,8 @@ impl vte::Perform for TerminalPane {
|
||||
} else if c == 'K' {
|
||||
// clear line (0 => right, 1 => left, 2 => all)
|
||||
if params[0] == 0 {
|
||||
self.scroll.clear_canonical_line_right_of_cursor();
|
||||
self.scroll
|
||||
.clear_canonical_line_right_of_cursor(self.pending_styles);
|
||||
}
|
||||
// TODO: implement 1 and 2
|
||||
} else if c == 'J' {
|
||||
@ -669,9 +693,11 @@ impl vte::Perform for TerminalPane {
|
||||
} else if c == 'H' {
|
||||
// goto row/col
|
||||
let (row, col) = if params.len() == 1 {
|
||||
(params[0] as usize, 0) // TODO: is this always correct ?
|
||||
(params[0] as usize, params[0] as usize)
|
||||
} else {
|
||||
(params[0] as usize - 1, params[1] as usize - 1) // we subtract 1 here because this csi is 1 indexed and we index from 0
|
||||
// we subtract 1 from the column because after we get a cursor goto, the print
|
||||
// character should be printed on top of the cursor
|
||||
(params[0] as usize, params[1] as usize - 1)
|
||||
};
|
||||
self.scroll.move_cursor_to(row, col);
|
||||
} else if c == 'A' {
|
||||
@ -749,19 +775,39 @@ impl vte::Perform for TerminalPane {
|
||||
};
|
||||
self.scroll
|
||||
.add_empty_lines_in_scroll_region(line_count_to_add);
|
||||
} else if c == 'q' || c == 'd' || c == 'X' || c == 'G' {
|
||||
} else if c == 'q' {
|
||||
// ignore for now to run on mac
|
||||
} else if c == 'G' {
|
||||
let column = if params[0] == 0 {
|
||||
0
|
||||
} else {
|
||||
// params[0] as usize
|
||||
params[0] as usize - 1
|
||||
};
|
||||
self.scroll.move_cursor_to_column(column);
|
||||
} else if c == 'd' {
|
||||
// goto line
|
||||
let line = if params[0] == 0 {
|
||||
1
|
||||
} else {
|
||||
params[0] as usize
|
||||
};
|
||||
self.scroll.move_cursor_to_line(line);
|
||||
} else if c == 'X' || c == 'P' {
|
||||
// erase characters
|
||||
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' {
|
||||
/*
|
||||
* 124 54 T SD
|
||||
* Scroll down, new lines inserted at top of screen
|
||||
* [4T = Scroll down 4, bring previous lines back into view
|
||||
*/
|
||||
debug_log_to_file(format!(
|
||||
"htop (only?) linux csi: {}->{:?} ({:?} - ignore: {})",
|
||||
c, params, _intermediates, _ignore
|
||||
))
|
||||
.unwrap();
|
||||
let line_count: i64 = *params.get(0).expect("A number of lines was expected.");
|
||||
|
||||
if line_count >= 0 {
|
||||
@ -769,17 +815,15 @@ impl vte::Perform for TerminalPane {
|
||||
} else {
|
||||
self.rotate_scroll_region_down(line_count.abs() as usize);
|
||||
}
|
||||
} else if c == 'P' {
|
||||
/*
|
||||
* 120 50 P * DCH
|
||||
* Delete Character, from current position to end of field
|
||||
* [4P = Delete 4 characters, VT102 series
|
||||
*/
|
||||
debug_log_to_file(format!(
|
||||
"htop (only?) linux csi: {}->{:?} (intermediates: {:?}, ignore: {})",
|
||||
c, params, _intermediates, _ignore
|
||||
))
|
||||
.unwrap();
|
||||
} else if c == 'S' {
|
||||
// move scroll up
|
||||
let count = if params[0] == 0 {
|
||||
1
|
||||
} else {
|
||||
params[0] as usize
|
||||
};
|
||||
self.scroll.delete_lines_in_scroll_region(count);
|
||||
self.scroll.add_empty_lines_in_scroll_region(count);
|
||||
} else {
|
||||
debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params)).unwrap();
|
||||
panic!("unhandled csi: {}->{:?}", c, params);
|
||||
|
BIN
src/tests/fixtures/htop
vendored
Normal file
BIN
src/tests/fixtures/htop
vendored
Normal file
Binary file not shown.
@ -192,3 +192,26 @@ pub fn vim_ctrl_u() {
|
||||
assert_snapshot!(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn htop() {
|
||||
let fake_win_size = PositionAndSize {
|
||||
columns: 116,
|
||||
rows: 28,
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
let fixture_name = "htop";
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
source: src/tests/integration/compatibility.rs
|
||||
expression: snapshot
|
||||
---
|
||||
1 [||||| 10.1%] Tasks: 73, 413 thr; 1 running
|
||||
2 [||||||| 13.5%] Load average: 1.03 1.07 1.30
|
||||
3 [|||||| 10.8%] Uptime: 22:41:15
|
||||
4 [|||||| 10.6%]
|
||||
Mem[|||||||||||||||||||||||||||||||||||||3.28G/15.3G]
|
||||
Swp[ 0K/16.0G]
|
||||
|
||||
PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command
|
||||
1352 aram 20 0 3776M 581M 238M S 8.7 3.7 2h01:10 /usr/lib/firefox/firefox
|
||||
98777 aram 20 0 537M 6184 4240 S 8.1 0.0 0:00.80 target/debug/mosaic --debug
|
||||
1669 aram 20 0 2944M 318M 130M S 8.1 2.0 1h01:33 /usr/lib/firefox/firefox -contentproc -childID 6 -i
|
||||
826 aram 9 -11 1581M 15092 11244 S 6.1 0.1 42:21.83 /usr/bin/pulseaudio --daemonize=no
|
||||
9419 aram 20 0 533M 7392 3344 S 4.7 0.0 22:01.92 /usr/local/bin/mosaic --max-panes 4
|
||||
98913 aram 20 0 537M 6184 4240 S 3.4 0.0 0:00.31 target/debug/mosaic --debug
|
||||
1505 aram 20 0 3187M 329M 206M S 3.4 2.1 23:35.90 /usr/lib/firefox/firefox -contentproc -childID 2 -i
|
||||
98912 aram 20 0 537M 6184 4240 S 2.7 0.0 0:00.22 target/debug/mosaic --debug
|
||||
1164 aram -6 0 1581M 15092 11244 S 2.7 0.1 21:39.80 /usr/bin/pulseaudio --daemonize=no
|
||||
1247 aram 20 0 1184M 292M 84828 S 2.7 1.9 38:01.54 /usr/lib/Xorg -nolisten tcp :0 vt1 -keeptty -auth /
|
||||
1475 aram -11 0 3776M 581M 238M S 2.0 3.7 14:27.94 /usr/lib/firefox/firefox
|
||||
8574 aram 20 0 2944M 318M 130M S 2.0 2.0 14:36.50 /usr/lib/firefox/firefox -contentproc -childID 6 -i
|
||||
1364 aram 20 0 3776M 581M 238M S 2.0 3.7 18:01.89 /usr/lib/firefox/firefox
|
||||
1870 aram 20 0 3776M 581M 238M S 2.0 3.7 13:27.06 /usr/lib/firefox/firefox
|
||||
9427 aram 20 0 533M 7392 3344 S 2.0 0.0 6:53.47 /usr/local/bin/mosaic --max-panes 4
|
||||
98905 aram 20 0 537M 6184 4240 S 2.0 0.0 0:00.17 target/debug/mosaic --debug
|
||||
99272 aram 20 0 8456 4348 3320 R 1.3 0.0 0:00.13 htop
|
||||
8611 aram 20 0 2944M 318M 130M S 1.3 2.0 8:17.90 /usr/lib/firefox/firefox -contentproc -childID 6 -i
|
||||
F1Help F2Setup F3SearchF4FilterF5Tree F6SortByF7Nice -F8Nice +F9Kill F10Quit
|
||||
Bye from Mosaic!
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
source: src/tests/integration/compatibility.rs
|
||||
expression: snapshot
|
||||
---
|
||||
|
||||
1 [||||| 10.1%] Tasks: 73, 413 thr; 1 running
|
||||
2 [||||||| 13.5%] Load average: 1.03 1.07 1.30
|
||||
3 [|||||| 10.8%] Uptime: 22:41:15
|
||||
4 [|||||| 10.6%]
|
||||
Mem[|||||||||||||||||||||||||||||||||||||3.28G/15.3G]
|
||||
Swp[ 0K/16.0G]
|
||||
|
||||
PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command
|
||||
1352 aram 20 0 3776M 581M 238M S 8.7 3.7 2h01:10 /usr/lib/firefox/firefox
|
||||
98777 aram 20 0 537M 6184 4240 S 8.1 0.0 0:00.80 target/debug/mosaic --debug
|
||||
1669 aram 20 0 2944M 318M 130M S 8.1 2.0 1h01:33 /usr/lib/firefox/firefox -contentproc -childID 6 -i
|
||||
826 aram 9 -11 1581M 15092 11244 S 6.1 0.1 42:21.83 /usr/bin/pulseaudio --daemonize=no
|
||||
9419 aram 20 0 533M 7392 3344 S 4.7 0.0 22:01.92 /usr/local/bin/mosaic --max-panes 4
|
||||
98913 aram 20 0 537M 6184 4240 S 3.4 0.0 0:00.31 target/debug/mosaic --debug
|
||||
1505 aram 20 0 3187M 329M 206M S 3.4 2.1 23:35.90 /usr/lib/firefox/firefox -contentproc -childID 2 -i
|
||||
98912 aram 20 0 537M 6184 4240 S 2.7 0.0 0:00.22 target/debug/mosaic --debug
|
||||
1164 aram -6 0 1581M 15092 11244 S 2.7 0.1 21:39.80 /usr/bin/pulseaudio --daemonize=no
|
||||
1247 aram 20 0 1184M 292M 84828 S 2.7 1.9 38:01.54 /usr/lib/Xorg -nolisten tcp :0 vt1 -keeptty -auth /
|
||||
1475 aram -11 0 3776M 581M 238M S 2.0 3.7 14:27.94 /usr/lib/firefox/firefox
|
||||
8574 aram 20 0 2944M 318M 130M S 2.0 2.0 14:36.50 /usr/lib/firefox/firefox -contentproc -childID 6 -i
|
||||
1364 aram 20 0 3776M 581M 238M S 2.0 3.7 18:01.89 /usr/lib/firefox/firefox
|
||||
1870 aram 20 0 3776M 581M 238M S 2.0 3.7 13:27.06 /usr/lib/firefox/firefox
|
||||
9427 aram 20 0 533M 7392 3344 S 2.0 0.0 6:53.47 /usr/local/bin/mosaic --max-panes 4
|
||||
98905 aram 20 0 537M 6184 4240 S 2.0 0.0 0:00.17 target/debug/mosaic --debug
|
||||
99272 aram 20 0 8456 4348 3320 R 1.3 0.0 0:00.13 htop
|
||||
8611 aram 20 0 2944M 318M 130M S 1.3 2.0 8:17.90 /usr/lib/firefox/firefox -contentproc -childID 6 -i
|
||||
F1Help F2Setup F3SearchF4FilterF5Tree F6SortByF7Nice -F8Nice +F9Kill F10Quit
|
@ -7,7 +7,12 @@ use std::{
|
||||
|
||||
use crate::utils::consts::{MOSAIC_TMP_LOG_DIR, MOSAIC_TMP_LOG_FILE};
|
||||
|
||||
fn atomic_create_file(file_name: &str) {
|
||||
let _ = fs::OpenOptions::new().create(true).open(file_name);
|
||||
}
|
||||
|
||||
pub fn debug_log_to_file(message: String) -> io::Result<()> {
|
||||
atomic_create_file(MOSAIC_TMP_LOG_FILE);
|
||||
let mut file = fs::OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
|
Loading…
Reference in New Issue
Block a user