Skip to main content

theme/theme/
syntax.rs

1use gpui::Hsla;
2
3use crate::color;
4
5/// Paint-only syntax colors. The hues follow the Git history graph's lane
6/// palette (indigo, pink, emerald, amber, red, neutral), while light-mode
7/// variants are darkened enough to remain readable as text on white.
8#[derive(Debug, Clone)]
9pub struct SyntaxPalette {
10    pub comment: Hsla,
11    pub keyword: Hsla,
12    pub string: Hsla,
13    pub string_special: Hsla,
14    pub escape: Hsla,
15    pub number: Hsla,
16    pub boolean: Hsla,
17    pub type_name: Hsla,
18    pub type_builtin: Hsla,
19    pub constructor: Hsla,
20    pub function: Hsla,
21    pub function_builtin: Hsla,
22    pub macro_name: Hsla,
23    pub property: Hsla,
24    pub constant: Hsla,
25    pub variable: Hsla,
26    pub variable_special: Hsla,
27    pub parameter: Hsla,
28    pub operator: Hsla,
29    pub punctuation: Hsla,
30    pub tag: Hsla,
31    pub attribute: Hsla,
32    pub label: Hsla,
33    pub invalid: Hsla,
34}
35
36/// The token category a highlighter paints with, mirroring [`SyntaxPalette`]
37/// field-for-field so classification can name a kind without knowing its color.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum HighlightKind {
40    Comment,
41    Keyword,
42    String,
43    StringSpecial,
44    Escape,
45    Number,
46    Boolean,
47    TypeName,
48    TypeBuiltin,
49    Constructor,
50    Function,
51    FunctionBuiltin,
52    MacroName,
53    Property,
54    Constant,
55    Variable,
56    VariableSpecial,
57    Parameter,
58    Operator,
59    Punctuation,
60    Tag,
61    Attribute,
62    Label,
63    Invalid,
64}
65
66impl SyntaxPalette {
67    /// The palette color for a token kind.
68    pub fn color(&self, kind: HighlightKind) -> Hsla {
69        match kind {
70            HighlightKind::Comment => self.comment,
71            HighlightKind::Keyword => self.keyword,
72            HighlightKind::String => self.string,
73            HighlightKind::StringSpecial => self.string_special,
74            HighlightKind::Escape => self.escape,
75            HighlightKind::Number => self.number,
76            HighlightKind::Boolean => self.boolean,
77            HighlightKind::TypeName => self.type_name,
78            HighlightKind::TypeBuiltin => self.type_builtin,
79            HighlightKind::Constructor => self.constructor,
80            HighlightKind::Function => self.function,
81            HighlightKind::FunctionBuiltin => self.function_builtin,
82            HighlightKind::MacroName => self.macro_name,
83            HighlightKind::Property => self.property,
84            HighlightKind::Constant => self.constant,
85            HighlightKind::Variable => self.variable,
86            HighlightKind::VariableSpecial => self.variable_special,
87            HighlightKind::Parameter => self.parameter,
88            HighlightKind::Operator => self.operator,
89            HighlightKind::Punctuation => self.punctuation,
90            HighlightKind::Tag => self.tag,
91            HighlightKind::Attribute => self.attribute,
92            HighlightKind::Label => self.label,
93            HighlightKind::Invalid => self.invalid,
94        }
95    }
96
97    pub(crate) fn dark(text: Hsla, comment: Hsla, danger: Hsla) -> Self {
98        // Same sources and 72% saturation treatment as history::graph_color.
99        let indigo = git_graph_tone(color::oklch(0.673, 0.182, 276.935));
100        let pink = git_graph_tone(color::oklch(0.718, 0.202, 349.761));
101        let emerald = git_graph_tone(color::oklch(0.765, 0.177, 163.223));
102        let amber = git_graph_tone(color::oklch(0.828, 0.189, 84.429));
103        let red = git_graph_tone(danger);
104        Self {
105            comment,
106            keyword: indigo,
107            string: emerald,
108            string_special: pink,
109            escape: pink,
110            number: amber,
111            boolean: amber,
112            type_name: amber,
113            type_builtin: emerald,
114            constructor: amber,
115            function: indigo,
116            function_builtin: pink,
117            macro_name: pink,
118            property: amber,
119            constant: emerald,
120            variable: text,
121            variable_special: pink,
122            parameter: text,
123            operator: text,
124            punctuation: text,
125            tag: pink,
126            attribute: amber,
127            label: amber,
128            invalid: red,
129        }
130    }
131
132    pub(crate) fn light(text: Hsla, comment: Hsla, danger: Hsla) -> Self {
133        // Match the light graph's hue families at text-safe lightness.
134        let indigo = git_graph_tone(color::oklch(0.47, 0.20, 276.966));
135        let pink = git_graph_tone(color::oklch(0.47, 0.17, 0.584));
136        let emerald = git_graph_tone(color::oklch(0.46, 0.11, 163.225));
137        let amber = git_graph_tone(color::oklch(0.47, 0.12, 48.998));
138        let red = git_graph_tone(danger);
139        Self {
140            comment,
141            keyword: indigo,
142            string: emerald,
143            string_special: pink,
144            escape: pink,
145            number: amber,
146            boolean: amber,
147            type_name: amber,
148            type_builtin: emerald,
149            constructor: amber,
150            function: indigo,
151            function_builtin: pink,
152            macro_name: pink,
153            property: amber,
154            constant: emerald,
155            variable: text,
156            variable_special: pink,
157            parameter: text,
158            operator: text,
159            punctuation: text,
160            tag: pink,
161            attribute: amber,
162            label: amber,
163            invalid: red,
164        }
165    }
166}
167
168/// Git history intentionally softens lane saturation so the graph remains
169/// colorful without competing with content. Syntax uses the same treatment.
170fn git_graph_tone(mut color: Hsla) -> Hsla {
171    color.s *= 0.72;
172    color
173}