aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--helix-tui/Cargo.toml1
-rw-r--r--helix-tui/src/backend/crossterm.rs55
-rw-r--r--runtime/themes/onedark.toml5
4 files changed, 60 insertions, 8 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e5edcaac..f980c417 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -177,6 +177,12 @@ dependencies = [
]
[[package]]
+name = "cxterminfo"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da92c5e3aaf2cc1fea346d9b3bac0c59c6ffc1d1d46f18d991d449912a3e6f07"
+
+[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -504,6 +510,7 @@ dependencies = [
"bitflags",
"cassowary",
"crossterm",
+ "cxterminfo",
"helix-core",
"helix-view",
"serde",
diff --git a/helix-tui/Cargo.toml b/helix-tui/Cargo.toml
index b220c64f..1c6a6a8d 100644
--- a/helix-tui/Cargo.toml
+++ b/helix-tui/Cargo.toml
@@ -20,6 +20,7 @@ bitflags = "1.3"
cassowary = "0.3"
unicode-segmentation = "1.10"
crossterm = { version = "0.25", optional = true }
+cxterminfo = "0.2"
serde = { version = "1", "optional" = true, features = ["derive"]}
helix-view = { version = "0.6", path = "../helix-view", features = ["term"] }
helix-core = { version = "0.6", path = "../helix-core" }
diff --git a/helix-tui/src/backend/crossterm.rs b/helix-tui/src/backend/crossterm.rs
index fe9da919..3a50074e 100644
--- a/helix-tui/src/backend/crossterm.rs
+++ b/helix-tui/src/backend/crossterm.rs
@@ -11,8 +11,38 @@ use crossterm::{
use helix_view::graphics::{Color, CursorKind, Modifier, Rect};
use std::io::{self, Write};
+fn vte_version() -> Option<usize> {
+ std::env::var("VTE_VERSION").ok()?.parse().ok()
+}
+
+/// Describes terminal capabilities like extended underline, truecolor, etc.
+#[derive(Copy, Clone, Debug, Default)]
+struct Capabilities {
+ /// Support for undercurled, underdashed, etc.
+ has_extended_underlines: bool,
+}
+
+impl Capabilities {
+ /// Detect capabilities from the terminfo database located based
+ /// on the $TERM environment variable. If detection fails, returns
+ /// a default value where no capability is supported.
+ pub fn from_env_or_default() -> Self {
+ match cxterminfo::terminfo::TermInfo::from_env() {
+ Err(_) => Capabilities::default(),
+ Ok(t) => Capabilities {
+ // Smulx, VTE: https://unix.stackexchange.com/a/696253/246284
+ // Su (used by kitty): https://sw.kovidgoyal.net/kitty/underlines
+ has_extended_underlines: t.get_ext_string("Smulx").is_some()
+ || *t.get_ext_bool("Su").unwrap_or(&false)
+ || vte_version() >= Some(5102),
+ },
+ }
+ }
+}
+
pub struct CrosstermBackend<W: Write> {
buffer: W,
+ capabilities: Capabilities,
}
impl<W> CrosstermBackend<W>
@@ -20,7 +50,10 @@ where
W: Write,
{
pub fn new(buffer: W) -> CrosstermBackend<W> {
- CrosstermBackend { buffer }
+ CrosstermBackend {
+ buffer,
+ capabilities: Capabilities::from_env_or_default(),
+ }
}
}
@@ -61,7 +94,7 @@ where
from: modifier,
to: cell.modifier,
};
- diff.queue(&mut self.buffer)?;
+ diff.queue(&mut self.buffer, self.capabilities)?;
modifier = cell.modifier;
}
if cell.fg != fg {
@@ -141,7 +174,7 @@ struct ModifierDiff {
}
impl ModifierDiff {
- fn queue<W>(&self, mut w: W) -> io::Result<()>
+ fn queue<W>(&self, mut w: W, caps: Capabilities) -> io::Result<()>
where
W: io::Write,
{
@@ -172,6 +205,14 @@ 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)))?;
@@ -186,16 +227,16 @@ impl ModifierDiff {
map_error(queue!(w, SetAttribute(CAttribute::Underlined)))?;
}
if added.contains(Modifier::UNDERCURLED) {
- map_error(queue!(w, SetAttribute(CAttribute::Undercurled)))?;
+ queue_styled_underline(CAttribute::Undercurled, &mut w)?;
}
if added.contains(Modifier::UNDERDOTTED) {
- map_error(queue!(w, SetAttribute(CAttribute::Underdotted)))?;
+ queue_styled_underline(CAttribute::Underdotted, &mut w)?;
}
if added.contains(Modifier::UNDERDASHED) {
- map_error(queue!(w, SetAttribute(CAttribute::Underdashed)))?;
+ queue_styled_underline(CAttribute::Underdashed, &mut w)?;
}
if added.contains(Modifier::DOUBLE_UNDERLINED) {
- map_error(queue!(w, SetAttribute(CAttribute::DoubleUnderlined)))?;
+ queue_styled_underline(CAttribute::DoubleUnderlined, &mut w)?;
}
if added.contains(Modifier::DIM) {
map_error(queue!(w, SetAttribute(CAttribute::Dim)))?;
diff --git a/runtime/themes/onedark.toml b/runtime/themes/onedark.toml
index a4cc12eb..e2bc2c47 100644
--- a/runtime/themes/onedark.toml
+++ b/runtime/themes/onedark.toml
@@ -39,7 +39,10 @@
"diff.delta" = "gold"
"diff.minus" = "red"
-diagnostic = { modifiers = ["undercurled"] }
+"diagnostic.info" = { underline = "blue", modifiers = ["undercurled"] }
+"diagnostic.hint" = { underline = "green", modifiers = ["undercurled"] }
+"diagnostic.warning" = { underline = "yellow", modifiers = ["undercurled"] }
+"diagnostic.error" = { underline = "red", modifiers = ["undercurled"] }
"info" = { fg = "blue", modifiers = ["bold"] }
"hint" = { fg = "green", modifiers = ["bold"] }
"warning" = { fg = "yellow", modifiers = ["bold"] }