use ratatui::style::Style; use ratatui::style::Stylize; use ratatui::text::Line; use ratatui::text::Span; use std::sync::OnceLock; use tree_sitter_highlight::Highlight; use tree_sitter_highlight::HighlightConfiguration; use tree_sitter_highlight::HighlightEvent; use tree_sitter_highlight::Highlighter; // Ref: https://github.com/tree-sitter/tree-sitter-bash/blob/master/queries/highlights.scm #[derive(Copy, Clone)] enum BashHighlight { Comment, Constant, Embedded, Function, Keyword, Number, Operator, Property, String, } impl BashHighlight { const ALL: [Self; 9] = [ Self::Comment, Self::Constant, Self::Embedded, Self::Function, Self::Keyword, Self::Number, Self::Operator, Self::Property, Self::String, ]; const fn as_str(self) -> &'static str { match self { Self::Comment => "comment", Self::Constant => "constant", Self::Embedded => "embedded", Self::Function => "function", Self::Keyword => "keyword", Self::Number => "number", Self::Operator => "operator", Self::Property => "property", Self::String => "string", } } fn style(self) -> Style { match self { Self::Comment | Self::Operator | Self::String => Style::default().dim(), _ => Style::default(), } } } static HIGHLIGHT_CONFIG: OnceLock = OnceLock::new(); fn highlight_names() -> &'static [&'static str] { static NAMES: OnceLock<[&'static str; BashHighlight::ALL.len()]> = OnceLock::new(); NAMES .get_or_init(|| BashHighlight::ALL.map(BashHighlight::as_str)) .as_slice() } fn highlight_config() -> &'static HighlightConfiguration { HIGHLIGHT_CONFIG.get_or_init(|| { let language = tree_sitter_bash::LANGUAGE.into(); #[expect(clippy::expect_used)] let mut config = HighlightConfiguration::new( language, "bash", tree_sitter_bash::HIGHLIGHT_QUERY, "", "", ) .expect("load bash highlight query"); config.configure(highlight_names()); config }) } fn highlight_for(highlight: Highlight) -> BashHighlight { BashHighlight::ALL[highlight.0] } fn push_segment(lines: &mut Vec>, segment: &str, style: Option