From ad0d100304849cd21293513dc4d8476fa0f5b174 Mon Sep 17 00:00:00 2001 From: ahoyiski <91404038+AhoyISki@users.noreply.github.com> Date: Thu, 30 Jun 2022 16:33:49 -0300 Subject: [PATCH] Add support for other underline types and the ability to color them (#679) --- .../interactive-demo/src/test/attribute.rs | 4 ++ src/style.rs | 24 +++++++++ src/style/content_style.rs | 2 + src/style/stylize.rs | 52 +++++++++++++------ src/style/types/attribute.rs | 18 ++++++- src/style/types/colored.rs | 14 ++++- src/style/types/colors.rs | 4 ++ 7 files changed, 98 insertions(+), 20 deletions(-) diff --git a/examples/interactive-demo/src/test/attribute.rs b/examples/interactive-demo/src/test/attribute.rs index c4d4332..8c5de8e 100644 --- a/examples/interactive-demo/src/test/attribute.rs +++ b/examples/interactive-demo/src/test/attribute.rs @@ -8,6 +8,10 @@ const ATTRIBUTES: [(style::Attribute, style::Attribute); 6] = [ (style::Attribute::Bold, style::Attribute::NormalIntensity), (style::Attribute::Italic, style::Attribute::NoItalic), (style::Attribute::Underlined, style::Attribute::NoUnderline), + (style::Attribute::DoubleUnderlined, style::Attribute::NoUnderline), + (style::Attribute::Undercurled, style::Attribute::NoUnderline), + (style::Attribute::Underdotted, style::Attribute::NoUnderline), + (style::Attribute::Underdashed, style::Attribute::NoUnderline), (style::Attribute::Reverse, style::Attribute::NoReverse), ( style::Attribute::CrossedOut, diff --git a/src/style.rs b/src/style.rs index af0fc78..0022c15 100644 --- a/src/style.rs +++ b/src/style.rs @@ -217,6 +217,25 @@ impl Command for SetBackgroundColor { } } +/// A command that sets the the underline color. +/// +/// See [`Color`](enum.Color.html) for more info. +/// +/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background +/// color with one command. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SetUnderlineColor(pub Color); + +impl Command for SetUnderlineColor { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}m"), Colored::UnderlineColor(self.0)) + } +} + /// A command that optionally sets the foreground and/or background color. /// /// For example: @@ -340,6 +359,11 @@ impl Command for PrintStyledContent { reset_foreground = true; } + if let Some(ul) = style.underline_color { + execute_fmt(f, SetUnderlineColor(ul)).map_err(|_| fmt::Error)?; + reset_foreground = true; + } + if !style.attributes.is_empty() { execute_fmt(f, SetAttributes(style.attributes)).map_err(|_| fmt::Error)?; reset = true; diff --git a/src/style/content_style.rs b/src/style/content_style.rs index 45b3b1c..6e99bb6 100644 --- a/src/style/content_style.rs +++ b/src/style/content_style.rs @@ -11,6 +11,8 @@ pub struct ContentStyle { pub foreground_color: Option, /// The background color. pub background_color: Option, + /// The underline color. + pub underline_color: Option, /// List of attributes. pub attributes: Attributes, } diff --git a/src/style/stylize.rs b/src/style/stylize.rs index ae709a4..fcb12da 100644 --- a/src/style/stylize.rs +++ b/src/style/stylize.rs @@ -17,7 +17,7 @@ macro_rules! stylize_method { } } }; - ($method_name_fg:ident, $method_name_bg:ident Color::$color:ident) => { + ($method_name_fg:ident, $method_name_bg:ident, $method_name_ul:ident Color::$color:ident) => { calculated_docs! { #[doc = concat!( "Sets the foreground color to [`", @@ -40,6 +40,17 @@ macro_rules! stylize_method { fn $method_name_bg(self) -> Self::Styled { self.on(Color::$color) } + + #[doc = concat!( + "Sets the underline color to [`", + stringify!($color), + "`](Color::", + stringify!($color), + ")." + )] + fn $method_name_ul(self) -> Self::Styled { + self.underline(Color::$color) + } } }; } @@ -77,6 +88,13 @@ pub trait Stylize: Sized { styled } + /// Sets the underline color. + fn underline(self, color: Color) -> Self::Styled { + let mut styled = self.stylize(); + styled.as_mut().underline_color = Some(color); + styled + } + /// Styles the content with the attribute. fn attribute(self, attr: Attribute) -> Self::Styled { let mut styled = self.stylize(); @@ -96,22 +114,22 @@ pub trait Stylize: Sized { stylize_method!(hidden Attribute::Hidden); stylize_method!(crossed_out Attribute::CrossedOut); - stylize_method!(black, on_black Color::Black); - stylize_method!(dark_grey, on_dark_grey Color::DarkGrey); - stylize_method!(red, on_red Color::Red); - stylize_method!(dark_red, on_dark_red Color::DarkRed); - stylize_method!(green, on_green Color::Green); - stylize_method!(dark_green, on_dark_green Color::DarkGreen); - stylize_method!(yellow, on_yellow Color::Yellow); - stylize_method!(dark_yellow, on_dark_yellow Color::DarkYellow); - stylize_method!(blue, on_blue Color::Blue); - stylize_method!(dark_blue, on_dark_blue Color::DarkBlue); - stylize_method!(magenta, on_magenta Color::Magenta); - stylize_method!(dark_magenta, on_dark_magenta Color::DarkMagenta); - stylize_method!(cyan, on_cyan Color::Cyan); - stylize_method!(dark_cyan, on_dark_cyan Color::DarkCyan); - stylize_method!(white, on_white Color::White); - stylize_method!(grey, on_grey Color::Grey); + stylize_method!(black, on_black, underline_black Color::Black); + stylize_method!(dark_grey, on_dark_grey, underline_dark_grey Color::DarkGrey); + stylize_method!(red, on_red, underline_red Color::Red); + stylize_method!(dark_red, on_dark_red, underline_dark_red Color::DarkRed); + stylize_method!(green, on_green, underline_green Color::Green); + stylize_method!(dark_green, on_dark_green, underline_dark_green Color::DarkGreen); + stylize_method!(yellow, on_yellow, underline_yellow Color::Yellow); + stylize_method!(dark_yellow, on_dark_yellow, underline_dark_yellow Color::DarkYellow); + stylize_method!(blue, on_blue, underline_blue Color::Blue); + stylize_method!(dark_blue, on_dark_blue, underline_dark_blue Color::DarkBlue); + stylize_method!(magenta, on_magenta, underline_magenta Color::Magenta); + stylize_method!(dark_magenta, on_dark_magenta, underline_dark_magenta Color::DarkMagenta); + stylize_method!(cyan, on_cyan, underline_cyan Color::Cyan); + stylize_method!(dark_cyan, on_dark_cyan, underline_dark_cyan Color::DarkCyan); + stylize_method!(white, on_white, underline_white Color::White); + stylize_method!(grey, on_grey, underline_grey Color::Grey); } macro_rules! impl_stylize_for_display { diff --git a/src/style/types/attribute.rs b/src/style/types/attribute.rs index 992cfe1..cd1c768 100644 --- a/src/style/types/attribute.rs +++ b/src/style/types/attribute.rs @@ -100,6 +100,17 @@ Attribute! { Italic = 3, /// Underlines the text. Underlined = 4, + + // Other types of underlining + /// Double underlines the text. + DoubleUnderlined = 2, + /// Undercurls the text. + Undercurled = 3, + /// Underdots the text. + Underdotted = 4, + /// Underdashes the text. + Underdashed = 5, + /// Makes the text blinking (< 150 per minute). SlowBlink = 5, /// Makes the text blinking (>= 150 per minute). @@ -163,7 +174,10 @@ impl Attribute { /// Returns the SGR attribute value. /// /// See - pub fn sgr(self) -> i16 { - SGR[self as usize] + pub fn sgr(self) -> String { + if (self as usize) > 4 && (self as usize) < 9 { + return "4:".to_string() + SGR[self as usize].to_string().as_str() + } + SGR[self as usize].to_string() } } diff --git a/src/style/types/colored.rs b/src/style/types/colored.rs index 08769aa..08e1588 100644 --- a/src/style/types/colored.rs +++ b/src/style/types/colored.rs @@ -16,6 +16,8 @@ pub enum Colored { ForegroundColor(Color), /// A background color. BackgroundColor(Color), + /// An underline color. + UnderlineColor(Color), } impl Colored { @@ -39,16 +41,18 @@ impl Colored { /// /// See also: [`Color::parse_ansi`]. pub fn parse_ansi(ansi: &str) -> Option { - use Colored::{BackgroundColor, ForegroundColor}; + use Colored::{BackgroundColor, ForegroundColor, UnderlineColor}; let values = &mut ansi.split(';'); let output = match parse_next_u8(values)? { 38 => return Color::parse_ansi_iter(values).map(ForegroundColor), 48 => return Color::parse_ansi_iter(values).map(BackgroundColor), + 58 => return Color::parse_ansi_iter(values).map(UnderlineColor), 39 => ForegroundColor(Color::Reset), 49 => BackgroundColor(Color::Reset), + 59 => UnderlineColor(Color::Reset), _ => return None, }; @@ -82,6 +86,14 @@ impl fmt::Display for Colored { color = new_color; } } + Colored::UnderlineColor(new_color) => { + if new_color == Color::Reset { + return f.write_str("59"); + } else { + f.write_str("58;")?; + color = new_color; + } + } } match color { diff --git a/src/style/types/colors.rs b/src/style/types/colors.rs index 44e4233..7f3f64c 100644 --- a/src/style/types/colors.rs +++ b/src/style/types/colors.rs @@ -63,6 +63,10 @@ impl From for Colors { foreground: None, background: Some(color), }, + Colored::UnderlineColor(color) => Colors { + foreground: None, + background: Some(color), + } } } }