diff --git a/src/screen.rs b/src/screen.rs index 0c68bfa4..816af406 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -3,6 +3,7 @@ use std::collections::{HashSet, BTreeMap}; use nix::pty::Winsize; use std::os::unix::io::RawFd; use std::sync::mpsc::{channel, Sender, Receiver}; +use std::time::{Instant, Duration}; use crate::os_input_output::OsApi; use crate::terminal_pane::TerminalOutput; diff --git a/src/terminal_pane.rs b/src/terminal_pane.rs index 34c9c2ca..2b1707b6 100644 --- a/src/terminal_pane.rs +++ b/src/terminal_pane.rs @@ -69,6 +69,469 @@ impl NamedColor { } } +#[derive(Clone, Copy, Debug)] +pub struct CharacterStyles { + pub foreground: Option, + pub background: Option, + pub strike: Option, + pub hidden: Option, + pub reverse: Option, + pub slow_blink: Option, + pub fast_blink: Option, + pub underline: Option, + pub bold: Option, + pub dim: Option, + pub italic: Option, +} + +impl CharacterStyles { + pub fn new() -> Self { + CharacterStyles { + foreground: None, + background: None, + strike: None, + hidden: None, + reverse: None, + slow_blink: None, + fast_blink: None, + underline: None, + bold: None, + dim: None, + italic: None, + } + } + pub fn foreground(mut self, foreground_code: Option) -> Self { + self.foreground = foreground_code; + self + } + pub fn background(mut self, background_code: Option) -> Self { + self.background = background_code; + self + } + pub fn bold(mut self, bold_code: Option) -> Self { + self.bold = bold_code; + self + } + pub fn dim(mut self, dim_code: Option) -> Self { + self.dim = dim_code; + self + } + pub fn italic(mut self, italic_code: Option) -> Self { + self.italic = italic_code; + self + } + pub fn underline(mut self, underline_code: Option) -> Self { + self.underline = underline_code; + self + } + pub fn blink_slow(mut self, slow_blink_code: Option) -> Self { + self.slow_blink = slow_blink_code; + self + } + pub fn blink_fast(mut self, fast_blink_code: Option) -> Self { + self.fast_blink = fast_blink_code; + self + } + pub fn reverse(mut self, reverse_code: Option) -> Self { + self.reverse = reverse_code; + self + } + pub fn hidden(mut self, hidden_code: Option) -> Self { + self.hidden = hidden_code; + self + } + pub fn strike(mut self, strike_code: Option) -> Self { + self.strike = strike_code; + self + } + pub fn clear(&mut self) { + self.foreground = None; + self.background = None; + self.strike = None; + self.hidden = None; + self.reverse = None; + self.slow_blink = None; + self.fast_blink = None; + self.underline = None; + self.bold = None; + self.dim = None; + self.italic = None; + } + pub fn update_and_return_diff(&mut self, new_styles: &TerminalCharacter) -> Option { + let mut diff: Option = None; + if self.foreground != new_styles.foreground { + if let Some(new_diff) = diff.as_mut() { + diff = Some(new_diff.foreground(new_styles.foreground)); + self.foreground = new_styles.foreground; + } else { + diff = Some(CharacterStyles::new().foreground(new_styles.foreground)); + self.foreground = new_styles.foreground; + } + } + if self.background != new_styles.background { + if let Some(new_diff) = diff.as_mut() { + diff = Some(new_diff.background(new_styles.background)); + self.background = new_styles.background; + } else { + diff = Some(CharacterStyles::new().background(new_styles.background)); + self.background = new_styles.background; + } + } + if self.strike != new_styles.strike { + if let Some(new_diff) = diff.as_mut() { + diff = Some(new_diff.strike(new_styles.strike)); + self.strike = new_styles.strike; + } else { + diff = Some(CharacterStyles::new().strike(new_styles.strike)); + self.strike = new_styles.strike; + } + } + if self.hidden != new_styles.hidden { + if let Some(new_diff) = diff.as_mut() { + diff = Some(new_diff.hidden(new_styles.hidden)); + self.hidden = new_styles.hidden; + } else { + diff = Some(CharacterStyles::new().hidden(new_styles.hidden)); + self.hidden = new_styles.hidden; + } + } + if self.reverse != new_styles.reverse { + if let Some(new_diff) = diff.as_mut() { + diff = Some(new_diff.reverse(new_styles.reverse)); + self.reverse= new_styles.reverse; + } else { + diff = Some(CharacterStyles::new().reverse(new_styles.reverse)); + self.reverse= new_styles.reverse; + } + } + if self.slow_blink != new_styles.slow_blink { + if let Some(new_diff) = diff.as_mut() { + diff = Some(new_diff.blink_slow(new_styles.slow_blink)); + self.slow_blink = new_styles.slow_blink; + } else { + diff = Some(CharacterStyles::new().blink_slow(new_styles.slow_blink)); + self.slow_blink = new_styles.slow_blink; + } + } + if self.fast_blink != new_styles.fast_blink { + if let Some(new_diff) = diff.as_mut() { + diff = Some(new_diff.blink_fast(new_styles.fast_blink)); + self.fast_blink = new_styles.fast_blink; + } else { + diff = Some(CharacterStyles::new().blink_fast(new_styles.fast_blink)); + self.fast_blink = new_styles.fast_blink; + } + } + if self.underline != new_styles.underline { + if let Some(new_diff) = diff.as_mut() { + diff = Some(new_diff.underline(new_styles.underline)); + self.underline= new_styles.underline; + } else { + diff = Some(CharacterStyles::new().underline(new_styles.underline)); + self.underline= new_styles.underline; + } + } + if self.bold != new_styles.bold { + if let Some(new_diff) = diff.as_mut() { + diff = Some(new_diff.bold(new_styles.bold)); + self.bold= new_styles.bold; + } else { + diff = Some(CharacterStyles::new().bold(new_styles.bold)); + self.bold= new_styles.bold; + } + } + if self.dim != new_styles.dim { + if let Some(new_diff) = diff.as_mut() { + diff = Some(new_diff.dim(new_styles.dim)); + self.dim= new_styles.dim; + } else { + diff = Some(CharacterStyles::new().dim(new_styles.dim)); + self.dim= new_styles.dim; + } + } + if self.italic != new_styles.italic { + if let Some(new_diff) = diff.as_mut() { + diff = Some(new_diff.italic(new_styles.italic)); + self.italic = new_styles.italic; + } else { + diff = Some(CharacterStyles::new().italic(new_styles.italic)); + self.italic = new_styles.italic; + } + } + diff + } +} + +impl Display for CharacterStyles { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if self.foreground == Some(AnsiCode::Reset) && + self.background == Some(AnsiCode::Reset) && + self.strike == Some(AnsiCode::Reset) && + self.hidden == Some(AnsiCode::Reset) && + self.reverse == Some(AnsiCode::Reset) && + self.fast_blink == Some(AnsiCode::Reset) && + self.slow_blink == Some(AnsiCode::Reset) && + self.underline == Some(AnsiCode::Reset) && + self.bold == Some(AnsiCode::Reset) && + self.dim == Some(AnsiCode::Reset) && + self.italic == Some(AnsiCode::Reset) { + + write!(f, "\u{1b}[m")?; // reset all + return Ok(()); + } + if let Some(ansi_code) = self.foreground { + match ansi_code { + AnsiCode::Code((param1, param2)) => { + match (param1, param2) { + (Some(param1), Some(param2)) => { + write!(f, "\u{1b}[38;{};{}m", param1, param2)?; + }, + (Some(param1), None) => { + write!(f, "\u{1b}[38;{}m", param1)?; + }, + (_, _) => { + // TODO: can this happen? + } + } + }, + AnsiCode::Reset => { + write!(f, "\u{1b}[39m")?; + }, + AnsiCode::NamedColor(named_color) => { + write!(f, "\u{1b}[{}m", named_color.to_foreground_ansi_code())?; + } + } + }; + if let Some(ansi_code) = self.background { + match ansi_code { + AnsiCode::Code((param1, param2)) => { + match (param1, param2) { + (Some(param1), Some(param2)) => { + write!(f, "\u{1b}[48;{};{}m", param1, param2)?; + }, + (Some(param1), None) => { + write!(f, "\u{1b}[48;{}m", param1)?; + }, + (_, _) => { + // TODO: can this happen? + } + } + }, + AnsiCode::Reset => { + write!(f, "\u{1b}[49m")?; + } + AnsiCode::NamedColor(named_color) => { + write!(f, "\u{1b}[{}m", named_color.to_background_ansi_code())?; + } + } + } + if let Some(ansi_code) = self.strike { + match ansi_code { + AnsiCode::Code((param1, param2)) => { + match (param1, param2) { + (Some(param1), Some(param2)) => { + write!(f, "\u{1b}[9;{};{}m", param1, param2)?; + }, + (Some(param1), None) => { + write!(f, "\u{1b}[9;{}m", param1)?; + }, + (_, _) => { + write!(f, "\u{1b}[9m")?; + } + } + }, + AnsiCode::Reset => { + write!(f, "\u{1b}[29m")?; + }, + _ => {} + } + } + if let Some(ansi_code) = self.hidden { + match ansi_code { + AnsiCode::Code((param1, param2)) => { + match (param1, param2) { + (Some(param1), Some(param2)) => { + write!(f, "\u{1b}[8;{};{}m", param1, param2)?; + }, + (Some(param1), None) => { + write!(f, "\u{1b}[8;{}m", param1)?; + }, + (_, _) => { + write!(f, "\u{1b}[8m")?; + } + } + }, + AnsiCode::Reset => { + write!(f, "\u{1b}[28m")?; + }, + _ => {} + } + } + if let Some(ansi_code) = self.reverse { + match ansi_code { + AnsiCode::Code((param1, param2)) => { + match (param1, param2) { + (Some(param1), Some(param2)) => { + write!(f, "\u{1b}[7;{};{}m", param1, param2)?; + }, + (Some(param1), None) => { + write!(f, "\u{1b}[7;{}m", param1)?; + }, + (_, _) => { + write!(f, "\u{1b}[7m")?; + } + } + }, + AnsiCode::Reset => { + write!(f, "\u{1b}[27m")?; + }, + _ => {} + } + } + if let Some(ansi_code) = self.fast_blink { + match ansi_code { + AnsiCode::Code((param1, param2)) => { + match (param1, param2) { + (Some(param1), Some(param2)) => { + write!(f, "\u{1b}[6;{};{}m", param1, param2)?; + }, + (Some(param1), None) => { + write!(f, "\u{1b}[6;{}m", param1)?; + }, + (_, _) => { + write!(f, "\u{1b}[6m")?; + } + } + }, + AnsiCode::Reset => { + write!(f, "\u{1b}[25m")?; + }, + _ => {} + } + } + if let Some(ansi_code) = self.slow_blink { + match ansi_code { + AnsiCode::Code((param1, param2)) => { + match (param1, param2) { + (Some(param1), Some(param2)) => { + write!(f, "\u{1b}[5;{};{}m", param1, param2)?; + }, + (Some(param1), None) => { + write!(f, "\u{1b}[5;{}m", param1)?; + }, + (_, _) => { + write!(f, "\u{1b}[5m")?; + } + } + }, + AnsiCode::Reset => { + write!(f, "\u{1b}[25m")?; + }, + _ => {} + } + } + if let Some(ansi_code) = self.bold { + match ansi_code { + AnsiCode::Code((param1, param2)) => { + match (param1, param2) { + (Some(param1), Some(param2)) => { + write!(f, "\u{1b}[1;{};{}m", param1, param2)?; + }, + (Some(param1), None) => { + write!(f, "\u{1b}[1;{}m", param1)?; + }, + (_, _) => { + write!(f, "\u{1b}[1m")?; + } + } + }, + AnsiCode::Reset => { + write!(f, "\u{1b}[22m\u{1b}[24m")?; + // character_ansi_codes.push_str(&format!("\u{1b}[22m")); + // TODO: this cancels bold + underline, if this behaviour is indeed correct, we + // need to properly handle it in the struct methods etc like dim + }, + _ => {} + } + } + // notice the order is important here, bold must be before underline + // because the bold reset also resets underline, and would override it + // otherwise + if let Some(ansi_code) = self.underline { + match ansi_code { + AnsiCode::Code((param1, param2)) => { + match (param1, param2) { + (Some(param1), Some(param2)) => { + write!(f, "\u{1b}[4;{};{}m", param1, param2)?; + }, + (Some(param1), None) => { + write!(f, "\u{1b}[4;{}m", param1)?; + }, + (_, _) => { + write!(f, "\u{1b}[4m")?; + } + } + }, + AnsiCode::Reset => { + write!(f, "\u{1b}[24m")?; + }, + _ => {} + } + } + if let Some(ansi_code) = self.dim { + match ansi_code { + AnsiCode::Code((param1, param2)) => { + match (param1, param2) { + (Some(param1), Some(param2)) => { + write!(f, "\u{1b}[2;{};{}m", param1, param2)?; + }, + (Some(param1), None) => { + write!(f, "\u{1b}[2;{}m", param1)?; + }, + (_, _) => { + write!(f, "\u{1b}[2m")?; + } + } + }, + AnsiCode::Reset => { + if let Some(bold) = self.bold { + // we only reset dim if both dim and bold should be reset + match bold { + AnsiCode::Reset => { + write!(f, "\u{1b}[22m")?; + }, + _ => {} + } + } + }, + _ => {} + } + } + if let Some(ansi_code) = self.italic { + match ansi_code { + AnsiCode::Code((param1, param2)) => { + match (param1, param2) { + (Some(param1), Some(param2)) => { + write!(f, "\u{1b}[3;{};{}m", param1, param2)?; + }, + (Some(param1), None) => { + write!(f, "\u{1b}[3;{}m", param1)?; + }, + (_, _) => { + write!(f, "\u{1b}[3m")?; + } + } + }, + AnsiCode::Reset => { + write!(f, "\u{1b}[23m")?; + }, + _ => {} + } + }; + Ok(()) + } +} + #[derive(Clone, Copy)] pub struct TerminalCharacter { pub character: char, @@ -146,109 +609,6 @@ impl TerminalCharacter { self.strike = strike_code; self } - pub fn update_styles_and_return_diff(&mut self, new_styles: &TerminalCharacter) -> Option { - let mut diff: Option = None; - if self.foreground != new_styles.foreground { - if let Some(new_diff) = diff.as_mut() { - diff = Some(new_diff.foreground(new_styles.foreground)); - self.foreground = new_styles.foreground; - } else { - diff = Some(TerminalCharacter::new(self.character).foreground(new_styles.foreground)); - self.foreground = new_styles.foreground; - } - } - if self.background != new_styles.background { - if let Some(new_diff) = diff.as_mut() { - diff = Some(new_diff.background(new_styles.background)); - self.background = new_styles.background; - } else { - diff = Some(TerminalCharacter::new(self.character).background(new_styles.background)); - self.background = new_styles.background; - } - } - if self.strike != new_styles.strike { - if let Some(new_diff) = diff.as_mut() { - diff = Some(new_diff.strike(new_styles.strike)); - self.strike = new_styles.strike; - } else { - diff = Some(TerminalCharacter::new(self.character).strike(new_styles.strike)); - self.strike = new_styles.strike; - } - } - if self.hidden != new_styles.hidden { - if let Some(new_diff) = diff.as_mut() { - diff = Some(new_diff.hidden(new_styles.hidden)); - self.hidden = new_styles.hidden; - } else { - diff = Some(TerminalCharacter::new(self.character).hidden(new_styles.hidden)); - self.hidden = new_styles.hidden; - } - } - if self.reverse != new_styles.reverse { - if let Some(new_diff) = diff.as_mut() { - diff = Some(new_diff.reverse(new_styles.reverse)); - self.reverse= new_styles.reverse; - } else { - diff = Some(TerminalCharacter::new(self.character).reverse(new_styles.reverse)); - self.reverse= new_styles.reverse; - } - } - if self.slow_blink != new_styles.slow_blink { - if let Some(new_diff) = diff.as_mut() { - diff = Some(new_diff.blink_slow(new_styles.slow_blink)); - self.slow_blink = new_styles.slow_blink; - } else { - diff = Some(TerminalCharacter::new(self.character).blink_slow(new_styles.slow_blink)); - self.slow_blink = new_styles.slow_blink; - } - } - if self.fast_blink != new_styles.fast_blink { - if let Some(new_diff) = diff.as_mut() { - diff = Some(new_diff.blink_fast(new_styles.fast_blink)); - self.fast_blink = new_styles.fast_blink; - } else { - diff = Some(TerminalCharacter::new(self.character).blink_fast(new_styles.fast_blink)); - self.fast_blink = new_styles.fast_blink; - } - } - if self.underline != new_styles.underline { - if let Some(new_diff) = diff.as_mut() { - diff = Some(new_diff.underline(new_styles.underline)); - self.underline= new_styles.underline; - } else { - diff = Some(TerminalCharacter::new(self.character).underline(new_styles.underline)); - self.underline= new_styles.underline; - } - } - if self.bold != new_styles.bold { - if let Some(new_diff) = diff.as_mut() { - diff = Some(new_diff.bold(new_styles.bold)); - self.bold= new_styles.bold; - } else { - diff = Some(TerminalCharacter::new(self.character).bold(new_styles.bold)); - self.bold= new_styles.bold; - } - } - if self.dim != new_styles.dim { - if let Some(new_diff) = diff.as_mut() { - diff = Some(new_diff.dim(new_styles.dim)); - self.dim= new_styles.dim; - } else { - diff = Some(TerminalCharacter::new(self.character).dim(new_styles.dim)); - self.dim= new_styles.dim; - } - } - if self.italic != new_styles.italic { - if let Some(new_diff) = diff.as_mut() { - diff = Some(new_diff.italic(new_styles.italic)); - self.italic = new_styles.italic; - } else { - diff = Some(TerminalCharacter::new(self.character).italic(new_styles.italic)); - self.italic = new_styles.italic; - } - } - diff.and_then(|d| Some(d.to_string())) - } } impl Display for TerminalCharacter { @@ -820,20 +1180,21 @@ impl TerminalOutput { let mut vte_output = String::new(); let buffer_lines = &self.read_buffer_as_lines(); let display_cols = &self.display_cols; + let mut character_styles = CharacterStyles::new(); for (row, line) in buffer_lines.iter().enumerate() { - vte_output.push_str(&format!("\u{1b}[{};{}H\u{1b}[m", self.y_coords as usize + row + 1, self.x_coords + 1)); // goto row/col - let mut terminal_character_styles = TerminalCharacter::new(' '); + vte_output = format!("{}\u{1b}[{};{}H\u{1b}[m", vte_output, self.y_coords as usize + row + 1, self.x_coords + 1); // goto row/col for (col, t_character) in line.iter().enumerate() { if (col as u16) < *display_cols { // in some cases (eg. while resizing) some characters will spill over // before they are corrected by the shell (for the prompt) or by reflowing // lines - if let Some(new_styles) = terminal_character_styles.update_styles_and_return_diff(&t_character) { - vte_output.push_str(&new_styles); + if let Some(new_styles) = character_styles.update_and_return_diff(&t_character) { + vte_output = format!("{}{}", vte_output, new_styles); } vte_output.push(t_character.character); } } + character_styles.clear(); } self.should_render = false; Some(vte_output)