syntax.rs

  1use indexmap::IndexMap;
  2use serde::Deserialize;
  3use strum::EnumIter;
  4
  5#[derive(Debug, PartialEq, Eq, Deserialize)]
  6#[serde(untagged)]
  7pub enum VsCodeTokenScope {
  8    One(String),
  9    Many(Vec<String>),
 10}
 11
 12#[derive(Debug, Deserialize)]
 13pub struct VsCodeTokenColor {
 14    pub name: Option<String>,
 15    pub scope: Option<VsCodeTokenScope>,
 16    pub settings: VsCodeTokenColorSettings,
 17}
 18
 19#[derive(Debug, Deserialize)]
 20pub struct VsCodeTokenColorSettings {
 21    pub foreground: Option<String>,
 22    pub background: Option<String>,
 23    #[serde(rename = "fontStyle")]
 24    pub font_style: Option<String>,
 25}
 26
 27#[derive(Debug, PartialEq, Copy, Clone, EnumIter)]
 28pub enum ZedSyntaxToken {
 29    Attribute,
 30    Boolean,
 31    Comment,
 32    CommentDoc,
 33    Constant,
 34    Constructor,
 35    Embedded,
 36    Emphasis,
 37    EmphasisStrong,
 38    Enum,
 39    Function,
 40    Hint,
 41    Keyword,
 42    Label,
 43    LinkText,
 44    LinkUri,
 45    Number,
 46    Operator,
 47    Predictive,
 48    Preproc,
 49    Primary,
 50    Property,
 51    Punctuation,
 52    PunctuationBracket,
 53    PunctuationDelimiter,
 54    PunctuationListMarker,
 55    PunctuationSpecial,
 56    String,
 57    StringEscape,
 58    StringRegex,
 59    StringSpecial,
 60    StringSpecialSymbol,
 61    Tag,
 62    TextLiteral,
 63    Title,
 64    Type,
 65    Variable,
 66    VariableSpecial,
 67    Variant,
 68}
 69
 70impl std::fmt::Display for ZedSyntaxToken {
 71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 72        write!(
 73            f,
 74            "{}",
 75            match self {
 76                ZedSyntaxToken::Attribute => "attribute",
 77                ZedSyntaxToken::Boolean => "boolean",
 78                ZedSyntaxToken::Comment => "comment",
 79                ZedSyntaxToken::CommentDoc => "comment.doc",
 80                ZedSyntaxToken::Constant => "constant",
 81                ZedSyntaxToken::Constructor => "constructor",
 82                ZedSyntaxToken::Embedded => "embedded",
 83                ZedSyntaxToken::Emphasis => "emphasis",
 84                ZedSyntaxToken::EmphasisStrong => "emphasis.strong",
 85                ZedSyntaxToken::Enum => "enum",
 86                ZedSyntaxToken::Function => "function",
 87                ZedSyntaxToken::Hint => "hint",
 88                ZedSyntaxToken::Keyword => "keyword",
 89                ZedSyntaxToken::Label => "label",
 90                ZedSyntaxToken::LinkText => "link_text",
 91                ZedSyntaxToken::LinkUri => "link_uri",
 92                ZedSyntaxToken::Number => "number",
 93                ZedSyntaxToken::Operator => "operator",
 94                ZedSyntaxToken::Predictive => "predictive",
 95                ZedSyntaxToken::Preproc => "preproc",
 96                ZedSyntaxToken::Primary => "primary",
 97                ZedSyntaxToken::Property => "property",
 98                ZedSyntaxToken::Punctuation => "punctuation",
 99                ZedSyntaxToken::PunctuationBracket => "punctuation.bracket",
100                ZedSyntaxToken::PunctuationDelimiter => "punctuation.delimiter",
101                ZedSyntaxToken::PunctuationListMarker => "punctuation.list_marker",
102                ZedSyntaxToken::PunctuationSpecial => "punctuation.special",
103                ZedSyntaxToken::String => "string",
104                ZedSyntaxToken::StringEscape => "string.escape",
105                ZedSyntaxToken::StringRegex => "string.regex",
106                ZedSyntaxToken::StringSpecial => "string.special",
107                ZedSyntaxToken::StringSpecialSymbol => "string.special.symbol",
108                ZedSyntaxToken::Tag => "tag",
109                ZedSyntaxToken::TextLiteral => "text.literal",
110                ZedSyntaxToken::Title => "title",
111                ZedSyntaxToken::Type => "type",
112                ZedSyntaxToken::Variable => "variable",
113                ZedSyntaxToken::VariableSpecial => "variable.special",
114                ZedSyntaxToken::Variant => "variant",
115            }
116        )
117    }
118}
119
120impl ZedSyntaxToken {
121    pub fn find_best_token_color_match<'a>(
122        &self,
123        token_colors: &'a [VsCodeTokenColor],
124    ) -> Option<&'a VsCodeTokenColor> {
125        let mut ranked_matches = IndexMap::new();
126
127        for (ix, token_color) in token_colors.iter().enumerate() {
128            if token_color.settings.foreground.is_none() {
129                continue;
130            }
131
132            let Some(rank) = self.rank_match(token_color) else {
133                continue;
134            };
135
136            if rank > 0 {
137                ranked_matches.insert(ix, rank);
138            }
139        }
140
141        ranked_matches
142            .into_iter()
143            .max_by_key(|(_, rank)| *rank)
144            .map(|(ix, _)| &token_colors[ix])
145    }
146
147    fn rank_match(&self, token_color: &VsCodeTokenColor) -> Option<u32> {
148        let candidate_scopes = match token_color.scope.as_ref()? {
149            VsCodeTokenScope::One(scope) => vec![scope],
150            VsCodeTokenScope::Many(scopes) => scopes.iter().collect(),
151        }
152        .iter()
153        .flat_map(|scope| scope.split(',').map(|s| s.trim()))
154        .collect::<Vec<_>>();
155
156        let scopes_to_match = self.to_vscode();
157        let number_of_scopes_to_match = scopes_to_match.len();
158
159        let mut matches = 0;
160
161        for (ix, scope) in scopes_to_match.into_iter().enumerate() {
162            // Assign each entry a weight that is inversely proportional to its
163            // position in the list.
164            //
165            // Entries towards the front are weighted higher than those towards the end.
166            let weight = (number_of_scopes_to_match - ix) as u32;
167
168            if candidate_scopes.contains(&scope) {
169                matches += 1 + weight;
170            }
171        }
172
173        Some(matches)
174    }
175
176    pub fn fallbacks(&self) -> &[Self] {
177        match self {
178            ZedSyntaxToken::CommentDoc => &[ZedSyntaxToken::Comment],
179            ZedSyntaxToken::Number => &[ZedSyntaxToken::Constant],
180            ZedSyntaxToken::VariableSpecial => &[ZedSyntaxToken::Variable],
181            ZedSyntaxToken::PunctuationBracket
182            | ZedSyntaxToken::PunctuationDelimiter
183            | ZedSyntaxToken::PunctuationListMarker
184            | ZedSyntaxToken::PunctuationSpecial => &[ZedSyntaxToken::Punctuation],
185            ZedSyntaxToken::StringEscape
186            | ZedSyntaxToken::StringRegex
187            | ZedSyntaxToken::StringSpecial
188            | ZedSyntaxToken::StringSpecialSymbol => &[ZedSyntaxToken::String],
189            _ => &[],
190        }
191    }
192
193    fn to_vscode(self) -> Vec<&'static str> {
194        match self {
195            ZedSyntaxToken::Attribute => vec!["entity.other.attribute-name"],
196            ZedSyntaxToken::Boolean => vec!["constant.language"],
197            ZedSyntaxToken::Comment => vec!["comment"],
198            ZedSyntaxToken::CommentDoc => vec!["comment.block.documentation"],
199            ZedSyntaxToken::Constant => vec!["constant", "constant.language", "constant.character"],
200            ZedSyntaxToken::Constructor => {
201                vec![
202                    "entity.name.tag",
203                    "entity.name.function.definition.special.constructor",
204                ]
205            }
206            ZedSyntaxToken::Embedded => vec!["meta.embedded"],
207            ZedSyntaxToken::Emphasis => vec!["markup.italic"],
208            ZedSyntaxToken::EmphasisStrong => vec![
209                "markup.bold",
210                "markup.italic markup.bold",
211                "markup.bold markup.italic",
212            ],
213            ZedSyntaxToken::Enum => vec!["support.type.enum"],
214            ZedSyntaxToken::Function => vec![
215                "entity.function",
216                "entity.name.function",
217                "variable.function",
218            ],
219            ZedSyntaxToken::Hint => vec![],
220            ZedSyntaxToken::Keyword => vec![
221                "keyword",
222                "keyword.other.fn.rust",
223                "keyword.control",
224                "keyword.control.fun",
225                "keyword.control.class",
226                "punctuation.accessor",
227                "entity.name.tag",
228            ],
229            ZedSyntaxToken::Label => vec![
230                "label",
231                "entity.name",
232                "entity.name.import",
233                "entity.name.package",
234            ],
235            ZedSyntaxToken::LinkText => vec!["markup.underline.link", "string.other.link"],
236            ZedSyntaxToken::LinkUri => vec!["markup.underline.link", "string.other.link"],
237            ZedSyntaxToken::Number => vec!["constant.numeric", "number"],
238            ZedSyntaxToken::Operator => vec!["operator", "keyword.operator"],
239            ZedSyntaxToken::Predictive => vec![],
240            ZedSyntaxToken::Preproc => vec![
241                "preproc",
242                "meta.preprocessor",
243                "punctuation.definition.preprocessor",
244            ],
245            ZedSyntaxToken::Primary => vec![],
246            ZedSyntaxToken::Property => vec![
247                "variable.member",
248                "support.type.property-name",
249                "variable.object.property",
250                "variable.other.field",
251            ],
252            ZedSyntaxToken::Punctuation => vec![
253                "punctuation",
254                "punctuation.section",
255                "punctuation.accessor",
256                "punctuation.separator",
257                "punctuation.definition.tag",
258            ],
259            ZedSyntaxToken::PunctuationBracket => vec![
260                "punctuation.bracket",
261                "punctuation.definition.tag.begin",
262                "punctuation.definition.tag.end",
263            ],
264            ZedSyntaxToken::PunctuationDelimiter => vec![
265                "punctuation.delimiter",
266                "punctuation.separator",
267                "punctuation.terminator",
268            ],
269            ZedSyntaxToken::PunctuationListMarker => {
270                vec!["markup.list punctuation.definition.list.begin"]
271            }
272            ZedSyntaxToken::PunctuationSpecial => vec!["punctuation.special"],
273            ZedSyntaxToken::String => vec!["string"],
274            ZedSyntaxToken::StringEscape => {
275                vec!["string.escape", "constant.character", "constant.other"]
276            }
277            ZedSyntaxToken::StringRegex => vec!["string.regex"],
278            ZedSyntaxToken::StringSpecial => vec!["string.special", "constant.other.symbol"],
279            ZedSyntaxToken::StringSpecialSymbol => {
280                vec!["string.special.symbol", "constant.other.symbol"]
281            }
282            ZedSyntaxToken::Tag => vec!["tag", "entity.name.tag", "meta.tag.sgml"],
283            ZedSyntaxToken::TextLiteral => vec!["text.literal", "string"],
284            ZedSyntaxToken::Title => vec!["title", "entity.name"],
285            ZedSyntaxToken::Type => vec![
286                "entity.name.type",
287                "entity.name.type.primitive",
288                "entity.name.type.numeric",
289                "keyword.type",
290                "support.type",
291                "support.type.primitive",
292                "support.class",
293            ],
294            ZedSyntaxToken::Variable => vec![
295                "variable",
296                "variable.language",
297                "variable.member",
298                "variable.parameter",
299                "variable.parameter.function-call",
300            ],
301            ZedSyntaxToken::VariableSpecial => vec![
302                "variable.special",
303                "variable.member",
304                "variable.annotation",
305                "variable.language",
306            ],
307            ZedSyntaxToken::Variant => vec!["variant"],
308        }
309    }
310}