2023-11-02 22:57:13 +00:00
mod theme_printer ;
2023-11-03 14:54:12 +00:00
mod util ;
2023-11-02 22:57:13 +00:00
mod vscode ;
2023-11-02 22:00:55 +00:00
use std ::fs ::{ self , File } ;
2023-11-02 23:14:51 +00:00
use std ::io ::Write ;
2023-11-02 21:21:11 +00:00
use std ::path ::PathBuf ;
2023-11-07 17:45:09 +00:00
use std ::process ::Command ;
2023-11-02 21:21:11 +00:00
use std ::str ::FromStr ;
use anyhow ::{ anyhow , Context , Result } ;
2023-11-02 23:14:51 +00:00
use convert_case ::{ Case , Casing } ;
2023-11-02 22:24:38 +00:00
use gpui ::serde_json ;
2023-11-10 00:22:15 +00:00
use json_comments ::StripComments ;
2023-11-02 21:21:11 +00:00
use log ::LevelFilter ;
use serde ::Deserialize ;
use simplelog ::SimpleLogger ;
2023-11-07 16:40:07 +00:00
use theme ::{ Appearance , UserThemeFamily } ;
2023-11-02 22:00:55 +00:00
2023-11-07 16:40:07 +00:00
use crate ::theme_printer ::UserThemeFamilyPrinter ;
2023-11-02 22:00:55 +00:00
use crate ::vscode ::VsCodeTheme ;
2023-11-09 18:07:32 +00:00
use crate ::vscode ::VsCodeThemeConverter ;
2023-11-02 21:21:11 +00:00
2023-11-02 22:00:55 +00:00
#[ derive(Debug, Deserialize) ]
2023-11-02 22:24:38 +00:00
struct FamilyMetadata {
2023-11-02 21:21:11 +00:00
pub name : String ,
2023-11-02 22:00:55 +00:00
pub author : String ,
2023-11-02 22:24:38 +00:00
pub themes : Vec < ThemeMetadata > ,
2023-11-02 21:21:11 +00:00
}
2023-11-09 17:46:37 +00:00
#[ derive(Debug, Clone, Copy, Deserialize) ]
2023-11-02 21:21:11 +00:00
#[ serde(rename_all = " snake_case " ) ]
2023-11-02 22:57:13 +00:00
pub enum ThemeAppearanceJson {
2023-11-02 21:21:11 +00:00
Light ,
Dark ,
}
2023-11-02 22:24:38 +00:00
impl From < ThemeAppearanceJson > for Appearance {
fn from ( value : ThemeAppearanceJson ) -> Self {
match value {
ThemeAppearanceJson ::Light = > Self ::Light ,
ThemeAppearanceJson ::Dark = > Self ::Dark ,
}
}
2023-11-02 21:21:11 +00:00
}
2023-11-02 22:24:38 +00:00
#[ derive(Debug, Deserialize) ]
2023-11-02 22:57:13 +00:00
pub struct ThemeMetadata {
2023-11-02 21:21:11 +00:00
pub name : String ,
2023-11-02 22:24:38 +00:00
pub file_name : String ,
pub appearance : ThemeAppearanceJson ,
2023-11-02 21:21:11 +00:00
}
fn main ( ) -> Result < ( ) > {
2023-11-06 19:54:21 +00:00
const SOURCE_PATH : & str = " assets/themes/src/vscode " ;
const OUT_PATH : & str = " crates/theme2/src/themes " ;
2023-11-02 21:21:11 +00:00
SimpleLogger ::init ( LevelFilter ::Info , Default ::default ( ) ) . expect ( " could not initialize logger " ) ;
2023-11-06 19:54:21 +00:00
println! ( " Loading themes source... " ) ;
let vscode_themes_path = PathBuf ::from_str ( SOURCE_PATH ) ? ;
if ! vscode_themes_path . exists ( ) {
return Err ( anyhow! ( format! (
" Couldn't find {}, make sure it exists " ,
SOURCE_PATH
) ) ) ;
}
2023-11-02 21:21:11 +00:00
2023-11-02 22:00:55 +00:00
let mut theme_families = Vec ::new ( ) ;
2023-11-02 21:21:11 +00:00
2023-11-02 22:00:55 +00:00
for theme_family_dir in fs ::read_dir ( & vscode_themes_path ) ? {
let theme_family_dir = theme_family_dir ? ;
2023-11-02 21:21:11 +00:00
2023-11-06 19:54:21 +00:00
if ! theme_family_dir . file_type ( ) ? . is_dir ( ) {
2023-11-06 16:03:17 +00:00
continue ;
}
2023-11-02 22:00:55 +00:00
let theme_family_slug = theme_family_dir
. path ( )
. file_stem ( )
. ok_or ( anyhow! ( " no file stem " ) )
. map ( | stem | stem . to_string_lossy ( ) . to_string ( ) ) ? ;
2023-11-02 21:21:11 +00:00
2023-11-02 22:00:55 +00:00
let family_metadata_file = File ::open ( theme_family_dir . path ( ) . join ( " family.json " ) )
2023-11-06 19:54:21 +00:00
. context ( format! (
" no `family.json` found for '{}' " ,
theme_family_slug
) ) ? ;
let license_file_path = theme_family_dir . path ( ) . join ( " LICENSE " ) ;
if ! license_file_path . exists ( ) {
println! ( " Skipping theme family ' {} ' because it does not have a LICENSE file. This theme will only be imported once a LICENSE file is provided. " , theme_family_slug ) ;
continue ;
}
2023-11-02 21:21:11 +00:00
2023-11-02 22:24:38 +00:00
let family_metadata : FamilyMetadata = serde_json ::from_reader ( family_metadata_file )
. context ( format! (
" failed to parse `family.json` for '{theme_family_slug}' "
) ) ? ;
2023-11-02 21:21:11 +00:00
2023-11-02 22:00:55 +00:00
let mut themes = Vec ::new ( ) ;
2023-11-02 21:21:11 +00:00
2023-11-02 22:24:38 +00:00
for theme_metadata in family_metadata . themes {
let theme_file_path = theme_family_dir . path ( ) . join ( & theme_metadata . file_name ) ;
2023-11-02 21:21:11 +00:00
2023-11-06 17:43:10 +00:00
let theme_file = match File ::open ( & theme_file_path ) {
Ok ( file ) = > file ,
Err ( _ ) = > {
println! ( " Failed to open file at path: {:?} " , theme_file_path ) ;
continue ;
}
} ;
2023-11-02 21:21:11 +00:00
2023-11-10 00:22:15 +00:00
let theme_without_comments = StripComments ::new ( theme_file ) ;
let vscode_theme : VsCodeTheme = serde_json ::from_reader ( theme_without_comments )
2023-11-02 22:00:55 +00:00
. context ( format! ( " failed to parse theme {theme_file_path:?} " ) ) ? ;
2023-11-02 21:21:11 +00:00
2023-11-02 22:24:38 +00:00
let converter = VsCodeThemeConverter ::new ( vscode_theme , theme_metadata ) ;
let theme = converter . convert ( ) ? ;
2023-11-02 22:00:55 +00:00
themes . push ( theme ) ;
}
2023-11-07 16:40:07 +00:00
let theme_family = UserThemeFamily {
2023-11-02 22:00:55 +00:00
name : family_metadata . name . into ( ) ,
author : family_metadata . author . into ( ) ,
2023-11-02 22:24:38 +00:00
themes ,
2023-11-02 22:00:55 +00:00
} ;
theme_families . push ( theme_family ) ;
}
2023-11-02 21:21:11 +00:00
2023-11-06 20:24:36 +00:00
let themes_output_path = PathBuf ::from_str ( OUT_PATH ) ? ;
if ! themes_output_path . exists ( ) {
println! ( " Creating directory: {:?} " , themes_output_path ) ;
fs ::create_dir_all ( & themes_output_path ) ? ;
}
let mut mod_rs_file = File ::create ( themes_output_path . join ( format! ( " mod.rs " ) ) ) ? ;
2023-11-02 23:14:51 +00:00
let mut theme_modules = Vec ::new ( ) ;
2023-11-02 22:57:13 +00:00
for theme_family in theme_families {
2023-11-02 23:14:51 +00:00
let theme_family_slug = theme_family . name . to_string ( ) . to_case ( Case ::Snake ) ;
let mut output_file =
File ::create ( themes_output_path . join ( format! ( " {theme_family_slug} .rs " ) ) ) ? ;
2023-11-06 17:43:10 +00:00
println! (
" Creating file: {:?} " ,
themes_output_path . join ( format! ( " {theme_family_slug} .rs " ) )
) ;
2023-11-02 23:14:51 +00:00
let theme_module = format! (
r #"
2023-11-07 17:45:09 +00:00
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
2023-11-03 14:58:06 +00:00
use gpui ::rgba ;
2023-11-02 23:14:51 +00:00
2023-11-09 19:41:26 +00:00
#[ allow(unused) ]
2023-11-02 23:14:51 +00:00
use crate ::{ {
2023-11-09 17:59:20 +00:00
Appearance , StatusColorsRefinement , ThemeColorsRefinement , UserHighlightStyle , UserSyntaxTheme ,
2023-11-09 19:41:26 +00:00
UserTheme , UserThemeFamily , UserThemeStylesRefinement , UserFontWeight , UserFontStyle
2023-11-02 23:14:51 +00:00
} } ;
2023-11-07 16:40:07 +00:00
pub fn { theme_family_slug } ( ) -> UserThemeFamily { {
2023-11-02 23:14:51 +00:00
{ theme_family_definition }
} }
" #,
2023-11-07 16:40:07 +00:00
theme_family_definition = format! ( " {:#?} " , UserThemeFamilyPrinter ::new ( theme_family ) )
2023-11-02 23:14:51 +00:00
) ;
output_file . write_all ( theme_module . as_bytes ( ) ) ? ;
theme_modules . push ( theme_family_slug ) ;
2023-11-02 22:57:13 +00:00
}
2023-11-06 19:54:21 +00:00
let themes_vector_contents = format! (
r #"
2023-11-07 16:40:07 +00:00
use crate ::UserThemeFamily ;
2023-11-06 19:54:21 +00:00
2023-11-07 17:45:09 +00:00
pub ( crate ) fn all_user_themes ( ) -> Vec < UserThemeFamily > { {
2023-11-06 19:54:21 +00:00
vec! [ { all_themes } ]
} }
" #,
all_themes = theme_modules
. iter ( )
. map ( | module | format! ( " {} () " , module ) )
. collect ::< Vec < _ > > ( )
. join ( " , " )
2023-11-06 17:43:10 +00:00
) ;
2023-11-02 23:14:51 +00:00
let mod_rs_contents = format! (
r #"
2023-11-07 17:45:09 +00:00
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
2023-11-02 23:14:51 +00:00
{ mod_statements }
{ use_statements }
2023-11-06 19:54:21 +00:00
{ themes_vector_contents }
2023-11-02 23:14:51 +00:00
" #,
mod_statements = theme_modules
. iter ( )
. map ( | module | format! ( " mod {module} ; " ) )
. collect ::< Vec < _ > > ( )
. join ( " \n " ) ,
use_statements = theme_modules
. iter ( )
. map ( | module | format! ( " pub use {module} ::*; " ) )
. collect ::< Vec < _ > > ( )
2023-11-06 19:54:21 +00:00
. join ( " \n " ) ,
themes_vector_contents = themes_vector_contents
2023-11-02 23:14:51 +00:00
) ;
mod_rs_file . write_all ( mod_rs_contents . as_bytes ( ) ) ? ;
2023-11-07 17:45:09 +00:00
println! ( " Formatting themes... " ) ;
let format_result = format_themes_crate ( )
// We need to format a second time to catch all of the formatting issues.
. and_then ( | _ | format_themes_crate ( ) ) ;
if let Err ( err ) = format_result {
eprintln! ( " Failed to format themes: {} " , err ) ;
}
println! ( " Done! " ) ;
2023-11-02 21:21:11 +00:00
Ok ( ( ) )
}
2023-11-07 17:45:09 +00:00
fn format_themes_crate ( ) -> std ::io ::Result < std ::process ::Output > {
Command ::new ( " cargo " )
. args ( [ " fmt " , " --package " , " theme2 " ] )
. output ( )
}