converter.rs

  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}