vscode.rs

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