use super::Highlighter; use pulldown_cmark::escape::escape_html; use std::fmt::Write; use tree_sitter_highlight::{Highlight as TSHighlight, HighlightConfiguration, HighlightEvent}; pub struct TreeSitterHighlighter { config: &'static HighlightConfiguration, } // SAFETY: the only thing we store is a HighlightConfiguration, which is thread safe // unsafe impl Sync for TreeSitterHighlighter {} impl TreeSitterHighlighter { pub fn new(config: &'static HighlightConfiguration) -> Self { Self { config } } } impl Highlighter for TreeSitterHighlighter { fn highlight(&self, code: &str) -> String { let mut highlighter = tree_sitter_highlight::Highlighter::new(); let highlights = highlighter .highlight(self.config, code.as_bytes(), None, |_| None) .unwrap(); let mut buf = String::new(); for highlight in highlights { let event = highlight.unwrap(); match event { HighlightEvent::Source { start, end } => { escape_html( &mut buf, std::str::from_utf8(&code.as_bytes()[start..end]).unwrap(), ) .unwrap(); } HighlightEvent::HighlightStart(TSHighlight(idx)) => { write!( &mut buf, "", HIGHLIGHT_CLASS_NAMES[idx] ) .unwrap(); } HighlightEvent::HighlightEnd => { buf.push_str(""); } } } buf } } pub const HIGHLIGHT_NAMES: &[&str] = &[ "attribute", "comment", "constant", "embedded", // something inside a string, e.g., the foo in `${foo}` in js "function", "function.builtin", "keyword", "module", // e.g., atoms in elixir "number", "operator", "property", "punctuation.special", // e.g., in js the dollar sign and curly braces in `${foo}` "string", "string.special.key", // e.g., json keys "tag", // e.g., html tags, css class/id selectors "type", "type.builtin", "variable", "variable.builtin", ]; const HIGHLIGHT_CLASS_NAMES: &[&str] = &[ "attr", "cmt", "const", "emb", "fn", "builtin", "kw", "mod", "num", "op", "prop", "punct-sp", "str", "key", "tag", "type", "builtin", "var", "builtin", ];