1use anyhow::Result;
2use gpui::{Hsla, Rgba};
3use indexmap::IndexMap;
4use strum::IntoEnumIterator;
5use theme::{
6 StatusColorsRefinement, ThemeColorsRefinement, UserFontStyle, UserFontWeight,
7 UserHighlightStyle, UserSyntaxTheme, UserTheme, UserThemeStylesRefinement,
8};
9
10use crate::util::Traverse;
11use crate::vscode::VsCodeTheme;
12use crate::ThemeMetadata;
13
14use super::ZedSyntaxToken;
15
16pub(crate) fn try_parse_color(color: &str) -> Result<Hsla> {
17 Ok(Rgba::try_from(color)?.into())
18}
19
20pub(crate) fn try_parse_font_weight(font_style: &str) -> Option<UserFontWeight> {
21 match font_style {
22 style if style.contains("bold") => Some(UserFontWeight::BOLD),
23 _ => None,
24 }
25}
26
27pub(crate) fn try_parse_font_style(font_style: &str) -> Option<UserFontStyle> {
28 match font_style {
29 style if style.contains("italic") => Some(UserFontStyle::Italic),
30 style if style.contains("oblique") => Some(UserFontStyle::Oblique),
31 _ => None,
32 }
33}
34
35pub struct VsCodeThemeConverter {
36 theme: VsCodeTheme,
37 theme_metadata: ThemeMetadata,
38}
39
40impl VsCodeThemeConverter {
41 pub fn new(theme: VsCodeTheme, theme_metadata: ThemeMetadata) -> Self {
42 Self {
43 theme,
44 theme_metadata,
45 }
46 }
47
48 pub fn convert(self) -> Result<UserTheme> {
49 let appearance = self.theme_metadata.appearance.into();
50
51 let status_color_refinements = self.convert_status_colors()?;
52 let theme_colors_refinements = self.convert_theme_colors()?;
53 let syntax_theme = self.convert_syntax_theme()?;
54
55 Ok(UserTheme {
56 name: self.theme_metadata.name.into(),
57 appearance,
58 styles: UserThemeStylesRefinement {
59 colors: theme_colors_refinements,
60 status: status_color_refinements,
61 syntax: Some(syntax_theme),
62 },
63 })
64 }
65
66 fn convert_status_colors(&self) -> Result<StatusColorsRefinement> {
67 let vscode_colors = &self.theme.colors;
68
69 Ok(StatusColorsRefinement {
70 // conflict: None,
71 // created: None,
72 deleted: vscode_colors
73 .error_foreground
74 .as_ref()
75 .traverse(|color| try_parse_color(&color))?,
76 error: vscode_colors
77 .error_foreground
78 .as_ref()
79 .traverse(|color| try_parse_color(&color))?,
80 hidden: vscode_colors
81 .tab_inactive_foreground
82 .as_ref()
83 .traverse(|color| try_parse_color(&color))?,
84 // ignored: None,
85 // info: None,
86 // modified: None,
87 // renamed: None,
88 // success: None,
89 warning: vscode_colors
90 .list_warning_foreground
91 .as_ref()
92 .traverse(|color| try_parse_color(&color))?,
93 ..Default::default()
94 })
95 }
96
97 fn convert_theme_colors(&self) -> Result<ThemeColorsRefinement> {
98 let vscode_colors = &self.theme.colors;
99
100 Ok(ThemeColorsRefinement {
101 border: vscode_colors
102 .panel_border
103 .as_ref()
104 .traverse(|color| try_parse_color(&color))?,
105 border_variant: vscode_colors
106 .panel_border
107 .as_ref()
108 .traverse(|color| try_parse_color(&color))?,
109 border_focused: vscode_colors
110 .focus_border
111 .as_ref()
112 .traverse(|color| try_parse_color(&color))?,
113 border_disabled: vscode_colors
114 .panel_border
115 .as_ref()
116 .traverse(|color| try_parse_color(&color))?,
117 border_selected: vscode_colors
118 .panel_border
119 .as_ref()
120 .traverse(|color| try_parse_color(&color))?,
121 border_transparent: vscode_colors
122 .panel_border
123 .as_ref()
124 .traverse(|color| try_parse_color(&color))?,
125 elevated_surface_background: vscode_colors
126 .panel_background
127 .as_ref()
128 .traverse(|color| try_parse_color(&color))?,
129 surface_background: vscode_colors
130 .panel_background
131 .as_ref()
132 .traverse(|color| try_parse_color(&color))?,
133 background: vscode_colors
134 .editor_background
135 .as_ref()
136 .traverse(|color| try_parse_color(&color))?,
137 element_background: vscode_colors
138 .button_background
139 .as_ref()
140 .traverse(|color| try_parse_color(&color))?,
141 element_hover: vscode_colors
142 .list_hover_background
143 .as_ref()
144 .traverse(|color| try_parse_color(&color))?,
145 element_selected: vscode_colors
146 .list_active_selection_background
147 .as_ref()
148 .traverse(|color| try_parse_color(&color))?,
149 ghost_element_hover: vscode_colors
150 .list_hover_background
151 .as_ref()
152 .traverse(|color| try_parse_color(&color))?,
153 drop_target_background: vscode_colors
154 .list_drop_background
155 .as_ref()
156 .traverse(|color| try_parse_color(&color))?,
157 text: vscode_colors
158 .foreground
159 .as_ref()
160 .traverse(|color| try_parse_color(&color))?
161 .or_else(|| {
162 self.theme
163 .token_colors
164 .iter()
165 .find(|token_color| token_color.scope.is_none())
166 .and_then(|token_color| token_color.settings.foreground.as_ref())
167 .traverse(|color| try_parse_color(&color))
168 .ok()
169 .flatten()
170 }),
171 tab_active_background: vscode_colors
172 .tab_active_background
173 .as_ref()
174 .traverse(|color| try_parse_color(&color))?,
175 tab_inactive_background: vscode_colors
176 .tab_inactive_background
177 .as_ref()
178 .traverse(|color| try_parse_color(&color))?,
179 editor_background: vscode_colors
180 .editor_background
181 .as_ref()
182 .traverse(|color| try_parse_color(&color))?,
183 editor_gutter_background: vscode_colors
184 .editor_background
185 .as_ref()
186 .traverse(|color| try_parse_color(&color))?,
187 editor_line_number: vscode_colors
188 .editor_line_number_foreground
189 .as_ref()
190 .traverse(|color| try_parse_color(&color))?,
191 editor_active_line_number: vscode_colors
192 .editor_foreground
193 .as_ref()
194 .traverse(|color| try_parse_color(&color))?,
195 terminal_background: vscode_colors
196 .terminal_background
197 .as_ref()
198 .traverse(|color| try_parse_color(&color))?,
199 terminal_ansi_bright_black: vscode_colors
200 .terminal_ansi_bright_black
201 .as_ref()
202 .traverse(|color| try_parse_color(&color))?,
203 terminal_ansi_bright_red: vscode_colors
204 .terminal_ansi_bright_red
205 .as_ref()
206 .traverse(|color| try_parse_color(&color))?,
207 terminal_ansi_bright_green: vscode_colors
208 .terminal_ansi_bright_green
209 .as_ref()
210 .traverse(|color| try_parse_color(&color))?,
211 terminal_ansi_bright_yellow: vscode_colors
212 .terminal_ansi_bright_yellow
213 .as_ref()
214 .traverse(|color| try_parse_color(&color))?,
215 terminal_ansi_bright_blue: vscode_colors
216 .terminal_ansi_bright_blue
217 .as_ref()
218 .traverse(|color| try_parse_color(&color))?,
219 terminal_ansi_bright_magenta: vscode_colors
220 .terminal_ansi_bright_magenta
221 .as_ref()
222 .traverse(|color| try_parse_color(&color))?,
223 terminal_ansi_bright_cyan: vscode_colors
224 .terminal_ansi_bright_cyan
225 .as_ref()
226 .traverse(|color| try_parse_color(&color))?,
227 terminal_ansi_bright_white: vscode_colors
228 .terminal_ansi_bright_white
229 .as_ref()
230 .traverse(|color| try_parse_color(&color))?,
231 terminal_ansi_black: vscode_colors
232 .terminal_ansi_black
233 .as_ref()
234 .traverse(|color| try_parse_color(&color))?,
235 terminal_ansi_red: vscode_colors
236 .terminal_ansi_red
237 .as_ref()
238 .traverse(|color| try_parse_color(&color))?,
239 terminal_ansi_green: vscode_colors
240 .terminal_ansi_green
241 .as_ref()
242 .traverse(|color| try_parse_color(&color))?,
243 terminal_ansi_yellow: vscode_colors
244 .terminal_ansi_yellow
245 .as_ref()
246 .traverse(|color| try_parse_color(&color))?,
247 terminal_ansi_blue: vscode_colors
248 .terminal_ansi_blue
249 .as_ref()
250 .traverse(|color| try_parse_color(&color))?,
251 terminal_ansi_magenta: vscode_colors
252 .terminal_ansi_magenta
253 .as_ref()
254 .traverse(|color| try_parse_color(&color))?,
255 terminal_ansi_cyan: vscode_colors
256 .terminal_ansi_cyan
257 .as_ref()
258 .traverse(|color| try_parse_color(&color))?,
259 terminal_ansi_white: vscode_colors
260 .terminal_ansi_white
261 .as_ref()
262 .traverse(|color| try_parse_color(&color))?,
263 ..Default::default()
264 })
265 }
266
267 fn convert_syntax_theme(&self) -> Result<UserSyntaxTheme> {
268 let mut highlight_styles = IndexMap::new();
269
270 for syntax_token in ZedSyntaxToken::iter() {
271 let multimatch_scopes = syntax_token.to_vscode();
272
273 let token_color = self.theme.token_colors.iter().find(|token_color| {
274 token_color
275 .scope
276 .as_ref()
277 .map(|scope| scope.multimatch(&multimatch_scopes))
278 .unwrap_or(false)
279 });
280
281 let Some(token_color) = token_color else {
282 continue;
283 };
284
285 let highlight_style = UserHighlightStyle {
286 color: token_color
287 .settings
288 .foreground
289 .as_ref()
290 .traverse(|color| try_parse_color(&color))?,
291 font_style: token_color
292 .settings
293 .font_style
294 .as_ref()
295 .and_then(|style| try_parse_font_style(&style)),
296 font_weight: token_color
297 .settings
298 .font_style
299 .as_ref()
300 .and_then(|style| try_parse_font_weight(&style)),
301 };
302
303 if highlight_style.is_empty() {
304 continue;
305 }
306
307 highlight_styles.insert(syntax_token.to_string(), highlight_style);
308 }
309
310 Ok(UserSyntaxTheme {
311 highlights: highlight_styles.into_iter().collect(),
312 })
313
314 // let mut highlight_styles = IndexMap::new();
315
316 // for token_color in self.theme.token_colors {
317 // highlight_styles.extend(token_color.highlight_styles()?);
318 // }
319
320 // let syntax_theme = UserSyntaxTheme {
321 // highlights: highlight_styles.into_iter().collect(),
322 // };
323
324 // pub fn highlight_styles(&self) -> Result<IndexMap<String, UserHighlightStyle>> {
325 // let mut highlight_styles = IndexMap::new();
326
327 // for syntax_token in ZedSyntaxToken::iter() {
328 // let scope = syntax_token.to_scope();
329
330 // // let token_color =
331 // }
332
333 // let scope = match self.scope {
334 // Some(VsCodeTokenScope::One(ref scope)) => vec![scope.clone()],
335 // Some(VsCodeTokenScope::Many(ref scopes)) => scopes.clone(),
336 // None => return Ok(IndexMap::new()),
337 // };
338
339 // for scope in &scope {
340 // let Some(syntax_token) = Self::to_zed_token(&scope) else {
341 // continue;
342 // };
343
344 // let highlight_style = UserHighlightStyle {
345 // color: self
346 // .settings
347 // .foreground
348 // .as_ref()
349 // .traverse(|color| try_parse_color(&color))?,
350 // font_style: self
351 // .settings
352 // .font_style
353 // .as_ref()
354 // .and_then(|style| try_parse_font_style(&style)),
355 // font_weight: self
356 // .settings
357 // .font_style
358 // .as_ref()
359 // .and_then(|style| try_parse_font_weight(&style)),
360 // };
361
362 // if highlight_style.is_empty() {
363 // continue;
364 // }
365
366 // highlight_styles.insert(syntax_token, highlight_style);
367 // }
368
369 // Ok(highlight_styles)
370 // }
371 }
372}