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}