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) -> Self {
17 let ss = SyntaxSet::load_defaults_newlines();
18 let ts = ThemeSet::load_defaults();
19 let theme = &ts.themes["base16-ocean.dark"];
20
21 let mut cache = HashMap::new();
22
23 for (fi, file) in diff_data.files.iter().enumerate() {
24 let filename = file.target_file.trim_start_matches("b/");
26 let syntax = ss
27 .find_syntax_for_file(filename)
28 .ok()
29 .flatten()
30 .unwrap_or_else(|| ss.find_syntax_plain_text());
31
32 let mut highlighter = HighlightLines::new(syntax, theme);
33
34 for (hi, hunk) in file.hunks.iter().enumerate() {
35 for (li, line) in hunk.lines.iter().enumerate() {
36 let spans = match highlighter.highlight_line(&line.content, &ss) {
37 Ok(regions) => regions
38 .into_iter()
39 .map(|(style, text)| {
40 (syntect_to_ratatui_style(style), text.to_string())
41 })
42 .collect(),
43 Err(_) => {
44 vec![(Style::default(), line.content.clone())]
46 }
47 };
48 cache.insert((fi, hi, li), spans);
49 }
50 }
51 }
52
53 Self { cache }
54 }
55
56 pub fn get(&self, file_idx: usize, hunk_idx: usize, line_idx: usize) -> Option<&Vec<(Style, String)>> {
58 self.cache.get(&(file_idx, hunk_idx, line_idx))
59 }
60}
61
62fn syntect_to_ratatui_style(syntect_style: SyntectStyle) -> Style {
64 let fg = syntect_style.foreground;
65 Style::default().fg(Color::Rgb(fg.r, fg.g, fg.b))
66}