Skip to main content

scal_core/
theme.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4use crate::{Syntax, color::Color, highlight_specs};
5
6#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
7/// <https://github.com/tinted-theming/schemes/tree/spec-0.11/base16>
8#[allow(missing_docs)]
9pub struct Base16 {
10    pub colors: [Color; 16],
11}
12
13impl Base16 {
14    #[must_use]
15    /// <https://github.com/tinted-theming/schemes/tree/spec-0.11/base16>
16    /// Use normal hex colors and just remove # from the front and replace it with 0x
17    /// ```
18    /// let theme = Theme::from_base16(Base16::from_hex([
19    ///     0x11121d, 0x1A1B2A, 0x212234, 0x282c34, 0x4a5057, 0xa0a8cd, 0xa0a8cd, 0xa0a8cd, 0xee6d85,
20    ///     0xf6955b, 0xd7a65f, 0x95c561, 0x38a89d, 0x7199ee, 0xa485dd, 0x773440,
21    /// ]));
22    /// ```
23    pub fn from_hex(hex: [u32; 16]) -> Self {
24        let mut colors = [Color::BLACK; 16];
25        for (i, &h) in hex.iter().enumerate() {
26            colors[i] = Color::from(h);
27        }
28        Self { colors }
29    }
30}
31
32/// A syntax highlighting theme, constructed from a Base16 palette.
33#[derive(Clone, Debug, Serialize, Deserialize)]
34pub struct Theme {
35    /// <https://github.com/tinted-theming/schemes/tree/spec-0.11/base16>
36    pub base: Base16,
37
38    #[allow(missing_docs)]
39    pub syntax_specific: HashMap<Syntax, SyntaxTheme>,
40}
41impl Theme {
42    #[must_use]
43    #[allow(missing_docs)]
44    pub fn from_base16(base: Base16) -> Self {
45        let mut syntax_specific = HashMap::new();
46
47        for (syntax, spec) in highlight_specs::all_specs() {
48            let theme = SyntaxTheme::from_base16_and_spec(base, spec);
49            syntax_specific.insert(syntax, theme);
50        }
51
52        Self {
53            base,
54            syntax_specific,
55        }
56    }
57}
58
59impl Default for Theme {
60    fn default() -> Self {
61        Self::from_base16(Base16::default())
62    }
63}
64
65impl Default for Base16 {
66    fn default() -> Self {
67        Self {
68            colors: [
69                Color {
70                    r: 0.067,
71                    g: 0.071,
72                    b: 0.114,
73                    a: 1.0,
74                },
75                Color {
76                    r: 0.102,
77                    g: 0.106,
78                    b: 0.165,
79                    a: 1.0,
80                },
81                Color {
82                    r: 0.129,
83                    g: 0.133,
84                    b: 0.204,
85                    a: 1.0,
86                },
87                Color {
88                    r: 0.157,
89                    g: 0.173,
90                    b: 0.204,
91                    a: 1.0,
92                },
93                Color {
94                    r: 0.290,
95                    g: 0.314,
96                    b: 0.341,
97                    a: 1.0,
98                },
99                Color {
100                    r: 0.627,
101                    g: 0.659,
102                    b: 0.804,
103                    a: 1.0,
104                },
105                Color {
106                    r: 0.627,
107                    g: 0.659,
108                    b: 0.804,
109                    a: 1.0,
110                },
111                Color {
112                    r: 0.627,
113                    g: 0.659,
114                    b: 0.804,
115                    a: 1.0,
116                },
117                Color {
118                    r: 0.933,
119                    g: 0.427,
120                    b: 0.522,
121                    a: 1.0,
122                },
123                Color {
124                    r: 0.965,
125                    g: 0.584,
126                    b: 0.357,
127                    a: 1.0,
128                },
129                Color {
130                    r: 0.843,
131                    g: 0.651,
132                    b: 0.373,
133                    a: 1.0,
134                },
135                Color {
136                    r: 0.584,
137                    g: 0.773,
138                    b: 0.380,
139                    a: 1.0,
140                },
141                Color {
142                    r: 0.220,
143                    g: 0.659,
144                    b: 0.616,
145                    a: 1.0,
146                },
147                Color {
148                    r: 0.443,
149                    g: 0.600,
150                    b: 0.933,
151                    a: 1.0,
152                },
153                Color {
154                    r: 0.643,
155                    g: 0.522,
156                    b: 0.867,
157                    a: 1.0,
158                },
159                Color {
160                    r: 0.467,
161                    g: 0.204,
162                    b: 0.251,
163                    a: 1.0,
164                },
165            ],
166        }
167    }
168}
169/// Highlight specification used for syntax highlighting using Tree-sitter.
170pub struct HighlightSpec {
171    /// Names of the Tree-sitter nodes. Syntax specific
172    pub names: Vec<String>,
173    /// indices into Base16 palette
174    pub indices: Vec<usize>,
175}
176#[allow(missing_docs)]
177impl HighlightSpec {
178    pub fn new(names: Vec<&str>, indices: Vec<usize>) -> Self {
179        Self {
180            indices,
181            names: names
182                .iter()
183                .map(std::string::ToString::to_string)
184                .collect::<Vec<String>>(),
185        }
186    }
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
190/// Syntax specific syntax highlighting theme settings
191pub struct SyntaxTheme {
192    /// Names of the Tree-sitter nodes. Syntax specific
193    pub highlight_names: Vec<String>,
194    /// indices into Base16 palette
195    pub highlight_colors: Vec<Color>,
196    #[allow(missing_docs)]
197    pub default_color: Color,
198}
199
200#[allow(missing_docs)]
201impl SyntaxTheme {
202    #[must_use]
203    pub fn from_base16_and_spec(base: Base16, spec: HighlightSpec) -> Self {
204        let mut colors = Vec::with_capacity(spec.indices.len());
205
206        for idx in &spec.indices {
207            colors.push(base.colors[*idx]);
208        }
209
210        Self {
211            highlight_names: spec.names,
212            highlight_colors: colors,
213            default_color: base.colors[5],
214        }
215    }
216
217    #[must_use]
218    pub fn color_for_name(&self, name: &str) -> Option<Color> {
219        self.highlight_names
220            .iter()
221            .position(|n| n == name)
222            .and_then(|idx| self.highlight_colors.get(idx).copied())
223    }
224}