From e48332c81f91fdbd709740e4ad06fd2723e1d060 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 2 Nov 2023 18:00:55 -0400 Subject: [PATCH] Parse VSCode theme files --- assets/themes/src/{vsc => vscode}/ayu/LICENSE | 0 .../src/{vsc => vscode}/ayu/ayu-dark.json | 0 .../src/{vsc => vscode}/ayu/ayu-light.json | 0 .../src/{vsc => vscode}/ayu/ayu-mirage.json | 0 .../src/{vsc => vscode}/ayu/family.json | 0 .../src/{vsc => vscode}/dracula/LICENSE | 0 .../src/{vsc => vscode}/dracula/dracula.json | 0 .../src/{vsc => vscode}/dracula/family.json | 0 crates/theme2/src/theme2.rs | 3 +- crates/theme_importer/src/main.rs | 128 +++++++++++------- crates/theme_importer/src/vscode.rs | 72 +++++----- 11 files changed, 118 insertions(+), 85 deletions(-) rename assets/themes/src/{vsc => vscode}/ayu/LICENSE (100%) rename assets/themes/src/{vsc => vscode}/ayu/ayu-dark.json (100%) rename assets/themes/src/{vsc => vscode}/ayu/ayu-light.json (100%) rename assets/themes/src/{vsc => vscode}/ayu/ayu-mirage.json (100%) rename assets/themes/src/{vsc => vscode}/ayu/family.json (100%) rename assets/themes/src/{vsc => vscode}/dracula/LICENSE (100%) rename assets/themes/src/{vsc => vscode}/dracula/dracula.json (100%) rename assets/themes/src/{vsc => vscode}/dracula/family.json (100%) diff --git a/assets/themes/src/vsc/ayu/LICENSE b/assets/themes/src/vscode/ayu/LICENSE similarity index 100% rename from assets/themes/src/vsc/ayu/LICENSE rename to assets/themes/src/vscode/ayu/LICENSE diff --git a/assets/themes/src/vsc/ayu/ayu-dark.json b/assets/themes/src/vscode/ayu/ayu-dark.json similarity index 100% rename from assets/themes/src/vsc/ayu/ayu-dark.json rename to assets/themes/src/vscode/ayu/ayu-dark.json diff --git a/assets/themes/src/vsc/ayu/ayu-light.json b/assets/themes/src/vscode/ayu/ayu-light.json similarity index 100% rename from assets/themes/src/vsc/ayu/ayu-light.json rename to assets/themes/src/vscode/ayu/ayu-light.json diff --git a/assets/themes/src/vsc/ayu/ayu-mirage.json b/assets/themes/src/vscode/ayu/ayu-mirage.json similarity index 100% rename from assets/themes/src/vsc/ayu/ayu-mirage.json rename to assets/themes/src/vscode/ayu/ayu-mirage.json diff --git a/assets/themes/src/vsc/ayu/family.json b/assets/themes/src/vscode/ayu/family.json similarity index 100% rename from assets/themes/src/vsc/ayu/family.json rename to assets/themes/src/vscode/ayu/family.json diff --git a/assets/themes/src/vsc/dracula/LICENSE b/assets/themes/src/vscode/dracula/LICENSE similarity index 100% rename from assets/themes/src/vsc/dracula/LICENSE rename to assets/themes/src/vscode/dracula/LICENSE diff --git a/assets/themes/src/vsc/dracula/dracula.json b/assets/themes/src/vscode/dracula/dracula.json similarity index 100% rename from assets/themes/src/vsc/dracula/dracula.json rename to assets/themes/src/vscode/dracula/dracula.json diff --git a/assets/themes/src/vsc/dracula/family.json b/assets/themes/src/vscode/dracula/family.json similarity index 100% rename from assets/themes/src/vsc/dracula/family.json rename to assets/themes/src/vscode/dracula/family.json diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index 0aa3542a77..4232fde324 100644 --- a/crates/theme2/src/theme2.rs +++ b/crates/theme2/src/theme2.rs @@ -49,8 +49,7 @@ pub struct ThemeFamily { impl ThemeFamily {} pub struct ThemeVariant { - #[allow(dead_code)] - pub(crate) id: String, + pub id: String, pub name: SharedString, pub appearance: Appearance, pub styles: ThemeStyles, diff --git a/crates/theme_importer/src/main.rs b/crates/theme_importer/src/main.rs index 82e2105e0c..b616b44e47 100644 --- a/crates/theme_importer/src/main.rs +++ b/crates/theme_importer/src/main.rs @@ -1,15 +1,21 @@ +use std::borrow::Cow; +use std::fs::{self, File}; use std::path::PathBuf; use std::str::FromStr; -use std::{borrow::Cow, fs::File}; use anyhow::{anyhow, Context, Result}; use convert_case::Case; -use gpui::{AssetSource, SharedString}; +use gpui::{serde_json, AssetSource, SharedString}; use log::LevelFilter; use rust_embed::RustEmbed; use serde::Deserialize; use simplelog::SimpleLogger; -use theme::{default_color_scales, ThemeColorsRefinement, ThemeFamily}; +use theme::{ + default_color_scales, Appearance, GitStatusColors, PlayerColors, StatusColors, SyntaxTheme, + SystemColors, ThemeColors, ThemeColorsRefinement, ThemeFamily, ThemeStyles, ThemeVariant, +}; + +use crate::vscode::VsCodeTheme; mod vscode; @@ -23,20 +29,21 @@ pub(crate) fn new_theme_family(name: String, author: String) -> ThemeFamily { } } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] struct FamilyJson { pub name: String, + pub author: String, pub themes: Vec, } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "snake_case")] enum ThemeAppearanceJson { Light, Dark, } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] struct ThemeVariantJson { pub name: String, pub appearance: ThemeAppearanceJson, @@ -69,55 +76,80 @@ fn main() -> Result<()> { let themes_path = PathBuf::from_str("crates/theme2/src/themes")?; - let mut theme_modules = Vec::new(); + let vscode_themes_path = PathBuf::from_str("assets/themes/src/vscode/")?; - for theme_path in Assets.list("themes/src/vsc/")? { - let (_, theme_name) = theme_path.split_once("themes/").unwrap(); + let mut theme_families = Vec::new(); - if theme_name == ".gitkeep" { - continue; + for theme_family_dir in fs::read_dir(&vscode_themes_path)? { + let theme_family_dir = theme_family_dir?; + + let theme_family_slug = theme_family_dir + .path() + .file_stem() + .ok_or(anyhow!("no file stem")) + .map(|stem| stem.to_string_lossy().to_string())?; + + let family_metadata_file = File::open(theme_family_dir.path().join("family.json")) + .context(format!("no `family.json` found for '{theme_family_slug}'"))?; + + let family_metadata: FamilyJson = serde_json::from_reader(family_metadata_file).context( + format!("failed to parse `family.json` for '{theme_family_slug}'"), + )?; + + let mut themes = Vec::new(); + + for theme_entry in fs::read_dir(vscode_themes_path.join(theme_family_slug))? { + let theme_entry = theme_entry?; + + let theme_file_path = theme_entry.path(); + + let file_name = theme_file_path + .file_name() + .ok_or(anyhow!("no file stem")) + .map(|file_name| file_name.to_string_lossy())?; + + if !file_name.ends_with(".json") { + continue; + } + + if file_name == "family.json" { + continue; + } + + let theme_file = File::open(&theme_file_path)?; + + let theme: VsCodeTheme = serde_json::from_reader(theme_file) + .context(format!("failed to parse theme {theme_file_path:?}"))?; + + themes.push(theme); } - let theme_contents = Assets::get(&theme_path) - .with_context(|| format!("theme file not found: '{theme_path}'"))?; + let theme_family = ThemeFamily { + id: uuid::Uuid::new_v4().to_string(), + name: family_metadata.name.into(), + author: family_metadata.author.into(), + themes: themes + .into_iter() + .map(|theme| ThemeVariant { + id: uuid::Uuid::new_v4().to_string(), + name: "".into(), + appearance: Appearance::Dark, + styles: ThemeStyles { + system: SystemColors::default(), + colors: ThemeColors::default_dark(), + status: StatusColors::default(), + git: GitStatusColors::default(), + player: PlayerColors::default(), + syntax: SyntaxTheme::default_dark(), + }, + }) + .collect(), + scales: default_color_scales(), + }; - // let json_theme: JsonTheme = - // serde_json::from_str(std::str::from_utf8(&theme_contents.data)?) - // .context("failed to parse legacy theme")?; - - // let (json_theme, legacy_theme) = load_theme(&theme_path)?; - - // let theme = convert_theme(json_theme, legacy_theme)?; - - // let theme_slug = theme - // .metadata - // .name - // .as_ref() - // .replace("é", "e") - // .to_case(Case::Snake); - - // let mut output_file = File::create(themes_path.join(format!("{theme_slug}.rs")))?; - - // let theme_module = format!( - // r#" - // use gpui2::rgba; - - // use crate::{{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}}; - - // pub fn {theme_slug}() -> Theme {{ - // {theme_definition} - // }} - // "#, - // theme_definition = format!("{:#?}", ThemePrinter::new(theme)) - // ); - - // output_file.write_all(theme_module.as_bytes())?; - - theme_modules.push(theme_slug); + theme_families.push(theme_family); } - println!("Hello, world!"); - Ok(()) } diff --git a/crates/theme_importer/src/vscode.rs b/crates/theme_importer/src/vscode.rs index 559d6eb4b7..c664108c83 100644 --- a/crates/theme_importer/src/vscode.rs +++ b/crates/theme_importer/src/vscode.rs @@ -5,21 +5,21 @@ use serde::Deserialize; use theme::{default_color_scales, ColorScales, ThemeFamily}; #[derive(Deserialize, Debug)] -pub struct VSCodeTheme { +pub struct VsCodeTheme { #[serde(rename = "$schema")] - pub schema: String, - pub name: String, - pub author: String, - pub maintainers: Vec, + pub schema: Option, + pub name: Option, + pub author: Option, + pub maintainers: Option>, #[serde(rename = "semanticClass")] - pub semantic_class: String, + pub semantic_class: Option, #[serde(rename = "semanticHighlighting")] - pub semantic_highlighting: bool, - pub colors: VSCodeColors, + pub semantic_highlighting: Option, + pub colors: VsCodeColors, } #[derive(Debug, Deserialize)] -pub struct VSCodeColors { +pub struct VsCodeColors { #[serde(rename = "editor.foreground")] text: String, #[serde(rename = "editor.background")] @@ -27,36 +27,38 @@ pub struct VSCodeColors { } pub(crate) fn new_theme_family_from_vsc(path: &Path) -> Result { - let path_str = path.to_str().unwrap(); - let family_name = path_str.split('/').last().unwrap(); + todo!() - let mut json_files: Vec = Vec::new(); + // let path_str = path.to_str().unwrap(); + // let family_name = path_str.split('/').last().unwrap(); - if path.is_dir() { - for entry in std::fs::read_dir(path).unwrap() { - let entry = entry.unwrap(); - let path = entry.path(); - if path.is_file() { - if let Some(extension) = path.extension() { - if extension == "json" { - json_files.push(path.file_name().unwrap().to_str().unwrap().to_string()); - } - } - } - } - } else { - anyhow::bail!("Path is not a directory"); - } + // let mut json_files: Vec = Vec::new(); - let mut theme_family = ThemeFamily { - id: uuid::Uuid::new_v4().to_string(), - name: family_name.into(), - author: "New Theme Family".into(), - themes: Vec::new(), - scales: default_color_scales(), - }; + // if path.is_dir() { + // for entry in std::fs::read_dir(path).unwrap() { + // let entry = entry.unwrap(); + // let path = entry.path(); + // if path.is_file() { + // if let Some(extension) = path.extension() { + // if extension == "json" { + // json_files.push(path.file_name().unwrap().to_str().unwrap().to_string()); + // } + // } + // } + // } + // } else { + // anyhow::bail!("Path is not a directory"); + // } - Ok(theme_family) + // let mut theme_family = ThemeFamily { + // id: uuid::Uuid::new_v4().to_string(), + // name: family_name.into(), + // author: "New Theme Family".into(), + // themes: Vec::new(), + // scales: default_color_scales(), + // }; + + // Ok(theme_family) } #[cfg(test)]