git_igitt/util/
syntax_highlight.rs

1use lazy_static::lazy_static;
2use syntect::easy::HighlightLines;
3use syntect::highlighting::Color as SynColor;
4use syntect::highlighting::{Style, Theme, ThemeSet};
5use syntect::parsing::SyntaxSet;
6use tui::style::Color;
7use tui::text::{Span, Spans, Text};
8
9lazy_static! {
10    static ref SYNTAX: Syntax = Syntax {
11        syntax: SyntaxSet::load_defaults_nonewlines(),
12        themes: ThemeSet::load_defaults()
13    };
14    static ref THEME: Theme = create_custom_theme();
15}
16
17struct Syntax {
18    pub syntax: SyntaxSet,
19    pub themes: ThemeSet,
20}
21
22fn create_custom_theme() -> Theme {
23    let mut theme = SYNTAX.themes.themes["Solarized (dark)"].clone();
24    theme.settings.foreground = theme.settings.foreground.map(|color| brighter(color, 0.4)); //Some(syntect::highlighting::Color::WHITE);
25    theme
26}
27
28fn brighter(color: SynColor, factor: f32) -> SynColor {
29    SynColor {
30        r: color.r + ((255 - color.r) as f32 * factor) as u8,
31        g: color.g + ((255 - color.g) as f32 * factor) as u8,
32        b: color.b + ((255 - color.b) as f32 * factor) as u8,
33        a: color.a,
34    }
35}
36
37pub fn highlight(lines: &str, extension: &str) -> Option<Vec<Vec<(Style, String)>>> {
38    let syntax = match SYNTAX.syntax.find_syntax_by_extension(extension) {
39        None => return None,
40        Some(syntax) => syntax,
41    };
42
43    let mut h = HighlightLines::new(syntax, &THEME);
44
45    // TODO: Due to a bug in tui-rs (?), it is necessary to trim line ends.
46    // Otherwise, artifacts of the previous buffer may occur
47    let spans: Vec<_> = lines
48        .lines()
49        .map(|line| {
50            h.highlight_line(line.trim_end(), &SYNTAX.syntax)
51                .unwrap()
52                .into_iter()
53                .map(|(s, l)| (s, l.to_string()))
54                .collect::<Vec<_>>()
55        })
56        .collect();
57
58    Some(spans)
59}
60
61pub fn as_styled(lines: &'_ [Vec<(Style, String)>]) -> Text<'_> {
62    let spans: Vec<_> = lines
63        .iter()
64        .map(|line| {
65            Spans(
66                line.iter()
67                    .map(|(style, string)| {
68                        Span::styled(
69                            string,
70                            tui::style::Style::default().fg(Color::Rgb(
71                                style.foreground.r,
72                                style.foreground.g,
73                                style.foreground.b,
74                            )),
75                        )
76                    })
77                    .collect(),
78            )
79        })
80        .collect();
81
82    Text::from(spans)
83}