Detect extended underline support using terminfo

The cxterminfo crate has been used over popular alternatives
like `term` since it supports querying for extended capabilities
and also for it's small codebase size (which will make it easy
to inline it into helix in the future if required).
This commit is contained in:
Gokul Soumya 2022-09-07 17:28:58 +05:30 committed by Pascal Kuthe
parent de72b9c04c
commit 79d3d44c3d
No known key found for this signature in database
GPG key ID: D715E8655AE166A6
4 changed files with 60 additions and 8 deletions

7
Cargo.lock generated
View file

@ -176,6 +176,12 @@ dependencies = [
"winapi",
]
[[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"
@ -504,6 +510,7 @@ dependencies = [
"bitflags",
"cassowary",
"crossterm",
"cxterminfo",
"helix-core",
"helix-view",
"serde",

View file

@ -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" }

View file

@ -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)))?;

View file

@ -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"] }