fix(compatibility): upgrade vte to support csi subparameters (#469)

* fix(compatibility): upgrade vte to support csi subparameters

* style(fmt): rustfmt and clippy
This commit is contained in:
Aram Drevekenin 2021-05-07 16:03:45 +02:00 committed by GitHub
parent 62991f138b
commit 632a7a3209
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 218 additions and 263 deletions

6
Cargo.lock generated
View file

@ -1871,9 +1871,9 @@ dependencies = [
[[package]] [[package]]
name = "vte" name = "vte"
version = "0.8.0" version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96cc8a191608603611e78c6ec11dafef37e3cca0775aeef1931824753e81711d" checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"utf8parse 0.2.0", "utf8parse 0.2.0",
@ -2282,7 +2282,7 @@ dependencies = [
"termios", "termios",
"unicode-truncate", "unicode-truncate",
"unicode-width", "unicode-width",
"vte 0.8.0", "vte 0.10.1",
"wasmer", "wasmer",
"wasmer-wasi", "wasmer-wasi",
"zellij-tile", "zellij-tile",

View file

@ -30,7 +30,7 @@ termion = "1.5.0"
termios = "0.3" termios = "0.3"
unicode-truncate = "0.2.0" unicode-truncate = "0.2.0"
unicode-width = "0.1.8" unicode-width = "0.1.8"
vte = "0.8.0" vte = "0.10.1"
strum = "0.20.0" strum = "0.20.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
wasmer = "1.0.0" wasmer = "1.0.0"

View file

@ -4,6 +4,8 @@ use std::{
fmt::{self, Debug, Formatter}, fmt::{self, Debug, Formatter},
}; };
use vte::{Params, Perform};
const TABSTOP_WIDTH: usize = 8; // TODO: is this always right? const TABSTOP_WIDTH: usize = 8; // TODO: is this always right?
const SCROLL_BACK: usize = 10_000; const SCROLL_BACK: usize = 10_000;
@ -811,7 +813,8 @@ impl Grid {
pub fn show_cursor(&mut self) { pub fn show_cursor(&mut self) {
self.cursor.is_hidden = false; 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<usize>) {
let bottom_line_index = bottom_line_index.unwrap_or(self.height);
self.scroll_region = Some((top_line_index, bottom_line_index)); self.scroll_region = Some((top_line_index, bottom_line_index));
} }
pub fn clear_scroll_region(&mut self) { 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) { fn print(&mut self, c: char) {
let c = self.cursor.charsets[self.active_charset].map(c); let c = self.cursor.charsets[self.active_charset].map(c);
// apparently, building TerminalCharacter like this without a "new" method // 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 // TBD
} }
@ -982,80 +985,71 @@ impl vte::Perform for Grid {
// TBD // 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(|&param| param != 0)
.unwrap_or(default) as usize
};
if c == 'm' { if c == 'm' {
self.cursor self.cursor
.pending_styles .pending_styles
.add_style_from_ansi_params(params); .add_style_from_ansi_params(&mut params_iter);
} else if c == 'C' { } else if c == 'C' {
// move cursor forward // move cursor forward
let move_by = if params[0] == 0 { let move_by = next_param_or(1);
1
} else {
params[0] as usize
};
self.move_cursor_forward_until_edge(move_by); self.move_cursor_forward_until_edge(move_by);
} else if c == 'K' { } else if c == 'K' {
// clear line (0 => right, 1 => left, 2 => all) // clear line (0 => right, 1 => left, 2 => all)
if params[0] == 0 { if let Some(clear_type) = params_iter.next().map(|param| param[0]) {
if clear_type == 0 {
let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
char_to_replace.styles = self.cursor.pending_styles; char_to_replace.styles = self.cursor.pending_styles;
self.replace_characters_in_line_after_cursor(char_to_replace); self.replace_characters_in_line_after_cursor(char_to_replace);
} else if params[0] == 1 { } else if clear_type == 1 {
let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
char_to_replace.styles = self.cursor.pending_styles; char_to_replace.styles = self.cursor.pending_styles;
self.replace_characters_in_line_before_cursor(char_to_replace); self.replace_characters_in_line_before_cursor(char_to_replace);
} else if params[0] == 2 { } else if clear_type == 2 {
self.clear_cursor_line(); self.clear_cursor_line();
} }
};
} else if c == 'J' { } else if c == 'J' {
// clear all (0 => below, 1 => above, 2 => all, 3 => saved) // clear all (0 => below, 1 => above, 2 => all, 3 => saved)
let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
char_to_replace.styles = self.cursor.pending_styles; char_to_replace.styles = self.cursor.pending_styles;
if params[0] == 0 {
if let Some(clear_type) = params_iter.next().map(|param| param[0]) {
if clear_type == 0 {
self.clear_all_after_cursor(char_to_replace); self.clear_all_after_cursor(char_to_replace);
} else if params[0] == 1 { } else if clear_type == 1 {
self.clear_all_before_cursor(char_to_replace); self.clear_all_before_cursor(char_to_replace);
} else if params[0] == 2 { } else if clear_type == 2 {
self.clear_all(char_to_replace); self.clear_all(char_to_replace);
} }
};
// TODO: implement 1 // TODO: implement 1
} else if c == 'H' || c == 'f' { } else if c == 'H' || c == 'f' {
// goto row/col // goto row/col
// we subtract 1 from the row/column because these are 1 indexed // we subtract 1 from the row/column because these are 1 indexed
// (except when they are 0, in which case they should be 1 let row = next_param_or(1).saturating_sub(1);
// don't look at me, I don't make the rules) let col = next_param_or(1).saturating_sub(1);
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 pad_character = EMPTY_TERMINAL_CHARACTER; let pad_character = EMPTY_TERMINAL_CHARACTER;
self.move_cursor_to(col, row, pad_character); self.move_cursor_to(col, row, pad_character);
} else if c == 'A' { } else if c == 'A' {
// move cursor up until edge of screen // 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); self.move_cursor_up(move_up_count as usize);
} else if c == 'B' { } else if c == 'B' {
// move cursor down until edge of screen // 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; let pad_character = EMPTY_TERMINAL_CHARACTER;
self.move_cursor_down(move_down_count as usize, pad_character); self.move_cursor_down(move_down_count as usize, pad_character);
} else if c == 'D' { } else if c == 'D' {
let move_back_count = if params[0] == 0 { let move_back_count = next_param_or(1);
1
} else {
params[0] as usize
};
self.move_cursor_back(move_back_count); self.move_cursor_back(move_back_count);
} else if c == 'l' { } else if c == 'l' {
let first_intermediate_is_questionmark = match _intermediates.get(0) { let first_intermediate_is_questionmark = match _intermediates.get(0) {
@ -1064,8 +1058,8 @@ impl vte::Perform for Grid {
_ => false, _ => false,
}; };
if first_intermediate_is_questionmark { if first_intermediate_is_questionmark {
match params.get(0) { match params_iter.next().map(|param| param[0]) {
Some(&1049) => { Some(1049) => {
if let Some(( if let Some((
alternative_lines_above, alternative_lines_above,
alternative_viewport, alternative_viewport,
@ -1081,29 +1075,29 @@ impl vte::Perform for Grid {
self.change_size(self.height, self.width); // the alternative_viewport might have been of a different size... self.change_size(self.height, self.width); // the alternative_viewport might have been of a different size...
self.mark_for_rerender(); self.mark_for_rerender();
} }
Some(&25) => { Some(25) => {
self.hide_cursor(); self.hide_cursor();
self.mark_for_rerender(); self.mark_for_rerender();
} }
Some(&1) => { Some(1) => {
self.cursor_key_mode = false; self.cursor_key_mode = false;
} }
Some(&3) => { Some(3) => {
// DECCOLM - only side effects // DECCOLM - only side effects
self.scroll_region = None; self.scroll_region = None;
self.clear_all(EMPTY_TERMINAL_CHARACTER); self.clear_all(EMPTY_TERMINAL_CHARACTER);
self.cursor.x = 0; self.cursor.x = 0;
self.cursor.y = 0; self.cursor.y = 0;
} }
Some(&6) => { Some(6) => {
self.erasure_mode = false; self.erasure_mode = false;
} }
Some(&7) => { Some(7) => {
self.disable_linewrap = true; 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; self.insert_mode = false;
} }
} else if c == 'h' { } else if c == 'h' {
@ -1113,12 +1107,12 @@ impl vte::Perform for Grid {
_ => false, _ => false,
}; };
if first_intermediate_is_questionmark { if first_intermediate_is_questionmark {
match params.get(0) { match params_iter.next().map(|param| param[0]) {
Some(&25) => { Some(25) => {
self.show_cursor(); self.show_cursor();
self.mark_for_rerender(); self.mark_for_rerender();
} }
Some(&1049) => { Some(1049) => {
let current_lines_above = std::mem::replace( let current_lines_above = std::mem::replace(
&mut self.lines_above, &mut self.lines_above,
VecDeque::with_capacity(SCROLL_BACK), VecDeque::with_capacity(SCROLL_BACK),
@ -1130,99 +1124,75 @@ impl vte::Perform for Grid {
Some((current_lines_above, current_viewport, current_cursor)); Some((current_lines_above, current_viewport, current_cursor));
self.clear_viewport_before_rendering = true; self.clear_viewport_before_rendering = true;
} }
Some(&1) => { Some(1) => {
self.cursor_key_mode = true; self.cursor_key_mode = true;
} }
Some(&3) => { Some(3) => {
// DECCOLM - only side effects // DECCOLM - only side effects
self.scroll_region = None; self.scroll_region = None;
self.clear_all(EMPTY_TERMINAL_CHARACTER); self.clear_all(EMPTY_TERMINAL_CHARACTER);
self.cursor.x = 0; self.cursor.x = 0;
self.cursor.y = 0; self.cursor.y = 0;
} }
Some(&6) => { Some(6) => {
self.erasure_mode = true; self.erasure_mode = true;
} }
Some(&7) => { Some(7) => {
self.disable_linewrap = false; 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; self.insert_mode = true;
} }
} else if c == 'r' { } else if c == 'r' {
if params.len() > 1 { if params.len() > 1 {
// minus 1 because these are 1 indexed let top = (next_param_or(1) as usize).saturating_sub(1);
let top_line_index = (params[0] as usize).saturating_sub(1); let bottom = params_iter
let bottom_line_index = (params[1] as usize).saturating_sub(1); .next()
self.set_scroll_region(top_line_index, bottom_line_index); .map(|param| param[0] as usize)
.filter(|&param| param != 0)
.map(|bottom| bottom.saturating_sub(1));
self.set_scroll_region(top, bottom);
if self.erasure_mode { 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.move_cursor_to_beginning_of_line();
} }
self.show_cursor();
} else { } else {
self.clear_scroll_region(); self.clear_scroll_region();
} }
} else if c == 'M' { } else if c == 'M' {
// delete lines if currently inside scroll region // delete lines if currently inside scroll region
let line_count_to_delete = if params[0] == 0 { let line_count_to_delete = next_param_or(1);
1
} else {
params[0] as usize
};
let pad_character = EMPTY_TERMINAL_CHARACTER; let pad_character = EMPTY_TERMINAL_CHARACTER;
self.delete_lines_in_scroll_region(line_count_to_delete, pad_character); self.delete_lines_in_scroll_region(line_count_to_delete, pad_character);
} else if c == 'L' { } else if c == 'L' {
// insert blank lines if inside scroll region // insert blank lines if inside scroll region
let line_count_to_add = if params[0] == 0 { let line_count_to_add = next_param_or(1);
1
} else {
params[0] as usize
};
let pad_character = EMPTY_TERMINAL_CHARACTER; let pad_character = EMPTY_TERMINAL_CHARACTER;
self.add_empty_lines_in_scroll_region(line_count_to_add, pad_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' { } else if c == 'G' {
let column = if params[0] == 0 { let column = next_param_or(1).saturating_sub(1);
0
} else {
params[0] as usize - 1
};
self.move_cursor_to_column(column); self.move_cursor_to_column(column);
} else if c == 'g' { } 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); self.clear_tabstop(self.cursor.x);
} else if params[0] == 3 { } else if clear_type == 3 {
self.clear_all_tabstops(); self.clear_all_tabstops();
} }
} else if c == 'd' { } else if c == 'd' {
// goto line // goto line
let line = if params[0] == 0 { let line = next_param_or(1).saturating_sub(1);
1
} else {
// minus 1 because this is 1 indexed
params[0] as usize - 1
};
let pad_character = EMPTY_TERMINAL_CHARACTER; let pad_character = EMPTY_TERMINAL_CHARACTER;
self.move_cursor_to_line(line, pad_character); self.move_cursor_to_line(line, pad_character);
} else if c == 'P' { } else if c == 'P' {
// erase characters // erase characters
let count = if params[0] == 0 { let count = next_param_or(1);
1
} else {
params[0] as usize
};
self.erase_characters(count, self.cursor.pending_styles); self.erase_characters(count, self.cursor.pending_styles);
} else if c == 'X' { } else if c == 'X' {
// erase characters and replace with empty characters of current style // erase characters and replace with empty characters of current style
let count = if params[0] == 0 { let count = next_param_or(1);
1
} else {
params[0] as usize
};
self.replace_with_empty_chars(count, self.cursor.pending_styles); self.replace_with_empty_chars(count, self.cursor.pending_styles);
} else if c == 'T' { } else if c == 'T' {
/* /*
@ -1230,32 +1200,18 @@ impl vte::Perform for Grid {
* Scroll down, new lines inserted at top of screen * Scroll down, new lines inserted at top of screen
* [4T = Scroll down 4, bring previous lines back into view * [4T = Scroll down 4, bring previous lines back into view
*/ */
let line_count: i64 = *params.get(0).expect("A number of lines was expected."); let line_count = next_param_or(1);
if line_count >= 0 {
self.rotate_scroll_region_up(line_count as usize); 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);
}
} else if c == 'S' { } else if c == 'S' {
// move scroll up // move scroll up
let count = if params[0] == 0 { let count = next_param_or(1);
1
} else {
params[0] as usize
};
self.rotate_scroll_region_down(count); self.rotate_scroll_region_down(count);
} else if c == 's' { } else if c == 's' {
self.save_cursor_position(); self.save_cursor_position();
} else if c == 'u' { } else if c == 'u' {
self.restore_cursor_position(); self.restore_cursor_position();
} else if c == '@' { } else if c == '@' {
let count = if params[0] == 0 { let count = next_param_or(1);
1
} else {
params[0] as usize
};
for _ in 0..count { for _ in 0..count {
// TODO: should this be styled? // TODO: should this be styled?
self.insert_character_at_cursor_position(EMPTY_TERMINAL_CHARACTER); self.insert_character_at_cursor_position(EMPTY_TERMINAL_CHARACTER);

View file

@ -1,8 +1,10 @@
use unicode_width::UnicodeWidthChar; use unicode_width::UnicodeWidthChar;
use crate::utils::logging::debug_log_to_file; use crate::utils::logging::debug_log_to_file;
use std::convert::TryFrom;
use std::fmt::{self, Debug, Display, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
use vte::ParamsIter;
pub const EMPTY_TERMINAL_CHARACTER: TerminalCharacter = TerminalCharacter { pub const EMPTY_TERMINAL_CHARACTER: TerminalCharacter = TerminalCharacter {
character: ' ', character: ' ',
@ -327,139 +329,124 @@ impl CharacterStyles {
self.hidden = Some(AnsiCode::Reset); self.hidden = Some(AnsiCode::Reset);
self.strike = Some(AnsiCode::Reset); self.strike = Some(AnsiCode::Reset);
} }
pub fn add_style_from_ansi_params(&mut self, ansi_params: &[i64]) { pub fn add_style_from_ansi_params(&mut self, params: &mut ParamsIter) {
let mut params_used = 1; // if there's a parameter, it is always used while let Some(param) = params.next() {
match ansi_params { match param {
[] | [0, ..] => self.reset_all(), [] | [0] => self.reset_all(),
[1, ..] => *self = self.bold(Some(AnsiCode::On)), [1] => *self = self.bold(Some(AnsiCode::On)),
[2, ..] => *self = self.dim(Some(AnsiCode::On)), [2] => *self = self.dim(Some(AnsiCode::On)),
[3, ..] => *self = self.italic(Some(AnsiCode::On)), [3] => *self = self.italic(Some(AnsiCode::On)),
[4, ..] => *self = self.underline(Some(AnsiCode::On)), [4] => *self = self.underline(Some(AnsiCode::On)),
[5, ..] => *self = self.blink_slow(Some(AnsiCode::On)), [5] => *self = self.blink_slow(Some(AnsiCode::On)),
[6, ..] => *self = self.blink_fast(Some(AnsiCode::On)), [6] => *self = self.blink_fast(Some(AnsiCode::On)),
[7, ..] => *self = self.reverse(Some(AnsiCode::On)), [7] => *self = self.reverse(Some(AnsiCode::On)),
[8, ..] => *self = self.hidden(Some(AnsiCode::On)), [8] => *self = self.hidden(Some(AnsiCode::On)),
[9, ..] => *self = self.strike(Some(AnsiCode::On)), [9] => *self = self.strike(Some(AnsiCode::On)),
[21, ..] => *self = self.bold(Some(AnsiCode::Reset)), [21] => *self = self.bold(Some(AnsiCode::Reset)),
[22, ..] => { [22] => {
*self = self.bold(Some(AnsiCode::Reset)); *self = self.bold(Some(AnsiCode::Reset));
*self = self.dim(Some(AnsiCode::Reset)); *self = self.dim(Some(AnsiCode::Reset));
} }
[23, ..] => *self = self.italic(Some(AnsiCode::Reset)), [23] => *self = self.italic(Some(AnsiCode::Reset)),
[24, ..] => *self = self.underline(Some(AnsiCode::Reset)), [24] => *self = self.underline(Some(AnsiCode::Reset)),
[25, ..] => { [25] => {
*self = self.blink_slow(Some(AnsiCode::Reset)); *self = self.blink_slow(Some(AnsiCode::Reset));
*self = self.blink_fast(Some(AnsiCode::Reset)); *self = self.blink_fast(Some(AnsiCode::Reset));
} }
[27, ..] => *self = self.reverse(Some(AnsiCode::Reset)), [27] => *self = self.reverse(Some(AnsiCode::Reset)),
[28, ..] => *self = self.hidden(Some(AnsiCode::Reset)), [28] => *self = self.hidden(Some(AnsiCode::Reset)),
[29, ..] => *self = self.strike(Some(AnsiCode::Reset)), [29] => *self = self.strike(Some(AnsiCode::Reset)),
[30, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Black))), [30] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Black))),
[31, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Red))), [31] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Red))),
[32, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Green))), [32] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Green))),
[33, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Yellow))), [33] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Yellow))),
[34, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Blue))), [34] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Blue))),
[35, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Magenta))), [35] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Magenta))),
[36, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Cyan))), [36] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Cyan))),
[37, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::White))), [37] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::White))),
[38, 2, ..] => { [38] => {
let ansi_code = AnsiCode::RgbCode(( let mut iter = params.map(|param| param[0]);
*ansi_params.get(2).unwrap() as u8, if let Some(ansi_code) = parse_sgr_color(&mut iter) {
*ansi_params.get(3).unwrap() as u8,
*ansi_params.get(4).unwrap() as u8,
));
*self = self.foreground(Some(ansi_code)); *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); [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)); *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)), [39] => *self = self.foreground(Some(AnsiCode::Reset)),
[40, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Black))), [40] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Black))),
[41, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Red))), [41] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Red))),
[42, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Green))), [42] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Green))),
[43, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Yellow))), [43] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Yellow))),
[44, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Blue))), [44] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Blue))),
[45, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Magenta))), [45] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Magenta))),
[46, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Cyan))), [46] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Cyan))),
[47, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::White))), [47] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::White))),
[48, 2, ..] => { [48] => {
let ansi_code = AnsiCode::RgbCode(( let mut iter = params.map(|param| param[0]);
*ansi_params.get(2).unwrap() as u8, if let Some(ansi_code) = parse_sgr_color(&mut iter) {
*ansi_params.get(3).unwrap() as u8,
*ansi_params.get(4).unwrap() as u8,
));
*self = self.background(Some(ansi_code)); *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); [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)); *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)), [49] => *self = self.background(Some(AnsiCode::Reset)),
[90, ..] => { [90] => {
*self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlack))) *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlack)))
} }
[91, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightRed))), [91] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightRed))),
[92, ..] => { [92] => {
*self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightGreen))) *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightGreen)))
} }
[93, ..] => { [93] => {
*self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightYellow))) *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightYellow)))
} }
[94, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlue))), [94] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlue))),
[95, ..] => { [95] => {
*self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta))) *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta)))
} }
[96, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightCyan))), [96] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightCyan))),
[97, ..] => { [97] => {
*self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightWhite))) *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightWhite)))
} }
[100, ..] => { [100] => {
*self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlack))) *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlack)))
} }
[101, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightRed))), [101] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightRed))),
[102, ..] => { [102] => {
*self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightGreen))) *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightGreen)))
} }
[103, ..] => { [103] => {
*self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightYellow))) *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightYellow)))
} }
[104, ..] => { [104] => {
*self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlue))) *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlue)))
} }
[105, ..] => { [105] => {
*self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta))) *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta)))
} }
[106, ..] => { [106] => {
*self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightCyan))) *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightCyan)))
} }
[107, ..] => { [107] => {
*self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightWhite))) *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 {:?}", param));
let _ = debug_log_to_file(format!("unhandled csi m code {:?}", ansi_params));
return; return;
} }
} }
if let Some(next_params) = ansi_params.get(params_used..) {
if !next_params.is_empty() {
self.add_style_from_ansi_params(next_params);
}
} }
} }
} }
@ -756,3 +743,15 @@ impl TerminalCharacter {
self.width() > 1 self.width() > 1
} }
} }
fn parse_sgr_color(params: &mut dyn Iterator<Item = u16>) -> Option<AnsiCode> {
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,
}
}

View file

@ -3,7 +3,7 @@ source: src/tests/integration/compatibility.rs
expression: snapshot_before_quit expression: snapshot_before_quit
--- ---
1 [||||||||||||||||||||||||||||||||||||||||||100.0%] Tasks: 79, 382 thr; 1 running 1 [||||||||||||||||||||||||||||||||||||||||||100.0%] Tasks: 79, 382 thr; 1 running
2 [ 0.0%] Load average: 1.40 1.43 1.38 2 [ 0.0%] Load average: 1.40 1.43 1.38
3 [ 0.0%] Uptime: 2 days, 07:33:50 3 [ 0.0%] Uptime: 2 days, 07:33:50