vscode.rs

  1use anyhow::Result;
  2use gpui::{Hsla, Refineable, Rgba};
  3use serde::Deserialize;
  4use theme::{
  5    Appearance, GitStatusColors, PlayerColors, StatusColors, SyntaxTheme, SystemColors,
  6    ThemeColors, ThemeColorsRefinement, ThemeStyles, ThemeVariant,
  7};
  8
  9use crate::ThemeMetadata;
 10
 11#[derive(Deserialize, Debug)]
 12pub struct VsCodeTheme {
 13    #[serde(rename = "$schema")]
 14    pub schema: Option<String>,
 15    pub name: Option<String>,
 16    pub author: Option<String>,
 17    pub maintainers: Option<Vec<String>>,
 18    #[serde(rename = "semanticClass")]
 19    pub semantic_class: Option<String>,
 20    #[serde(rename = "semanticHighlighting")]
 21    pub semantic_highlighting: Option<bool>,
 22    pub colors: VsCodeColors,
 23}
 24
 25#[derive(Debug, Deserialize)]
 26pub struct VsCodeColors {
 27    #[serde(rename = "editor.foreground")]
 28    text: String,
 29    #[serde(rename = "editor.background")]
 30    editor_background: String,
 31    terminal_background: String,
 32    terminal_ansi_bright_black: String,
 33    terminal_ansi_bright_red: String,
 34    terminal_ansi_bright_green: String,
 35    terminal_ansi_bright_yellow: String,
 36    terminal_ansi_bright_blue: String,
 37    terminal_ansi_bright_magenta: String,
 38    terminal_ansi_bright_cyan: String,
 39    terminal_ansi_bright_white: String,
 40    terminal_ansi_black: String,
 41    terminal_ansi_red: String,
 42    terminal_ansi_green: String,
 43    terminal_ansi_yellow: String,
 44    terminal_ansi_blue: String,
 45    terminal_ansi_magenta: String,
 46    terminal_ansi_cyan: String,
 47    terminal_ansi_white: String,
 48}
 49
 50fn try_parse_color(color: &str) -> Result<Hsla> {
 51    Ok(Rgba::try_from(color)?.into())
 52}
 53
 54pub struct VsCodeThemeConverter {
 55    theme: VsCodeTheme,
 56    theme_metadata: ThemeMetadata,
 57}
 58
 59impl VsCodeThemeConverter {
 60    pub fn new(theme: VsCodeTheme, theme_metadata: ThemeMetadata) -> Self {
 61        Self {
 62            theme,
 63            theme_metadata,
 64        }
 65    }
 66
 67    pub fn convert(self) -> Result<ThemeVariant> {
 68        let appearance = self.theme_metadata.appearance.into();
 69
 70        let mut theme_colors = match appearance {
 71            Appearance::Light => ThemeColors::default_light(),
 72            Appearance::Dark => ThemeColors::default_dark(),
 73        };
 74
 75        let vscode_colors = &self.theme.colors;
 76
 77        let theme_colors_refinements = ThemeColorsRefinement {
 78            background: Some(try_parse_color(&vscode_colors.editor_background)?),
 79            text: Some(try_parse_color(&vscode_colors.text)?),
 80            terminal_background: Some(try_parse_color(&vscode_colors.terminal_background)?),
 81            terminal_ansi_bright_black: Some(try_parse_color(
 82                &vscode_colors.terminal_ansi_bright_black,
 83            )?),
 84            terminal_ansi_bright_red: Some(try_parse_color(
 85                &vscode_colors.terminal_ansi_bright_red,
 86            )?),
 87            terminal_ansi_bright_green: Some(try_parse_color(
 88                &vscode_colors.terminal_ansi_bright_green,
 89            )?),
 90            terminal_ansi_bright_yellow: Some(try_parse_color(
 91                &vscode_colors.terminal_ansi_bright_yellow,
 92            )?),
 93            terminal_ansi_bright_blue: Some(try_parse_color(
 94                &vscode_colors.terminal_ansi_bright_blue,
 95            )?),
 96            terminal_ansi_bright_magenta: Some(try_parse_color(
 97                &vscode_colors.terminal_ansi_bright_magenta,
 98            )?),
 99            terminal_ansi_bright_cyan: Some(try_parse_color(
100                &vscode_colors.terminal_ansi_bright_cyan,
101            )?),
102            terminal_ansi_bright_white: Some(try_parse_color(
103                &vscode_colors.terminal_ansi_bright_white,
104            )?),
105            terminal_ansi_black: Some(try_parse_color(&vscode_colors.terminal_ansi_black)?),
106            terminal_ansi_red: Some(try_parse_color(&vscode_colors.terminal_ansi_red)?),
107            terminal_ansi_green: Some(try_parse_color(&vscode_colors.terminal_ansi_green)?),
108            terminal_ansi_yellow: Some(try_parse_color(&vscode_colors.terminal_ansi_yellow)?),
109            terminal_ansi_blue: Some(try_parse_color(&vscode_colors.terminal_ansi_blue)?),
110            terminal_ansi_magenta: Some(try_parse_color(&vscode_colors.terminal_ansi_magenta)?),
111            terminal_ansi_cyan: Some(try_parse_color(&vscode_colors.terminal_ansi_cyan)?),
112            terminal_ansi_white: Some(try_parse_color(&vscode_colors.terminal_ansi_white)?),
113            ..Default::default()
114        };
115
116        theme_colors.refine(&theme_colors_refinements);
117
118        Ok(ThemeVariant {
119            id: uuid::Uuid::new_v4().to_string(),
120            name: self.theme_metadata.name.into(),
121            appearance,
122            styles: ThemeStyles {
123                system: SystemColors::default(),
124                colors: theme_colors,
125                status: StatusColors::default(),
126                git: GitStatusColors::default(),
127                player: PlayerColors::default(),
128                syntax: SyntaxTheme::default_dark(),
129            },
130        })
131    }
132}
133
134// #[cfg(test)]
135// mod tests {
136//     use super::*;
137//     use std::path::PathBuf;
138
139//     #[test]
140//     fn test_deserialize_theme() {
141//         let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
142//         let root_dir = manifest_dir.parent().unwrap().parent().unwrap();
143
144//         let mut d = root_dir.to_path_buf();
145//         d.push("assets/themes/src/vsc/dracula/dracula.json");
146
147//         let data = std::fs::read_to_string(d).expect("Unable to read file");
148
149//         let result: Theme = serde_json::from_str(&data).unwrap();
150//         println!("{:#?}", result);
151//     }
152// }