From 219e9858b7fbb7857d5732cc51bd3fd6df2eb884 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 7 May 2021 11:41:13 +0200 Subject: [PATCH 1/2] fix(compatibility): upgrade vte to support csi subparameters --- Cargo.lock | 6 +- Cargo.toml | 2 +- src/client/panes/grid.rs | 202 ++++++-------- src/client/panes/terminal_character.rs | 256 +++++++++--------- ...ration__compatibility__htop_scrolling.snap | 2 +- 5 files changed, 205 insertions(+), 263 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d04bc27f3..463f38b74a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1809,9 +1809,9 @@ dependencies = [ [[package]] name = "vte" -version = "0.8.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cc8a191608603611e78c6ec11dafef37e3cca0775aeef1931824753e81711d" +checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" dependencies = [ "arrayvec", "utf8parse 0.2.0", @@ -2219,7 +2219,7 @@ dependencies = [ "termios", "unicode-truncate", "unicode-width", - "vte 0.8.0", + "vte 0.10.1", "wasmer", "wasmer-wasi", "zellij-tile", diff --git a/Cargo.toml b/Cargo.toml index c04206dafc..9fed2d3852 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ termion = "1.5.0" termios = "0.3" unicode-truncate = "0.2.0" unicode-width = "0.1.8" -vte = "0.8.0" +vte = "0.10.1" strum = "0.20.0" lazy_static = "1.4.0" wasmer = "1.0.0" diff --git a/src/client/panes/grid.rs b/src/client/panes/grid.rs index e893fd3244..32cecf1afa 100644 --- a/src/client/panes/grid.rs +++ b/src/client/panes/grid.rs @@ -4,6 +4,8 @@ use std::{ fmt::{self, Debug, Formatter}, }; +use vte::{Perform, Params}; + const TABSTOP_WIDTH: usize = 8; // TODO: is this always right? const SCROLL_BACK: usize = 10_000; @@ -811,7 +813,8 @@ impl Grid { pub fn show_cursor(&mut self) { self.cursor.is_hidden = false; } - pub fn set_scroll_region(&mut self, top_line_index: usize, bottom_line_index: usize) { + pub fn set_scroll_region(&mut self, top_line_index: usize, bottom_line_index: Option) { + let bottom_line_index = bottom_line_index.unwrap_or(self.height); self.scroll_region = Some((top_line_index, bottom_line_index)); } pub fn clear_scroll_region(&mut self) { @@ -925,7 +928,7 @@ impl Grid { } } -impl vte::Perform for Grid { +impl Perform for Grid { fn print(&mut self, c: char) { let c = self.cursor.charsets[self.active_charset].map(c); // apparently, building TerminalCharacter like this without a "new" method @@ -966,7 +969,7 @@ impl vte::Perform for Grid { } } - fn hook(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool, _c: char) { + fn hook(&mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, _c: char) { // TBD } @@ -982,80 +985,68 @@ impl vte::Perform for Grid { // TBD } - fn csi_dispatch(&mut self, params: &[i64], _intermediates: &[u8], _ignore: bool, c: char) { + fn csi_dispatch(&mut self, params: &Params, _intermediates: &[u8], _ignore: bool, c: char) { + + let mut params_iter = params.iter(); + let mut next_param_or = |default: u16| { + params_iter.next().map(|param| param[0]).filter(|¶m| param != 0).unwrap_or(default) as usize + }; if c == 'm' { self.cursor .pending_styles - .add_style_from_ansi_params(params); + .add_style_from_ansi_params(&mut params_iter); } else if c == 'C' { // move cursor forward - let move_by = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; + let move_by = next_param_or(1); self.move_cursor_forward_until_edge(move_by); } else if c == 'K' { // clear line (0 => right, 1 => left, 2 => all) - if params[0] == 0 { - let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; - char_to_replace.styles = self.cursor.pending_styles; - self.replace_characters_in_line_after_cursor(char_to_replace); - } else if params[0] == 1 { - let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; - char_to_replace.styles = self.cursor.pending_styles; - self.replace_characters_in_line_before_cursor(char_to_replace); - } else if params[0] == 2 { - self.clear_cursor_line(); - } + params_iter.next().map(|param| param[0]).map(|clear_type| { + if clear_type == 0 { + let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; + char_to_replace.styles = self.cursor.pending_styles; + self.replace_characters_in_line_after_cursor(char_to_replace); + } else if clear_type == 1 { + let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; + char_to_replace.styles = self.cursor.pending_styles; + self.replace_characters_in_line_before_cursor(char_to_replace); + } else if clear_type == 2 { + self.clear_cursor_line(); + } + }); } else if c == 'J' { // clear all (0 => below, 1 => above, 2 => all, 3 => saved) let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; char_to_replace.styles = self.cursor.pending_styles; - if params[0] == 0 { - self.clear_all_after_cursor(char_to_replace); - } else if params[0] == 1 { - self.clear_all_before_cursor(char_to_replace); - } else if params[0] == 2 { - self.clear_all(char_to_replace); - } + + params_iter.next().map(|param| param[0]).map(|clear_type| { + if clear_type == 0 { + self.clear_all_after_cursor(char_to_replace); + } else if clear_type == 1 { + self.clear_all_before_cursor(char_to_replace); + } else if clear_type == 2 { + self.clear_all(char_to_replace); + } + }); // TODO: implement 1 } else if c == 'H' || c == 'f' { // goto row/col // we subtract 1 from the row/column because these are 1 indexed - // (except when they are 0, in which case they should be 1 - // don't look at me, I don't make the rules) - let (row, col) = if params.len() == 1 { - if params[0] == 0 { - (0, params[0] as usize) - } else { - ((params[0] as usize).saturating_sub(1), params[0] as usize) - } - } else if params[0] == 0 { - (0, (params[1] as usize).saturating_sub(1)) - } else { - ( - (params[0] as usize).saturating_sub(1), - (params[1] as usize).saturating_sub(1), - ) - }; + let row = next_param_or(1).saturating_sub(1); + let col = next_param_or(1).saturating_sub(1); let pad_character = EMPTY_TERMINAL_CHARACTER; self.move_cursor_to(col, row, pad_character); } else if c == 'A' { // move cursor up until edge of screen - let move_up_count = if params[0] == 0 { 1 } else { params[0] }; + let move_up_count = next_param_or(1); self.move_cursor_up(move_up_count as usize); } else if c == 'B' { // move cursor down until edge of screen - let move_down_count = if params[0] == 0 { 1 } else { params[0] }; + let move_down_count = next_param_or(1); let pad_character = EMPTY_TERMINAL_CHARACTER; self.move_cursor_down(move_down_count as usize, pad_character); } else if c == 'D' { - let move_back_count = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; + let move_back_count = next_param_or(1); self.move_cursor_back(move_back_count); } else if c == 'l' { let first_intermediate_is_questionmark = match _intermediates.get(0) { @@ -1064,8 +1055,8 @@ impl vte::Perform for Grid { _ => false, }; if first_intermediate_is_questionmark { - match params.get(0) { - Some(&1049) => { + match params_iter.next().map(|param| param[0]) { + Some(1049) => { if let Some(( alternative_lines_above, alternative_viewport, @@ -1081,29 +1072,29 @@ impl vte::Perform for Grid { self.change_size(self.height, self.width); // the alternative_viewport might have been of a different size... self.mark_for_rerender(); } - Some(&25) => { + Some(25) => { self.hide_cursor(); self.mark_for_rerender(); } - Some(&1) => { + Some(1) => { self.cursor_key_mode = false; } - Some(&3) => { + Some(3) => { // DECCOLM - only side effects self.scroll_region = None; self.clear_all(EMPTY_TERMINAL_CHARACTER); self.cursor.x = 0; self.cursor.y = 0; } - Some(&6) => { + Some(6) => { self.erasure_mode = false; } - Some(&7) => { + Some(7) => { self.disable_linewrap = true; } _ => {} }; - } else if let Some(&4) = params.get(0) { + } else if let Some(4) = params_iter.next().map(|param| param[0]) { self.insert_mode = false; } } else if c == 'h' { @@ -1113,12 +1104,12 @@ impl vte::Perform for Grid { _ => false, }; if first_intermediate_is_questionmark { - match params.get(0) { - Some(&25) => { + match params_iter.next().map(|param| param[0]) { + Some(25) => { self.show_cursor(); self.mark_for_rerender(); } - Some(&1049) => { + Some(1049) => { let current_lines_above = std::mem::replace( &mut self.lines_above, VecDeque::with_capacity(SCROLL_BACK), @@ -1130,99 +1121,72 @@ impl vte::Perform for Grid { Some((current_lines_above, current_viewport, current_cursor)); self.clear_viewport_before_rendering = true; } - Some(&1) => { + Some(1) => { self.cursor_key_mode = true; } - Some(&3) => { + Some(3) => { // DECCOLM - only side effects self.scroll_region = None; self.clear_all(EMPTY_TERMINAL_CHARACTER); self.cursor.x = 0; self.cursor.y = 0; } - Some(&6) => { + Some(6) => { self.erasure_mode = true; } - Some(&7) => { + Some(7) => { self.disable_linewrap = false; } _ => {} }; - } else if let Some(&4) = params.get(0) { + } else if let Some(4) = params_iter.next().map(|param| param[0]) { self.insert_mode = true; } } else if c == 'r' { if params.len() > 1 { - // minus 1 because these are 1 indexed - let top_line_index = (params[0] as usize).saturating_sub(1); - let bottom_line_index = (params[1] as usize).saturating_sub(1); - self.set_scroll_region(top_line_index, bottom_line_index); + let top = (next_param_or(1) as usize).saturating_sub(1); + let bottom = + params_iter.next().map(|param| param[0] as usize).filter(|¶m| param != 0).map(|bottom| bottom.saturating_sub(1)); + self.set_scroll_region(top, bottom); if self.erasure_mode { - self.move_cursor_to_line(top_line_index, EMPTY_TERMINAL_CHARACTER); + self.move_cursor_to_line(top, EMPTY_TERMINAL_CHARACTER); self.move_cursor_to_beginning_of_line(); } - self.show_cursor(); } else { self.clear_scroll_region(); } } else if c == 'M' { // delete lines if currently inside scroll region - let line_count_to_delete = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; + let line_count_to_delete = next_param_or(1); let pad_character = EMPTY_TERMINAL_CHARACTER; self.delete_lines_in_scroll_region(line_count_to_delete, pad_character); } else if c == 'L' { // insert blank lines if inside scroll region - let line_count_to_add = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; + let line_count_to_add = next_param_or(1); let pad_character = EMPTY_TERMINAL_CHARACTER; self.add_empty_lines_in_scroll_region(line_count_to_add, pad_character); - } 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 - 1 - }; + let column = next_param_or(1).saturating_sub(1); self.move_cursor_to_column(column); } else if c == 'g' { - if params[0] == 0 { + let clear_type = next_param_or(0); + if clear_type == 0 { self.clear_tabstop(self.cursor.x); - } else if params[0] == 3 { + } else if clear_type == 3 { self.clear_all_tabstops(); } } else if c == 'd' { // goto line - let line = if params[0] == 0 { - 1 - } else { - // minus 1 because this is 1 indexed - params[0] as usize - 1 - }; + let line = next_param_or(1).saturating_sub(1); let pad_character = EMPTY_TERMINAL_CHARACTER; self.move_cursor_to_line(line, pad_character); } else if c == 'P' { // erase characters - let count = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; + let count = next_param_or(1); self.erase_characters(count, self.cursor.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 - }; + let count = next_param_or(1); self.replace_with_empty_chars(count, self.cursor.pending_styles); } else if c == 'T' { /* @@ -1230,32 +1194,18 @@ impl vte::Perform for Grid { * Scroll down, new lines inserted at top of screen * [4T = Scroll down 4, bring previous lines back into view */ - let line_count: i64 = *params.get(0).expect("A number of lines was expected."); - - if line_count >= 0 { - self.rotate_scroll_region_up(line_count as usize); - } else { - // TODO: can this actually happen? - self.rotate_scroll_region_down(line_count.abs() as usize); - } + let line_count = next_param_or(1); + self.rotate_scroll_region_up(line_count as usize); } else if c == 'S' { // move scroll up - let count = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; + let count = next_param_or(1); self.rotate_scroll_region_down(count); } else if c == 's' { self.save_cursor_position(); } else if c == 'u' { self.restore_cursor_position(); } else if c == '@' { - let count = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; + let count = next_param_or(1); for _ in 0..count { // TODO: should this be styled? self.insert_character_at_cursor_position(EMPTY_TERMINAL_CHARACTER); diff --git a/src/client/panes/terminal_character.rs b/src/client/panes/terminal_character.rs index 31e017b11e..6a55fcb5b3 100644 --- a/src/client/panes/terminal_character.rs +++ b/src/client/panes/terminal_character.rs @@ -3,6 +3,8 @@ use unicode_width::UnicodeWidthChar; use crate::utils::logging::debug_log_to_file; use std::fmt::{self, Debug, Display, Formatter}; use std::ops::{Index, IndexMut}; +use std::convert::TryFrom; +use vte::ParamsIter; pub const EMPTY_TERMINAL_CHARACTER: TerminalCharacter = TerminalCharacter { character: ' ', @@ -327,138 +329,115 @@ impl CharacterStyles { self.hidden = Some(AnsiCode::Reset); self.strike = Some(AnsiCode::Reset); } - pub fn add_style_from_ansi_params(&mut self, ansi_params: &[i64]) { - let mut params_used = 1; // if there's a parameter, it is always used - match ansi_params { - [] | [0, ..] => self.reset_all(), - [1, ..] => *self = self.bold(Some(AnsiCode::On)), - [2, ..] => *self = self.dim(Some(AnsiCode::On)), - [3, ..] => *self = self.italic(Some(AnsiCode::On)), - [4, ..] => *self = self.underline(Some(AnsiCode::On)), - [5, ..] => *self = self.blink_slow(Some(AnsiCode::On)), - [6, ..] => *self = self.blink_fast(Some(AnsiCode::On)), - [7, ..] => *self = self.reverse(Some(AnsiCode::On)), - [8, ..] => *self = self.hidden(Some(AnsiCode::On)), - [9, ..] => *self = self.strike(Some(AnsiCode::On)), - [21, ..] => *self = self.bold(Some(AnsiCode::Reset)), - [22, ..] => { - *self = self.bold(Some(AnsiCode::Reset)); - *self = self.dim(Some(AnsiCode::Reset)); - } - [23, ..] => *self = self.italic(Some(AnsiCode::Reset)), - [24, ..] => *self = self.underline(Some(AnsiCode::Reset)), - [25, ..] => { - *self = self.blink_slow(Some(AnsiCode::Reset)); - *self = self.blink_fast(Some(AnsiCode::Reset)); - } - [27, ..] => *self = self.reverse(Some(AnsiCode::Reset)), - [28, ..] => *self = self.hidden(Some(AnsiCode::Reset)), - [29, ..] => *self = self.strike(Some(AnsiCode::Reset)), - [30, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Black))), - [31, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Red))), - [32, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Green))), - [33, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Yellow))), - [34, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Blue))), - [35, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Magenta))), - [36, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Cyan))), - [37, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::White))), - [38, 2, ..] => { - let ansi_code = AnsiCode::RgbCode(( - *ansi_params.get(2).unwrap() as u8, - *ansi_params.get(3).unwrap() as u8, - *ansi_params.get(4).unwrap() as u8, - )); - *self = self.foreground(Some(ansi_code)); - params_used += 4 // one for the indicator (2 in this case) and three for the rgb code - } - [38, 5, ..] => { - let ansi_code = AnsiCode::ColorIndex(*ansi_params.get(2).unwrap() as u8); - *self = self.foreground(Some(ansi_code)); - params_used += 2 // one for the indicator (5 in this case) and one for the color index - } - [38, ..] => { - // this is a bug - // it means we got a color encoding we don't know how to handle (or is invalid) - params_used += 1; // even if it's a bug, let's not create an endless loop, eh? - } - [39, ..] => *self = self.foreground(Some(AnsiCode::Reset)), - [40, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Black))), - [41, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Red))), - [42, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Green))), - [43, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Yellow))), - [44, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Blue))), - [45, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Magenta))), - [46, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Cyan))), - [47, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::White))), - [48, 2, ..] => { - let ansi_code = AnsiCode::RgbCode(( - *ansi_params.get(2).unwrap() as u8, - *ansi_params.get(3).unwrap() as u8, - *ansi_params.get(4).unwrap() as u8, - )); - *self = self.background(Some(ansi_code)); - params_used += 4 // one for the indicator (2 in this case) and three for the rgb code - } - [48, 5, ..] => { - let ansi_code = AnsiCode::ColorIndex(*ansi_params.get(2).unwrap() as u8); - *self = self.background(Some(ansi_code)); - params_used += 2 // one for the indicator (5 in this case) and one for the color index - } - [48, ..] => { - // this is a bug - // it means we got a color encoding we don't know how to handle (or is invalid) - params_used += 1; // even if it's a bug, let's not create an endless loop, eh? - } - [49, ..] => *self = self.background(Some(AnsiCode::Reset)), - [90, ..] => { - *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlack))) - } - [91, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightRed))), - [92, ..] => { - *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightGreen))) - } - [93, ..] => { - *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightYellow))) - } - [94, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlue))), - [95, ..] => { - *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta))) - } - [96, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightCyan))), - [97, ..] => { - *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightWhite))) - } - [100, ..] => { - *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlack))) - } - [101, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightRed))), - [102, ..] => { - *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightGreen))) - } - [103, ..] => { - *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightYellow))) - } - [104, ..] => { - *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlue))) - } - [105, ..] => { - *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta))) - } - [106, ..] => { - *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightCyan))) - } - [107, ..] => { - *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightWhite))) - } - _ => { - // if this happens, it's a bug - let _ = debug_log_to_file(format!("unhandled csi m code {:?}", ansi_params)); - return; - } - } - if let Some(next_params) = ansi_params.get(params_used..) { - if !next_params.is_empty() { - self.add_style_from_ansi_params(next_params); + pub fn add_style_from_ansi_params(&mut self, params: &mut ParamsIter) { + while let Some(param) = params.next() { + match param { + [] | [0] => self.reset_all(), + [1] => *self = self.bold(Some(AnsiCode::On)), + [2] => *self = self.dim(Some(AnsiCode::On)), + [3] => *self = self.italic(Some(AnsiCode::On)), + [4] => *self = self.underline(Some(AnsiCode::On)), + [5] => *self = self.blink_slow(Some(AnsiCode::On)), + [6] => *self = self.blink_fast(Some(AnsiCode::On)), + [7] => *self = self.reverse(Some(AnsiCode::On)), + [8] => *self = self.hidden(Some(AnsiCode::On)), + [9] => *self = self.strike(Some(AnsiCode::On)), + [21] => *self = self.bold(Some(AnsiCode::Reset)), + [22] => { + *self = self.bold(Some(AnsiCode::Reset)); + *self = self.dim(Some(AnsiCode::Reset)); + } + [23] => *self = self.italic(Some(AnsiCode::Reset)), + [24] => *self = self.underline(Some(AnsiCode::Reset)), + [25] => { + *self = self.blink_slow(Some(AnsiCode::Reset)); + *self = self.blink_fast(Some(AnsiCode::Reset)); + } + [27] => *self = self.reverse(Some(AnsiCode::Reset)), + [28] => *self = self.hidden(Some(AnsiCode::Reset)), + [29] => *self = self.strike(Some(AnsiCode::Reset)), + [30] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Black))), + [31] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Red))), + [32] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Green))), + [33] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Yellow))), + [34] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Blue))), + [35] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Magenta))), + [36] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Cyan))), + [37] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::White))), + [38] => { + let mut iter = params.map(|param| param[0]); + parse_sgr_color(&mut iter).map(|ansi_code| *self = self.foreground(Some(ansi_code))); + } + [38, params @ ..] => { + let rgb_start = if params.len() > 4 { 2 } else { 1 }; + let rgb_iter = params[rgb_start..].iter().copied(); + let mut iter = std::iter::once(params[0]).chain(rgb_iter); + parse_sgr_color(&mut iter).map(|ansi_code| *self = self.foreground(Some(ansi_code))); + } + [39] => *self = self.foreground(Some(AnsiCode::Reset)), + [40] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Black))), + [41] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Red))), + [42] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Green))), + [43] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Yellow))), + [44] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Blue))), + [45] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Magenta))), + [46] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Cyan))), + [47] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::White))), + [48] => { + let mut iter = params.map(|param| param[0]); + parse_sgr_color(&mut iter).map(|ansi_code| *self = self.background(Some(ansi_code))); + } + [48, params @ ..] => { + let rgb_start = if params.len() > 4 { 2 } else { 1 }; + let rgb_iter = params[rgb_start..].iter().copied(); + let mut iter = std::iter::once(params[0]).chain(rgb_iter); + parse_sgr_color(&mut iter).map(|ansi_code| *self = self.background(Some(ansi_code))); + } + [49] => *self = self.background(Some(AnsiCode::Reset)), + [90] => { + *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlack))) + } + [91] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightRed))), + [92] => { + *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightGreen))) + } + [93] => { + *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightYellow))) + } + [94] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlue))), + [95] => { + *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta))) + } + [96] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightCyan))), + [97] => { + *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightWhite))) + } + [100] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlack))) + } + [101] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightRed))), + [102] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightGreen))) + } + [103] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightYellow))) + } + [104] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlue))) + } + [105] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta))) + } + [106] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightCyan))) + } + [107] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightWhite))) + } + _ => { + let _ = debug_log_to_file(format!("unhandled csi m code {:?}", param)); + return; + } } } } @@ -756,3 +735,16 @@ impl TerminalCharacter { self.width() > 1 } } + + +fn parse_sgr_color(params: &mut dyn Iterator) -> Option { + match params.next() { + Some(2) => Some(AnsiCode::RgbCode(( + u8::try_from(params.next()?).ok()?, + u8::try_from(params.next()?).ok()?, + u8::try_from(params.next()?).ok()?, + ))), + Some(5) => Some(AnsiCode::ColorIndex(u8::try_from(params.next()?).ok()?)), + _ => None, + } +} diff --git a/src/tests/integration/snapshots/zellij__tests__integration__compatibility__htop_scrolling.snap b/src/tests/integration/snapshots/zellij__tests__integration__compatibility__htop_scrolling.snap index 6e34a96981..beffef7c53 100644 --- a/src/tests/integration/snapshots/zellij__tests__integration__compatibility__htop_scrolling.snap +++ b/src/tests/integration/snapshots/zellij__tests__integration__compatibility__htop_scrolling.snap @@ -3,7 +3,7 @@ source: src/tests/integration/compatibility.rs expression: snapshot_before_quit --- -█ + 1 [||||||||||||||||||||||||||||||||||||||||||100.0%] Tasks: 79, 382 thr; 1 running 2 [ 0.0%] Load average: 1.40 1.43 1.38 3 [ 0.0%] Uptime: 2 days, 07:33:50 From 6d61f7d0451a2cccc49c6738d3246ea08de91569 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 7 May 2021 15:51:53 +0200 Subject: [PATCH 2/2] style(fmt): rustfmt and clippy --- src/client/panes/grid.rs | 28 +++++++++++++-------- src/client/panes/terminal_character.rs | 35 +++++++++++++++----------- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/client/panes/grid.rs b/src/client/panes/grid.rs index 96eb4e00a1..824c83844c 100644 --- a/src/client/panes/grid.rs +++ b/src/client/panes/grid.rs @@ -4,7 +4,7 @@ use std::{ fmt::{self, Debug, Formatter}, }; -use vte::{Perform, Params}; +use vte::{Params, Perform}; const TABSTOP_WIDTH: usize = 8; // TODO: is this always right? const SCROLL_BACK: usize = 10_000; @@ -986,10 +986,13 @@ impl Perform for Grid { } fn csi_dispatch(&mut self, params: &Params, _intermediates: &[u8], _ignore: bool, c: char) { - - let mut params_iter = params.iter(); + let mut params_iter = params.iter(); let mut next_param_or = |default: u16| { - params_iter.next().map(|param| param[0]).filter(|¶m| param != 0).unwrap_or(default) as usize + params_iter + .next() + .map(|param| param[0]) + .filter(|¶m| param != 0) + .unwrap_or(default) as usize }; if c == 'm' { self.cursor @@ -1001,7 +1004,7 @@ impl Perform for Grid { self.move_cursor_forward_until_edge(move_by); } else if c == 'K' { // clear line (0 => right, 1 => left, 2 => all) - params_iter.next().map(|param| param[0]).map(|clear_type| { + if let Some(clear_type) = params_iter.next().map(|param| param[0]) { if clear_type == 0 { let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; char_to_replace.styles = self.cursor.pending_styles; @@ -1013,13 +1016,13 @@ impl Perform for Grid { } else if clear_type == 2 { self.clear_cursor_line(); } - }); + }; } else if c == 'J' { // clear all (0 => below, 1 => above, 2 => all, 3 => saved) let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; char_to_replace.styles = self.cursor.pending_styles; - - params_iter.next().map(|param| param[0]).map(|clear_type| { + + if let Some(clear_type) = params_iter.next().map(|param| param[0]) { if clear_type == 0 { self.clear_all_after_cursor(char_to_replace); } else if clear_type == 1 { @@ -1027,7 +1030,7 @@ impl Perform for Grid { } else if clear_type == 2 { self.clear_all(char_to_replace); } - }); + }; // TODO: implement 1 } else if c == 'H' || c == 'f' { // goto row/col @@ -1145,8 +1148,11 @@ impl Perform for Grid { } else if c == 'r' { if params.len() > 1 { let top = (next_param_or(1) as usize).saturating_sub(1); - let bottom = - params_iter.next().map(|param| param[0] as usize).filter(|¶m| param != 0).map(|bottom| bottom.saturating_sub(1)); + let bottom = params_iter + .next() + .map(|param| param[0] as usize) + .filter(|¶m| param != 0) + .map(|bottom| bottom.saturating_sub(1)); self.set_scroll_region(top, bottom); if self.erasure_mode { self.move_cursor_to_line(top, EMPTY_TERMINAL_CHARACTER); diff --git a/src/client/panes/terminal_character.rs b/src/client/panes/terminal_character.rs index 6a55fcb5b3..c7c616c179 100644 --- a/src/client/panes/terminal_character.rs +++ b/src/client/panes/terminal_character.rs @@ -1,9 +1,9 @@ use unicode_width::UnicodeWidthChar; use crate::utils::logging::debug_log_to_file; +use std::convert::TryFrom; use std::fmt::{self, Debug, Display, Formatter}; use std::ops::{Index, IndexMut}; -use std::convert::TryFrom; use vte::ParamsIter; pub const EMPTY_TERMINAL_CHARACTER: TerminalCharacter = TerminalCharacter { @@ -366,13 +366,17 @@ impl CharacterStyles { [37] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::White))), [38] => { let mut iter = params.map(|param| param[0]); - parse_sgr_color(&mut iter).map(|ansi_code| *self = self.foreground(Some(ansi_code))); + if let Some(ansi_code) = parse_sgr_color(&mut iter) { + *self = self.foreground(Some(ansi_code)); + } } - [38, params @ ..] => { - let rgb_start = if params.len() > 4 { 2 } else { 1 }; - let rgb_iter = params[rgb_start..].iter().copied(); - let mut iter = std::iter::once(params[0]).chain(rgb_iter); - parse_sgr_color(&mut iter).map(|ansi_code| *self = self.foreground(Some(ansi_code))); + [38, params @ ..] => { + let rgb_start = if params.len() > 4 { 2 } else { 1 }; + let rgb_iter = params[rgb_start..].iter().copied(); + let mut iter = std::iter::once(params[0]).chain(rgb_iter); + if let Some(ansi_code) = parse_sgr_color(&mut iter) { + *self = self.foreground(Some(ansi_code)); + } } [39] => *self = self.foreground(Some(AnsiCode::Reset)), [40] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Black))), @@ -385,13 +389,17 @@ impl CharacterStyles { [47] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::White))), [48] => { let mut iter = params.map(|param| param[0]); - parse_sgr_color(&mut iter).map(|ansi_code| *self = self.background(Some(ansi_code))); + if let Some(ansi_code) = parse_sgr_color(&mut iter) { + *self = self.background(Some(ansi_code)); + } } - [48, params @ ..] => { - let rgb_start = if params.len() > 4 { 2 } else { 1 }; - let rgb_iter = params[rgb_start..].iter().copied(); - let mut iter = std::iter::once(params[0]).chain(rgb_iter); - parse_sgr_color(&mut iter).map(|ansi_code| *self = self.background(Some(ansi_code))); + [48, params @ ..] => { + let rgb_start = if params.len() > 4 { 2 } else { 1 }; + let rgb_iter = params[rgb_start..].iter().copied(); + let mut iter = std::iter::once(params[0]).chain(rgb_iter); + if let Some(ansi_code) = parse_sgr_color(&mut iter) { + *self = self.background(Some(ansi_code)); + } } [49] => *self = self.background(Some(AnsiCode::Reset)), [90] => { @@ -736,7 +744,6 @@ impl TerminalCharacter { } } - fn parse_sgr_color(params: &mut dyn Iterator) -> Option { match params.next() { Some(2) => Some(AnsiCode::RgbCode((