use {
ratatui::text::{Line, Span},
std::sync::OnceLock,
syntect::{
easy::HighlightLines,
highlighting::{Theme, ThemeSet},
parsing::{syntax_definition::SyntaxDefinition, SyntaxReference, SyntaxSet},
},
syntect_tui::into_span,
terminal_colorsaurus::{theme_mode, QueryOptions, ThemeMode},
};
const SBPF_SYNTAX_YAML: &str = include_str!("sbpf.sublime-syntax");
static DETECTED_MODE: OnceLock<ThemeMode> = OnceLock::new();
pub fn detect_theme_mode_once() {
let _ = DETECTED_MODE.set(theme_mode(QueryOptions::default()).unwrap_or(ThemeMode::Dark));
}
struct Ctx {
syntaxes: SyntaxSet,
theme: Theme,
rust_syntax: SyntaxReference,
asm_syntax: SyntaxReference,
}
fn ctx() -> &'static Ctx {
static C: OnceLock<Ctx> = OnceLock::new();
C.get_or_init(|| {
let mut builder = two_face::syntax::extra_newlines().into_builder();
let sbpf_def =
SyntaxDefinition::load_from_str(SBPF_SYNTAX_YAML, true, Some("SBPF Assembly"))
.expect("compile bundled sbpf.sublime-syntax");
builder.add(sbpf_def);
let syntaxes = builder.build();
let themes = ThemeSet::load_defaults();
let (dark_name, light_name) = ("base16-eighties.dark", "Solarized (light)");
let theme_name = match DETECTED_MODE.get() {
Some(ThemeMode::Light) => light_name,
_ => dark_name,
};
let theme = themes
.themes
.get(theme_name)
.cloned()
.or_else(|| themes.themes.get(dark_name).cloned())
.unwrap_or_else(|| {
themes
.themes
.values()
.next()
.cloned()
.expect("default theme")
});
let rust_syntax = syntaxes
.find_syntax_by_token("rs")
.or_else(|| syntaxes.find_syntax_by_name("Rust"))
.unwrap_or_else(|| syntaxes.find_syntax_plain_text())
.clone();
let asm_syntax = syntaxes
.find_syntax_by_name("SBPF Assembly")
.or_else(|| syntaxes.find_syntax_by_name("Assembly x86 (NASM)"))
.or_else(|| syntaxes.find_syntax_by_token("asm"))
.unwrap_or_else(|| syntaxes.find_syntax_plain_text())
.clone();
Ctx {
syntaxes,
theme,
rust_syntax,
asm_syntax,
}
})
}
pub fn highlight_rust(line: &str) -> Line<'static> {
highlight_with(&ctx().rust_syntax, line)
}
pub fn highlight_asm(line: &str) -> Line<'static> {
highlight_with(&ctx().asm_syntax, line)
}
fn highlight_with(syntax: &SyntaxReference, line: &str) -> Line<'static> {
let c = ctx();
let mut hl = HighlightLines::new(syntax, &c.theme);
let owned_line = if line.ends_with('\n') {
line.to_owned()
} else {
format!("{line}\n")
};
let regions = match hl.highlight_line(&owned_line, &c.syntaxes) {
Ok(r) => r,
Err(_) => return Line::from(Span::raw(line.to_owned())),
};
let spans: Vec<Span<'static>> = regions
.into_iter()
.filter_map(|(style, text)| {
let trimmed = text.trim_end_matches('\n');
if trimmed.is_empty() {
return None;
}
into_span((style, trimmed)).ok().map(|s| {
let mut style = s.style;
style.bg = None;
Span::styled(trimmed.to_owned(), style)
})
})
.collect();
if spans.is_empty() {
Line::from(Span::raw(line.to_owned()))
} else {
Line::from(spans)
}
}