1use crate::theme::Theme;
2use clankerdiff_ratatui::highlighted_line;
3use clankerdiff_ratatui::syntax::LanguageHint;
4use clankerdiff_ratatui::theme::Fingerprint;
5use ratatui::style::Style;
6use ratatui::text::Line;
7use std::rc::Rc;
8
9#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
10pub struct HighlightStats {
11 pub calls: u64,
12 pub cache_misses: u64,
13 pub bytes_highlighted: u64,
14}
15
16pub type HighlightedLines = Rc<[Line<'static>]>;
17
18#[derive(Default)]
19pub struct SyntaxHighlighter {
20 pub(crate) inner: clankerdiff_ratatui::syntax::SyntaxHighlighter,
21}
22
23impl SyntaxHighlighter {
24 pub fn new() -> Self {
25 Self::default()
26 }
27
28 pub fn take_stats(&mut self) -> HighlightStats {
29 let stats = self.inner.take_stats();
30 HighlightStats { calls: stats.calls, cache_misses: stats.misses, bytes_highlighted: stats.bytes as u64 }
31 }
32
33 pub fn highlight(&mut self, code: &str, language: &str, theme: &Theme) -> HighlightedLines {
34 let highlights = self.inner.with_theme(&theme.review().syntax).highlight_document(
35 Fingerprint::of([code.as_bytes()]),
36 LanguageHint::InfoString(language),
37 || code,
38 );
39 let base = Style::new().fg(theme.code_fg).bg(theme.code_bg);
40 match highlights {
41 Ok(highlights) => code
42 .split('\n')
43 .enumerate()
44 .map(|(index, source)| highlighted_line(source, highlights.line(index).unwrap_or_default(), base))
45 .collect::<Vec<_>>()
46 .into(),
47 Err(error) => {
48 tracing::warn!(%error, "Could not highlight source snippet");
49 vec![Line::styled(format!("Could not highlight source: {error}"), base)].into()
50 }
51 }
52 }
53}