semantic_diff/
highlight.rs1use crate::diff::DiffData;
2use ratatui::style::{Color, Style};
3use std::collections::HashMap;
4use syntect::easy::HighlightLines;
5use syntect::highlighting::{Style as SyntectStyle, ThemeSet};
6use syntect::parsing::SyntaxSet;
7
8pub struct HighlightCache {
11 cache: HashMap<(usize, usize, usize), Vec<(Style, String)>>,
12}
13
14impl HighlightCache {
15 pub fn new(diff_data: &DiffData, syntect_theme_name: &str) -> Self {
19 let ss = SyntaxSet::load_defaults_newlines();
20 let ts = ThemeSet::load_defaults();
21 let theme = ts
23 .themes
24 .get(syntect_theme_name)
25 .unwrap_or_else(|| &ts.themes["base16-ocean.dark"]);
26
27 let mut cache = HashMap::new();
28
29 for (fi, file) in diff_data.files.iter().enumerate() {
30 let filename = file.target_file.trim_start_matches("b/");
32 let syntax = ss
33 .find_syntax_for_file(filename)
34 .ok()
35 .flatten()
36 .unwrap_or_else(|| ss.find_syntax_plain_text());
37
38 let mut highlighter = HighlightLines::new(syntax, theme);
39
40 for (hi, hunk) in file.hunks.iter().enumerate() {
41 for (li, line) in hunk.lines.iter().enumerate() {
42 let spans = match highlighter.highlight_line(&line.content, &ss) {
43 Ok(regions) => regions
44 .into_iter()
45 .map(|(style, text)| {
46 (syntect_to_ratatui_style(style), text.to_string())
47 })
48 .collect(),
49 Err(_) => {
50 vec![(Style::default(), line.content.clone())]
52 }
53 };
54 cache.insert((fi, hi, li), spans);
55 }
56 }
57 }
58
59 Self { cache }
60 }
61
62 pub fn get(&self, file_idx: usize, hunk_idx: usize, line_idx: usize) -> Option<&Vec<(Style, String)>> {
64 self.cache.get(&(file_idx, hunk_idx, line_idx))
65 }
66}
67
68fn syntect_to_ratatui_style(syntect_style: SyntectStyle) -> Style {
70 let fg = syntect_style.foreground;
71 Style::default().fg(Color::Rgb(fg.r, fg.g, fg.b))
72}