diff options
author | Kirill Chibisov <contact@kchibisov.com> | 2022-03-16 19:27:55 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-16 19:27:55 +0300 |
commit | f4bdf5fb36fdf3b329be8253da39050abe7238a5 (patch) | |
tree | 9b49a78b15fdd73850a7bfc279e3986af2b59683 /alacritty_terminal/src | |
parent | 589c1e9c6b8830625162af14a9a7aee32c7aade0 (diff) | |
download | r-alacritty-f4bdf5fb36fdf3b329be8253da39050abe7238a5.tar.gz r-alacritty-f4bdf5fb36fdf3b329be8253da39050abe7238a5.tar.bz2 r-alacritty-f4bdf5fb36fdf3b329be8253da39050abe7238a5.zip |
Add colored underline support
This commit adds support for colored underline and refines the dynamic
extra storage. The extra storage now is using `Arc` making cloning it way
faster compared to `Box` approach which scales really well when it comes
to cloning in `Term::write_at_cursor`, since cloning `Arc` is constant
time.
Fixes #4142.
Diffstat (limited to 'alacritty_terminal/src')
-rw-r--r-- | alacritty_terminal/src/ansi.rs | 36 | ||||
-rw-r--r-- | alacritty_terminal/src/term/cell.rs | 60 | ||||
-rw-r--r-- | alacritty_terminal/src/term/mod.rs | 6 |
3 files changed, 69 insertions, 33 deletions
diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs index 8475685d..c5ddb3bf 100644 --- a/alacritty_terminal/src/ansi.rs +++ b/alacritty_terminal/src/ansi.rs @@ -806,6 +806,8 @@ pub enum Attr { Foreground(Color), /// Set indexed background color. Background(Color), + /// Underline color. + UnderlineColor(Option<Color>), } /// Identifiers which can be assigned to a graphic character set. @@ -1364,13 +1366,7 @@ fn attrs_from_sgr_parameters(params: &mut ParamsIter<'_>) -> Vec<Option<Attr>> { let mut iter = params.map(|param| param[0]); parse_sgr_color(&mut iter).map(Attr::Foreground) }, - [38, params @ ..] => { - let rgb_start = if params.len() > 4 { 2 } else { 1 }; - let rgb_iter = params[rgb_start..].iter().copied(); - let mut iter = iter::once(params[0]).chain(rgb_iter); - - parse_sgr_color(&mut iter).map(Attr::Foreground) - }, + [38, params @ ..] => handle_colon_rgb(params).map(Attr::Foreground), [39] => Some(Attr::Foreground(Color::Named(NamedColor::Foreground))), [40] => Some(Attr::Background(Color::Named(NamedColor::Black))), [41] => Some(Attr::Background(Color::Named(NamedColor::Red))), @@ -1384,14 +1380,16 @@ fn attrs_from_sgr_parameters(params: &mut ParamsIter<'_>) -> Vec<Option<Attr>> { let mut iter = params.map(|param| param[0]); parse_sgr_color(&mut iter).map(Attr::Background) }, - [48, params @ ..] => { - let rgb_start = if params.len() > 4 { 2 } else { 1 }; - let rgb_iter = params[rgb_start..].iter().copied(); - let mut iter = iter::once(params[0]).chain(rgb_iter); - - parse_sgr_color(&mut iter).map(Attr::Background) - }, + [48, params @ ..] => handle_colon_rgb(params).map(Attr::Background), [49] => Some(Attr::Background(Color::Named(NamedColor::Background))), + [58] => { + let mut iter = params.map(|param| param[0]); + parse_sgr_color(&mut iter).map(|color| Attr::UnderlineColor(Some(color))) + }, + [58, params @ ..] => { + handle_colon_rgb(params).map(|color| Attr::UnderlineColor(Some(color))) + }, + [59] => Some(Attr::UnderlineColor(None)), [90] => Some(Attr::Foreground(Color::Named(NamedColor::BrightBlack))), [91] => Some(Attr::Foreground(Color::Named(NamedColor::BrightRed))), [92] => Some(Attr::Foreground(Color::Named(NamedColor::BrightGreen))), @@ -1416,6 +1414,16 @@ fn attrs_from_sgr_parameters(params: &mut ParamsIter<'_>) -> Vec<Option<Attr>> { attrs } +/// Handle colon separated rgb color escape sequence. +#[inline] +fn handle_colon_rgb(params: &[u16]) -> Option<Color> { + let rgb_start = if params.len() > 4 { 2 } else { 1 }; + let rgb_iter = params[rgb_start..].iter().copied(); + let mut iter = iter::once(params[0]).chain(rgb_iter); + + parse_sgr_color(&mut iter) +} + /// Parse a color specifier from list of attributes. fn parse_sgr_color(params: &mut dyn Iterator<Item = u16>) -> Option<Color> { match params.next() { diff --git a/alacritty_terminal/src/term/cell.rs b/alacritty_terminal/src/term/cell.rs index 2af1fbac..716bcf9b 100644 --- a/alacritty_terminal/src/term/cell.rs +++ b/alacritty_terminal/src/term/cell.rs @@ -1,4 +1,4 @@ -use std::boxed::Box; +use std::sync::Arc; use bitflags::bitflags; use serde::{Deserialize, Serialize}; @@ -57,19 +57,20 @@ impl ResetDiscriminant<Color> for Cell { /// allocation required ahead of time for every cell, with some additional overhead when the extra /// storage is actually required. #[derive(Serialize, Deserialize, Default, Debug, Clone, Eq, PartialEq)] -struct CellExtra { +pub struct CellExtra { zerowidth: Vec<char>, + + underline_color: Option<Color>, } /// Content and attributes of a single cell in the terminal grid. -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] pub struct Cell { pub c: char, pub fg: Color, pub bg: Color, pub flags: Flags, - #[serde(default)] - extra: Option<Box<CellExtra>>, + pub extra: Option<Arc<CellExtra>>, } impl Default for Cell { @@ -94,25 +95,39 @@ impl Cell { /// Write a new zerowidth character to this cell. #[inline] - pub fn push_zerowidth(&mut self, c: char) { - self.extra.get_or_insert_with(Default::default).zerowidth.push(c); - } - - /// Free all dynamically allocated cell storage. - #[inline] - pub fn drop_extra(&mut self) { - if self.extra.is_some() { - self.extra = None; - } + pub fn push_zerowidth(&mut self, character: char) { + let extra = self.extra.get_or_insert(Default::default()); + Arc::make_mut(extra).zerowidth.push(character); } /// Remove all wide char data from a cell. #[inline(never)] pub fn clear_wide(&mut self) { self.flags.remove(Flags::WIDE_CHAR); - self.drop_extra(); + if let Some(extra) = self.extra.as_mut() { + Arc::make_mut(extra).zerowidth = Vec::new(); + } self.c = ' '; } + + /// Set underline color on the cell. + pub fn set_underline_color(&mut self, color: Option<Color>) { + // If we reset color and we don't have zerowidth we should drop extra storage. + if color.is_none() && self.extra.as_ref().map_or(true, |extra| !extra.zerowidth.is_empty()) + { + self.extra = None; + return; + } + + let extra = self.extra.get_or_insert(Default::default()); + Arc::make_mut(extra).underline_color = color; + } + + /// Underline color stored in this cell. + #[inline] + pub fn underline_color(&self) -> Option<Color> { + self.extra.as_ref()?.underline_color + } } impl GridCell for Cell { @@ -184,12 +199,23 @@ impl LineLength for grid::Row<Cell> { #[cfg(test)] mod tests { - use super::{Cell, LineLength}; + use super::*; + + use std::mem; use crate::grid::Row; use crate::index::Column; #[test] + fn cell_size_is_below_cap() { + // Expected cell size on 64-bit architectures. + const EXPECTED_CELL_SIZE: usize = 24; + + // Ensure that cell size isn't growning by accident. + assert!(mem::size_of::<Cell>() <= EXPECTED_CELL_SIZE); + } + + #[test] fn line_length_works() { let mut row = Row::<Cell>::new(10); row[Column(5)].c = 'a'; diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 96f0d1e7..fa7d2b66 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -1047,6 +1047,7 @@ impl<T> Term<T> { let fg = self.grid.cursor.template.fg; let bg = self.grid.cursor.template.bg; let flags = self.grid.cursor.template.flags; + let extra = self.grid.cursor.template.extra.clone(); let mut cursor_cell = self.grid.cursor_cell(); @@ -1070,12 +1071,11 @@ impl<T> Term<T> { cursor_cell = self.grid.cursor_cell(); } - cursor_cell.drop_extra(); - cursor_cell.c = c; cursor_cell.fg = fg; cursor_cell.bg = bg; cursor_cell.flags = flags; + cursor_cell.extra = extra; } #[inline] @@ -1826,10 +1826,12 @@ impl<T: EventListener> Handler for Term<T> { match attr { Attr::Foreground(color) => cursor.template.fg = color, Attr::Background(color) => cursor.template.bg = color, + Attr::UnderlineColor(color) => cursor.template.set_underline_color(color), Attr::Reset => { cursor.template.fg = Color::Named(NamedColor::Foreground); cursor.template.bg = Color::Named(NamedColor::Background); cursor.template.flags = Flags::empty(); + cursor.template.set_underline_color(None); }, Attr::Reverse => cursor.template.flags.insert(Flags::INVERSE), Attr::CancelReverse => cursor.template.flags.remove(Flags::INVERSE), |