Skip to content

Commit

Permalink
make underline_style a seperate option
Browse files Browse the repository at this point in the history
Underline styles are mutally exclusive and overwrite each other.
Therefore implementing as an modifier lead to incorrect behaviour
when the underline style is overwritten.

For backwards compatability the "underline" modified is retained (but
deprecated). Instead the "underline_style" and "underline_color"
optios should be used to style underlines.
  • Loading branch information
pascalkuthe committed Oct 1, 2022
1 parent 79d3d44 commit 89d9a20
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 80 deletions.
23 changes: 17 additions & 6 deletions book/src/themes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ The default theme.toml can be found [here](https://github.com/helix-editor/helix
Each line in the theme file is specified as below:

```toml
key = { fg = "#ffffff", bg = "#000000", underline = "#ff0000", modifiers = ["bold", "italic", "undercurled"] }
key = { fg = "#ffffff", bg = "#000000", underline_color = "#ff0000", underline_style = "curl", modifiers = ["bold", "italic"] }
```

where `key` represents what you want to style, `fg` specifies the foreground color, `bg` the background color, `underline` the underline color (only meaningful if an underline modifier is enabled), and `modifiers` is a list of style modifiers. `bg`, `underline` and `modifiers` can be omitted to defer to the defaults.
where `key` represents what you want to style, `fg` specifies the foreground color, `bg` the background color, `underline_style` the underline style, `underline_color` the underline color (only meaningful if an underline style is enabled), and `modifiers` is a list of style modifiers. `bg`, `underline` and `modifiers` can be omitted to defer to the defaults.

To specify only the foreground color:

Expand Down Expand Up @@ -83,16 +83,27 @@ Less common modifiers might not be supported by your terminal emulator.
| `dim` |
| `italic` |
| `underlined` |
| `undercurled` |
| `underdashed` |
| `underdotted` |
| `double-underlined` |
| `slow_blink` |
| `rapid_blink` |
| `reversed` |
| `hidden` |
| `crossed_out` |

### Underline Style

One of the following values may be used as an `underline_styles`.

Some styles might not be supported by your terminal emulator.

| Modifier |
| --- |
| `line` |
| `curl` |
| `dashed` |
| `dot` |
| `double-line` |


### Scopes

The following is a list of scopes available to use for styling.
Expand Down
56 changes: 23 additions & 33 deletions helix-tui/src/backend/crossterm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crossterm::{
},
terminal::{self, Clear, ClearType},
};
use helix_view::graphics::{Color, CursorKind, Modifier, Rect};
use helix_view::graphics::{Color, CursorKind, Modifier, Rect, UnderlineStyle};
use std::io::{self, Write};

fn vte_version() -> Option<usize> {
Expand Down Expand Up @@ -80,7 +80,8 @@ where
{
let mut fg = Color::Reset;
let mut bg = Color::Reset;
let mut underline = Color::Reset;
let mut underline_color = Color::Reset;
let mut underline_style = UnderlineStyle::Reset;
let mut modifier = Modifier::empty();
let mut last_pos: Option<(u16, u16)> = None;
for (x, y, cell) in content {
Expand All @@ -94,7 +95,7 @@ where
from: modifier,
to: cell.modifier,
};
diff.queue(&mut self.buffer, self.capabilities)?;
diff.queue(&mut self.buffer)?;
modifier = cell.modifier;
}
if cell.fg != fg {
Expand All @@ -107,17 +108,32 @@ where
map_error(queue!(self.buffer, SetBackgroundColor(color)))?;
bg = cell.bg;
}
if cell.underline != underline {
let color = CColor::from(cell.underline);
if cell.underline_color != underline_color {
let color = CColor::from(cell.underline_color);
map_error(queue!(self.buffer, SetUnderlineColor(color)))?;
underline = cell.underline;
underline_color = cell.underline_color;
}

let mut new_underline_style = cell.underline_style;
if !self.capabilities.has_extended_underlines {
match new_underline_style {
UnderlineStyle::Reset => (),
_ => new_underline_style = UnderlineStyle::Line,
}
}

if new_underline_style != underline_style {
let attr = CAttribute::from(cell.underline_style);
map_error(queue!(self.buffer, SetAttribute(attr)))?;
underline_style = new_underline_style;
}

map_error(queue!(self.buffer, Print(&cell.symbol)))?;
}

map_error(queue!(
self.buffer,
SetUnderlineColor(CColor::Reset),
SetForegroundColor(CColor::Reset),
SetBackgroundColor(CColor::Reset),
SetAttribute(CAttribute::Reset)
Expand Down Expand Up @@ -174,7 +190,7 @@ struct ModifierDiff {
}

impl ModifierDiff {
fn queue<W>(&self, mut w: W, caps: Capabilities) -> io::Result<()>
fn queue<W>(&self, mut w: W) -> io::Result<()>
where
W: io::Write,
{
Expand All @@ -192,9 +208,6 @@ impl ModifierDiff {
if removed.contains(Modifier::ITALIC) {
map_error(queue!(w, SetAttribute(CAttribute::NoItalic)))?;
}
if removed.intersects(Modifier::ANY_UNDERLINE) {
map_error(queue!(w, SetAttribute(CAttribute::NoUnderline)))?;
}
if removed.contains(Modifier::DIM) {
map_error(queue!(w, SetAttribute(CAttribute::NormalIntensity)))?;
}
Expand All @@ -205,14 +218,6 @@ impl ModifierDiff {
map_error(queue!(w, SetAttribute(CAttribute::NoBlink)))?;
}

let queue_styled_underline = |styled_underline, w: &mut W| -> io::Result<()> {
let underline = match caps.has_extended_underlines {
true => styled_underline,
false => CAttribute::Underlined,
};
map_error(queue!(w, SetAttribute(underline)))
};

let added = self.to - self.from;
if added.contains(Modifier::REVERSED) {
map_error(queue!(w, SetAttribute(CAttribute::Reverse)))?;
Expand All @@ -223,21 +228,6 @@ impl ModifierDiff {
if added.contains(Modifier::ITALIC) {
map_error(queue!(w, SetAttribute(CAttribute::Italic)))?;
}
if added.contains(Modifier::UNDERLINED) {
map_error(queue!(w, SetAttribute(CAttribute::Underlined)))?;
}
if added.contains(Modifier::UNDERCURLED) {
queue_styled_underline(CAttribute::Undercurled, &mut w)?;
}
if added.contains(Modifier::UNDERDOTTED) {
queue_styled_underline(CAttribute::Underdotted, &mut w)?;
}
if added.contains(Modifier::UNDERDASHED) {
queue_styled_underline(CAttribute::Underdashed, &mut w)?;
}
if added.contains(Modifier::DOUBLE_UNDERLINED) {
queue_styled_underline(CAttribute::DoubleUnderlined, &mut w)?;
}
if added.contains(Modifier::DIM) {
map_error(queue!(w, SetAttribute(CAttribute::Dim)))?;
}
Expand Down
22 changes: 15 additions & 7 deletions helix-tui/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ use helix_core::unicode::width::UnicodeWidthStr;
use std::cmp::min;
use unicode_segmentation::UnicodeSegmentation;

use helix_view::graphics::{Color, Modifier, Rect, Style};
use helix_view::graphics::{Color, Modifier, Rect, Style, UnderlineStyle};

/// A buffer cell
#[derive(Debug, Clone, PartialEq)]
pub struct Cell {
pub symbol: String,
pub fg: Color,
pub bg: Color,
pub underline: Color,
pub underline_color: Color,
pub underline_style: UnderlineStyle,
pub modifier: Modifier,
}

Expand Down Expand Up @@ -45,9 +46,13 @@ impl Cell {
if let Some(c) = style.bg {
self.bg = c;
}
if let Some(c) = style.underline {
self.underline = c;
if let Some(c) = style.underline_color {
self.underline_color = c;
}
if let Some(style) = style.underline_style {
self.underline_style = style;
}

self.modifier.insert(style.add_modifier);
self.modifier.remove(style.sub_modifier);
self
Expand All @@ -57,7 +62,8 @@ impl Cell {
Style::default()
.fg(self.fg)
.bg(self.bg)
.underline(self.underline)
.underline_color(self.underline_color)
.underline_style(self.underline_style)
.add_modifier(self.modifier)
}

Expand All @@ -66,7 +72,8 @@ impl Cell {
self.symbol.push(' ');
self.fg = Color::Reset;
self.bg = Color::Reset;
self.underline = Color::Reset;
self.underline_color = Color::Reset;
self.underline_style = UnderlineStyle::Reset;
self.modifier = Modifier::empty();
}
}
Expand All @@ -77,7 +84,8 @@ impl Default for Cell {
symbol: " ".into(),
fg: Color::Reset,
bg: Color::Reset,
underline: Color::Reset,
underline_color: Color::Reset,
underline_style: UnderlineStyle::Reset,
modifier: Modifier::empty(),
}
}
Expand Down
89 changes: 65 additions & 24 deletions helix-view/src/graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -332,22 +370,11 @@ bitflags! {
const BOLD = 0b0000_0000_0000_0001;
const DIM = 0b0000_0000_0000_0010;
const ITALIC = 0b0000_0000_0000_0100;
const UNDERLINED = 0b0000_0000_0000_1000;
const SLOW_BLINK = 0b0000_0000_0001_0000;
const RAPID_BLINK = 0b0000_0000_0010_0000;
const REVERSED = 0b0000_0000_0100_0000;
const HIDDEN = 0b0000_0000_1000_0000;
const CROSSED_OUT = 0b0000_0001_0000_0000;
const UNDERCURLED = 0b0000_0010_0000_0000;
const UNDERDOTTED = 0b0000_0100_0000_0000;
const UNDERDASHED = 0b0000_1000_0000_0000;
const DOUBLE_UNDERLINED = 0b0001_0000_0000_0000;

const ANY_UNDERLINE = Self::UNDERLINED.bits
| Self::UNDERCURLED.bits
| Self::UNDERDOTTED.bits
| Self::UNDERDASHED.bits
| Self::DOUBLE_UNDERLINED.bits;
}
}

Expand All @@ -359,16 +386,11 @@ 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),
"hidden" => Ok(Self::HIDDEN),
"crossed_out" => Ok(Self::CROSSED_OUT),
"undercurled" => Ok(Self::UNDERCURLED),
"underdotted" => Ok(Self::UNDERDOTTED),
"underdashed" => Ok(Self::UNDERDASHED),
"double_underlined" => Ok(Self::DOUBLE_UNDERLINED),
_ => Err("Invalid modifier"),
}
}
Expand Down Expand Up @@ -442,7 +464,8 @@ impl FromStr for Modifier {
pub struct Style {
pub fg: Option<Color>,
pub bg: Option<Color>,
pub underline: Option<Color>,
pub underline_color: Option<Color>,
pub underline_style: Option<UnderlineStyle>,
pub add_modifier: Modifier,
pub sub_modifier: Modifier,
}
Expand All @@ -452,7 +475,8 @@ impl Default for Style {
Style {
fg: None,
bg: None,
underline: None,
underline_color: None,
underline_style: None,
add_modifier: Modifier::empty(),
sub_modifier: Modifier::empty(),
}
Expand All @@ -465,7 +489,8 @@ impl Style {
Style {
fg: Some(Color::Reset),
bg: Some(Color::Reset),
underline: Some(Color::Reset),
underline_color: None,
underline_style: None,
add_modifier: Modifier::empty(),
sub_modifier: Modifier::all(),
}
Expand Down Expand Up @@ -507,12 +532,27 @@ impl Style {
///
/// ```rust
/// # use helix_view::graphics::{Color, Style};
/// let style = Style::default().underline(Color::Blue);
/// let diff = Style::default().underline(Color::Red);
/// 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::Red));
/// ```
pub fn underline(mut self, color: Color) -> Style {
self.underline = Some(color);
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
}

Expand Down Expand Up @@ -572,7 +612,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 = other.underline.or(self.underline);
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);
Expand Down
Loading

0 comments on commit 89d9a20

Please sign in to comment.