use crate::diff::DiffData;
use ratatui::style::{Color, Style};
use std::collections::HashMap;
use syntect::easy::HighlightLines;
use syntect::highlighting::{Style as SyntectStyle, ThemeSet};
use syntect::parsing::SyntaxSet;
pub struct HighlightCache {
cache: HashMap<(usize, usize, usize), Vec<(Style, String)>>,
}
impl HighlightCache {
pub fn new(diff_data: &DiffData, syntect_theme_name: &str) -> Self {
let ss = SyntaxSet::load_defaults_newlines();
let ts = ThemeSet::load_defaults();
let theme = ts
.themes
.get(syntect_theme_name)
.unwrap_or_else(|| &ts.themes["base16-ocean.dark"]);
let mut cache = HashMap::new();
for (fi, file) in diff_data.files.iter().enumerate() {
let filename = file.target_file.trim_start_matches("b/");
let syntax = ss
.find_syntax_for_file(filename)
.ok()
.flatten()
.unwrap_or_else(|| ss.find_syntax_plain_text());
let mut highlighter = HighlightLines::new(syntax, theme);
for (hi, hunk) in file.hunks.iter().enumerate() {
for (li, line) in hunk.lines.iter().enumerate() {
let spans = match highlighter.highlight_line(&line.content, &ss) {
Ok(regions) => regions
.into_iter()
.map(|(style, text)| {
(syntect_to_ratatui_style(style), text.to_string())
})
.collect(),
Err(_) => {
vec![(Style::default(), line.content.clone())]
}
};
cache.insert((fi, hi, li), spans);
}
}
}
Self { cache }
}
pub fn get(&self, file_idx: usize, hunk_idx: usize, line_idx: usize) -> Option<&Vec<(Style, String)>> {
self.cache.get(&(file_idx, hunk_idx, line_idx))
}
}
fn syntect_to_ratatui_style(syntect_style: SyntectStyle) -> Style {
let fg = syntect_style.foreground;
Style::default().fg(Color::Rgb(fg.r, fg.g, fg.b))
}