diff options
author | Blaž Hrastnik | 2022-10-19 04:51:13 +0000 |
---|---|---|
committer | GitHub | 2022-10-19 04:51:13 +0000 |
commit | 418a622db9d957d09e10ccbc3897d8ac9268dc8e (patch) | |
tree | 6e69857287580ab24c1e60ac199138ac23dc37df /helix-view | |
parent | faf0c521d15c314f411cc6178024c5d3310212da (diff) | |
parent | 66a49080bc7e492c37f9cd10ed36a696de1787a3 (diff) |
Merge pull request #4061 from pascalkuthe/undercurl-modifier
Support different kinds of underline rendering (updated)
Diffstat (limited to 'helix-view')
-rw-r--r-- | helix-view/src/graphics.rs | 86 | ||||
-rw-r--r-- | helix-view/src/gutter.rs | 4 | ||||
-rw-r--r-- | helix-view/src/theme.rs | 34 |
3 files changed, 116 insertions, 8 deletions
diff --git a/helix-view/src/graphics.rs b/helix-view/src/graphics.rs index fb3c8b3f..4374a537 100644 --- a/helix-view/src/graphics.rs +++ b/helix-view/src/graphics.rs @@ -315,6 +315,44 @@ impl From<Color> for crossterm::style::Color { } } +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum UnderlineStyle { + Reset, + Line, + Curl, + Dotted, + Dashed, + DoubleLine, +} + +impl FromStr for UnderlineStyle { + type Err = &'static str; + + fn from_str(modifier: &str) -> Result<Self, Self::Err> { + match modifier { + "line" => Ok(Self::Line), + "curl" => Ok(Self::Curl), + "dotted" => Ok(Self::Dotted), + "dashed" => Ok(Self::Dashed), + "double_line" => Ok(Self::DoubleLine), + _ => Err("Invalid underline style"), + } + } +} + +impl From<UnderlineStyle> for crossterm::style::Attribute { + fn from(style: UnderlineStyle) -> Self { + match style { + UnderlineStyle::Line => crossterm::style::Attribute::Underlined, + UnderlineStyle::Curl => crossterm::style::Attribute::Undercurled, + UnderlineStyle::Dotted => crossterm::style::Attribute::Underdotted, + UnderlineStyle::Dashed => crossterm::style::Attribute::Underdashed, + UnderlineStyle::DoubleLine => crossterm::style::Attribute::DoubleUnderlined, + UnderlineStyle::Reset => crossterm::style::Attribute::NoUnderline, + } + } +} + bitflags! { /// Modifier changes the way a piece of text is displayed. /// @@ -332,7 +370,6 @@ bitflags! { const BOLD = 0b0000_0000_0001; const DIM = 0b0000_0000_0010; const ITALIC = 0b0000_0000_0100; - const UNDERLINED = 0b0000_0000_1000; const SLOW_BLINK = 0b0000_0001_0000; const RAPID_BLINK = 0b0000_0010_0000; const REVERSED = 0b0000_0100_0000; @@ -349,7 +386,6 @@ impl FromStr for Modifier { "bold" => Ok(Self::BOLD), "dim" => Ok(Self::DIM), "italic" => Ok(Self::ITALIC), - "underlined" => Ok(Self::UNDERLINED), "slow_blink" => Ok(Self::SLOW_BLINK), "rapid_blink" => Ok(Self::RAPID_BLINK), "reversed" => Ok(Self::REVERSED), @@ -375,7 +411,7 @@ impl FromStr for Modifier { /// just S3. /// /// ```rust -/// # use helix_view::graphics::{Rect, Color, Modifier, Style}; +/// # use helix_view::graphics::{Rect, Color, UnderlineStyle, Modifier, Style}; /// # use helix_tui::buffer::Buffer; /// let styles = [ /// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC), @@ -391,6 +427,8 @@ impl FromStr for Modifier { /// fg: Some(Color::Yellow), /// bg: Some(Color::Red), /// add_modifier: Modifier::BOLD, +/// underline_color: Some(Color::Reset), +/// underline_style: Some(UnderlineStyle::Reset), /// sub_modifier: Modifier::empty(), /// }, /// buffer[(0, 0)].style(), @@ -401,7 +439,7 @@ impl FromStr for Modifier { /// reset all properties until that point use [`Style::reset`]. /// /// ``` -/// # use helix_view::graphics::{Rect, Color, Modifier, Style}; +/// # use helix_view::graphics::{Rect, Color, UnderlineStyle, Modifier, Style}; /// # use helix_tui::buffer::Buffer; /// let styles = [ /// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC), @@ -415,6 +453,8 @@ impl FromStr for Modifier { /// Style { /// fg: Some(Color::Yellow), /// bg: Some(Color::Reset), +/// underline_color: Some(Color::Reset), +/// underline_style: Some(UnderlineStyle::Reset), /// add_modifier: Modifier::empty(), /// sub_modifier: Modifier::empty(), /// }, @@ -426,6 +466,8 @@ impl FromStr for Modifier { pub struct Style { pub fg: Option<Color>, pub bg: Option<Color>, + pub underline_color: Option<Color>, + pub underline_style: Option<UnderlineStyle>, pub add_modifier: Modifier, pub sub_modifier: Modifier, } @@ -435,6 +477,8 @@ impl Default for Style { Style { fg: None, bg: None, + underline_color: None, + underline_style: None, add_modifier: Modifier::empty(), sub_modifier: Modifier::empty(), } @@ -447,6 +491,8 @@ impl Style { Style { fg: Some(Color::Reset), bg: Some(Color::Reset), + underline_color: None, + underline_style: None, add_modifier: Modifier::empty(), sub_modifier: Modifier::all(), } @@ -482,6 +528,36 @@ impl Style { self } + /// Changes the underline color. + /// + /// ## Examples + /// + /// ```rust + /// # use helix_view::graphics::{Color, Style}; + /// let style = Style::default().underline_color(Color::Blue); + /// let diff = Style::default().underline_color(Color::Red); + /// assert_eq!(style.patch(diff), Style::default().underline_color(Color::Red)); + /// ``` + pub fn underline_color(mut self, color: Color) -> Style { + self.underline_color = Some(color); + self + } + + /// Changes the underline style. + /// + /// ## Examples + /// + /// ```rust + /// # use helix_view::graphics::{UnderlineStyle, Style}; + /// let style = Style::default().underline_style(UnderlineStyle::Line); + /// let diff = Style::default().underline_style(UnderlineStyle::Curl); + /// assert_eq!(style.patch(diff), Style::default().underline_style(UnderlineStyle::Curl)); + /// ``` + pub fn underline_style(mut self, style: UnderlineStyle) -> Style { + self.underline_style = Some(style); + self + } + /// Changes the text emphasis. /// /// When applied, it adds the given modifier to the `Style` modifiers. @@ -538,6 +614,8 @@ impl Style { pub fn patch(mut self, other: Style) -> Style { self.fg = other.fg.or(self.fg); self.bg = other.bg.or(self.bg); + self.underline_color = other.underline_color.or(self.underline_color); + self.underline_style = other.underline_style.or(self.underline_style); self.add_modifier.remove(other.sub_modifier); self.add_modifier.insert(other.add_modifier); diff --git a/helix-view/src/gutter.rs b/helix-view/src/gutter.rs index ab0e2986..2c207d27 100644 --- a/helix-view/src/gutter.rs +++ b/helix-view/src/gutter.rs @@ -1,7 +1,7 @@ use std::fmt::Write; use crate::{ - graphics::{Color, Modifier, Style}, + graphics::{Color, Style, UnderlineStyle}, Document, Editor, Theme, View, }; @@ -147,7 +147,7 @@ pub fn breakpoints<'doc>( .find(|breakpoint| breakpoint.line == line)?; let mut style = if breakpoint.condition.is_some() && breakpoint.log_message.is_some() { - error.add_modifier(Modifier::UNDERLINED) + error.underline_style(UnderlineStyle::Line) } else if breakpoint.condition.is_some() { error } else if breakpoint.log_message.is_some() { diff --git a/helix-view/src/theme.rs b/helix-view/src/theme.rs index 8a1f8b7e..302844b7 100644 --- a/helix-view/src/theme.rs +++ b/helix-view/src/theme.rs @@ -11,6 +11,7 @@ use once_cell::sync::Lazy; use serde::{Deserialize, Deserializer}; use toml::{map::Map, Value}; +use crate::graphics::UnderlineStyle; pub use crate::graphics::{Color, Modifier, Style}; pub static DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| { @@ -378,19 +379,48 @@ impl ThemePalette { .ok_or(format!("Theme: invalid modifier: {}", value)) } + pub fn parse_underline_style(value: &Value) -> Result<UnderlineStyle, String> { + value + .as_str() + .and_then(|s| s.parse().ok()) + .ok_or(format!("Theme: invalid underline style: {}", value)) + } + pub fn parse_style(&self, style: &mut Style, value: Value) -> Result<(), String> { if let Value::Table(entries) = value { - for (name, value) in entries { + for (name, mut value) in entries { match name.as_str() { "fg" => *style = style.fg(self.parse_color(value)?), "bg" => *style = style.bg(self.parse_color(value)?), + "underline" => { + let table = value + .as_table_mut() + .ok_or("Theme: underline must be table")?; + if let Some(value) = table.remove("color") { + *style = style.underline_color(self.parse_color(value)?); + } + if let Some(value) = table.remove("style") { + *style = style.underline_style(Self::parse_underline_style(&value)?); + } + + if let Some(attr) = table.keys().next() { + return Err(format!("Theme: invalid underline attribute: {attr}")); + } + } "modifiers" => { let modifiers = value .as_array() .ok_or("Theme: modifiers should be an array")?; for modifier in modifiers { - *style = style.add_modifier(Self::parse_modifier(modifier)?); + if modifier + .as_str() + .map_or(false, |modifier| modifier == "underlined") + { + *style = style.underline_style(UnderlineStyle::Line); + } else { + *style = style.add_modifier(Self::parse_modifier(modifier)?); + } } } _ => return Err(format!("Theme: invalid style attribute: {}", name)), |