1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use crate::{Color, HighlightLines, Style, SyntaxReference, SyntaxSet, Theme, ThemeSet};

const DEFAULT_THEME: &str = "solarized-light";

pub struct TextHighlighter {
    syntax_set: &'static SyntaxSet,
    theme_set: &'static ThemeSet,
    theme_name: Option<String>,
    syntax_ref: Option<&'static SyntaxReference>,
    highlight_lines: Option<HighlightLines<'static>>,
}

impl Default for TextHighlighter {
    fn default() -> Self {
        let syntax_set: &SyntaxSet = &crate::SYNTAX_SET;
        let theme_set: &ThemeSet = &crate::THEME_SET;
        Self {
            syntax_set,
            theme_set,
            theme_name: None,
            syntax_ref: None,
            highlight_lines: None,
        }
    }
}

impl TextHighlighter {
    pub fn with_theme(theme_name: &str) -> Self {
        let mut text_highlighter = Self::default();
        text_highlighter.select_theme(theme_name);
        text_highlighter
    }

    pub fn set_syntax_token(&mut self, syntax_token: &str) {
        let syntax_ref: &SyntaxReference = self
            .syntax_set
            .find_syntax_by_token(syntax_token)
            .unwrap_or_else(|| self.syntax_set.find_syntax_plain_text());
        self.syntax_ref = Some(syntax_ref);

        self.set_highlight_lines();
    }

    /// createa new HighlightLines and override existing
    /// to reset the underlying parse state
    fn set_highlight_lines(&mut self) {
        let syntax_ref = self
            .syntax_ref
            .as_ref()
            .expect("must have syntax_ref already");
        self.highlight_lines = Some(HighlightLines::new(syntax_ref, self.active_theme()));
    }

    /// reset syntect parse state by resetting highlight lines
    pub fn reset(&mut self) {
        self.set_highlight_lines();
    }

    /// set the theme name
    pub fn select_theme(&mut self, theme_name: &str) {
        if self.theme_set.themes.get(theme_name).is_some() {
            self.theme_name = Some(theme_name.to_string());
        } else {
            format!("The valid theme names are: {:?}", self.get_theme_names());
            log::trace!("The valid theme names are: {:?}", self.get_theme_names());
            panic!("theme name: {} doesn't match", theme_name);
        }
    }

    fn get_theme_names(&self) -> Vec<String> {
        self.theme_set.themes.keys().cloned().collect()
    }

    pub fn highlight_line<'l>(
        &mut self,
        line: &'l str,
    ) -> Result<Vec<(Style, &'l str)>, syntect::Error> {
        let highlight_lines = self
            .highlight_lines
            .as_mut()
            .expect("must have a highlight line");
        highlight_lines.highlight_line(line, self.syntax_set)
    }

    pub fn active_theme(&self) -> &'static Theme {
        if let Some(theme_name) = self.theme_name.as_ref() {
            &self.theme_set.themes[theme_name]
        } else {
            &self.theme_set.themes[DEFAULT_THEME]
        }
    }

    pub fn gutter_background(&self) -> Option<Color> {
        self.active_theme().settings.gutter
    }

    pub fn gutter_foreground(&self) -> Option<Color> {
        self.active_theme().settings.gutter_foreground
    }

    pub fn theme_background(&self) -> Option<Color> {
        self.active_theme().settings.background
    }

    pub fn theme_foreground(&self) -> Option<Color> {
        self.active_theme().settings.foreground
    }

    pub fn selection_background(&self) -> Option<Color> {
        self.active_theme().settings.selection
    }
}