From a0d45cbb48ffe97579a5b4797e48508a8eac4ca4 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sat, 13 Jun 2026 17:27:30 -0500 Subject: Add theme studio Rust and Zig samples --- scripts/theme-studio/README.md | 2 +- scripts/theme-studio/generate.py | 2 +- scripts/theme-studio/samples.py | 64 +++++++++++++++++++++++++++++++++- scripts/theme-studio/test_generate.py | 29 +++++++++++++++ scripts/theme-studio/theme-studio.html | 2 +- 5 files changed, 95 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/theme-studio/README.md b/scripts/theme-studio/README.md index a1178d1a..a357a61f 100644 --- a/scripts/theme-studio/README.md +++ b/scripts/theme-studio/README.md @@ -64,7 +64,7 @@ gates. - `browser-gates.js` — the browser hash-gate test harness, also inlined. - `app_inventory.py`, `face_specs.py`, `default_faces.py` — generator helpers for package inventory, face-spec defaults, and captured Emacs defaults. -- `samples.py` — the six language code samples and the default syntax +- `samples.py` — the language code samples and the default syntax category→color map (`COLS`). `generate.py` reads the part before the `cols=` marker. - `package-inventory.json` — generated map of every installed package to the diff --git a/scripts/theme-studio/generate.py b/scripts/theme-studio/generate.py index 0b2d4039..751dff71 100644 --- a/scripts/theme-studio/generate.py +++ b/scripts/theme-studio/generate.py @@ -46,7 +46,7 @@ BROWSER_GATES_BODY=strip_exports(open(os.path.join(HERE,'browser-gates.js')).rea ns={} src=open(os.path.join(HERE,'samples.py')).read() exec(src[:src.index('cols=')], ns) -SAMPLES={"Elisp":ns['ELS'],"Go":ns['GOS'],"Python":ns['PYS'],"TypeScript":ns['TSS'],"Java":ns['JAS'],"C":ns['CS'],"C++":ns['CPS'],"Shell":ns['SHS']} +SAMPLES={"Elisp":ns['ELS'],"Go":ns['GOS'],"Python":ns['PYS'],"TypeScript":ns['TSS'],"Java":ns['JAS'],"C":ns['CS'],"C++":ns['CPS'],"Rust":ns['RUSTS'],"Zig":ns['ZIGS'],"Shell":ns['SHS']} COLS=ns['COLS'] DEFAULT_FACES_PATH=os.path.join(HERE,'emacs-default-faces.json') DEFAULTS=DefaultFaces.from_path(DEFAULT_FACES_PATH) diff --git a/scripts/theme-studio/samples.py b/scripts/theme-studio/samples.py index e487cc5e..a854d672 100644 --- a/scripts/theme-studio/samples.py +++ b/scripts/theme-studio/samples.py @@ -226,7 +226,69 @@ SHS=[ [('fnc','main'),('p',' '),('str','"$@"')], ] -cols="".join(f'

{n}

{render(s)}
' for n,s in [("Elisp",ELS),("Go",GOS),("Python",PYS),("TypeScript",TSS),("Java",JAS),("C",CS),("C++",CPS),("Shell",SHS)]) +RUSTS=[ + [('cmd','//'),('cm',' theme.rs')], + [('dec','#![allow(dead_code)]')], + [('kw','use'),('p',' '),('var','std'),('op','::'),('var','fmt'),('punc',';')], + [], + [('dec','#[derive'),('punc','('),('dec','Debug'),('punc',','),('p',' '),('dec','Clone'),('punc',')]')], + [('kw','pub'),('p',' '),('kw','trait'),('p',' '),('ty','Theme'),('op','<'),('var',"'a"),('op','>'),('p',' '),('punc','{')], + [('p',' '),('kw','const'),('p',' '),('con','NAME'),('op',':'),('p',' '),('op','&'),('var',"'static"),('p',' '),('ty','str'),('punc',';')], + [('p',' '),('kw','fn'),('p',' '),('fnd','resolve'),('punc','('),('op','&'),('var',"'a"),('p',' '),('var','self'),('punc',','),('p',' '),('var','key'),('op',':'),('p',' '),('op','&'),('var',"'a"),('p',' '),('ty','str'),('punc',')'),('p',' '),('op','->'),('p',' '),('ty','Option'),('op','<'),('op','&'),('var',"'a"),('p',' '),('ty','str'),('op','>'),('punc',';')], + [('punc','}')], + [], + [('kw','pub'),('p',' '),('kw','struct'),('p',' '),('ty','Palette'),('op','<'),('var',"'a"),('op','>'),('p',' '),('punc','{')], + [('p',' '),('kw','pub'),('p',' '),('prop','name'),('op',':'),('p',' '),('op','&'),('var',"'a"),('p',' '),('ty','str'),('punc',',')], + [('p',' '),('kw','pub'),('p',' '),('prop','colors'),('op',':'),('p',' '),('ty','Vec'),('op','<'),('punc','('),('op','&'),('var',"'a"),('p',' '),('ty','str'),('punc',','),('p',' '),('op','&'),('var',"'a"),('p',' '),('ty','str'),('punc',')'),('op','>'),('punc',',')], + [('punc','}')], + [], + [('kw','impl'),('op','<'),('var',"'a"),('op','>'),('p',' '),('ty','Theme'),('op','<'),('var',"'a"),('op','>'),('p',' '),('kw','for'),('p',' '),('ty','Palette'),('op','<'),('var',"'a"),('op','>'),('p',' '),('punc','{')], + [('p',' '),('kw','const'),('p',' '),('con','NAME'),('op',':'),('p',' '),('op','&'),('var',"'static"),('p',' '),('ty','str'),('p',' '),('op','='),('p',' '),('str','"dupre"'),('punc',';')], + [('p',' '),('kw','fn'),('p',' '),('fnd','resolve'),('punc','('),('op','&'),('var',"'a"),('p',' '),('var','self'),('punc',','),('p',' '),('var','key'),('op',':'),('p',' '),('op','&'),('var',"'a"),('p',' '),('ty','str'),('punc',')'),('p',' '),('op','->'),('p',' '),('ty','Option'),('op','<'),('op','&'),('var',"'a"),('p',' '),('ty','str'),('op','>'),('p',' '),('punc','{')], + [('p',' '),('kw','if'),('p',' '),('var','key'),('op','.'),('fnc','is_empty'),('punc','()'),('p',' '),('punc','{'),('p',' '),('kw','return'),('p',' '),('con','None'),('punc',';'),('p',' '),('punc','}')], + [('p',' '),('var','self'),('op','.'),('prop','colors'),('op','.'),('fnc','iter'),('punc','()'),('op','.'),('fnc','find'),('punc','('),('op','|'),('punc','('),('var','k'),('punc',','),('p',' '),('var','_'),('punc',')'),('op','|'),('p',' '),('op','*'),('var','k'),('p',' '),('op','=='),('p',' '),('var','key'),('punc',')'),('op','.'),('fnc','map'),('punc','('),('op','|'),('punc','('),('var','_'),('punc',','),('p',' '),('var','v'),('punc',')'),('op','|'),('p',' '),('op','*'),('var','v'),('punc',')')], + [('p',' '),('punc','}')], + [('punc','}')], + [], + [('kw','fn'),('p',' '),('fnd','main'),('punc','()'),('p',' '),('punc','{')], + [('p',' '),('kw','let'),('p',' '),('var','palette'),('p',' '),('op','='),('p',' '),('ty','Palette'),('p',' '),('punc','{'),('p',' '),('prop','name'),('op',':'),('p',' '),('str','"dupre"'),('punc',','),('p',' '),('prop','colors'),('op',':'),('p',' '),('bi','vec!'),('punc','['),('punc','('),('str','"bg"'),('punc',','),('p',' '),('str','"#0d0b0a"'),('punc',')'),('punc',']'),('p',' '),('punc','}'),('punc',';')], + [('p',' '),('bi','println!'),('punc','('),('str','"{:?}"'),('punc',','),('p',' '),('var','palette'),('op','.'),('fnc','resolve'),('punc','('),('str','"bg"'),('punc','))'),('punc',';')], + [('punc','}')], +] + +ZIGS=[ + [('cmd','//'),('cm',' theme.zig')], + [('kw','const'),('p',' '),('var','std'),('p',' '),('op','='),('p',' '),('bi','@import'),('punc','('),('str','"std"'),('punc',')'),('punc',';')], + [('kw','const'),('p',' '),('ty','Allocator'),('p',' '),('op','='),('p',' '),('var','std'),('op','.'),('var','mem'),('op','.'),('ty','Allocator'),('punc',';')], + [], + [('kw','pub'),('p',' '),('kw','const'),('p',' '),('ty','Theme'),('p',' '),('op','='),('p',' '),('kw','struct'),('p',' '),('punc','{')], + [('p',' '),('prop','name'),('op',':'),('p',' '),('punc','['),('punc',']'),('kw','const'),('p',' '),('ty','u8'),('punc',',')], + [('p',' '),('prop','colors'),('op',':'),('p',' '),('punc','['),('punc',']'),('kw','const'),('p',' '),('ty','Color'),('punc',',')], + [], + [('p',' '),('kw','pub'),('p',' '),('kw','fn'),('p',' '),('fnd','init'),('punc','('),('var','alloc'),('op',':'),('p',' '),('op','*'),('ty','Allocator'),('punc',')'),('p',' '),('op','!'),('bi','@This'),('punc','()'),('p',' '),('punc','{')], + [('p',' '),('kw','const'),('p',' '),('var','colors'),('p',' '),('op','='),('p',' '),('kw','try'),('p',' '),('var','alloc'),('op','.'),('fnc','alloc'),('punc','('),('ty','Color'),('punc',','),('p',' '),('num','2'),('punc',')'),('punc',';')], + [('p',' '),('var','colors'),('punc','['),('num','0'),('punc',']'),('p',' '),('op','='),('p',' '),('ty','Color'),('punc','{'),('p',' '),('prop','.name'),('p',' '),('op','='),('p',' '),('str','"bg"'),('punc',','),('p',' '),('prop','.hex'),('p',' '),('op','='),('p',' '),('str','"#0d0b0a"'),('p',' '),('punc','}'),('punc',';')], + [('p',' '),('kw','return'),('p',' '),('bi','@This'),('punc','()'),('punc','{'),('p',' '),('prop','.name'),('p',' '),('op','='),('p',' '),('str','"dupre"'),('punc',','),('p',' '),('prop','.colors'),('p',' '),('op','='),('p',' '),('var','colors'),('p',' '),('punc','}'),('punc',';')], + [('p',' '),('punc','}')], + [('punc','}'),('punc',';')], + [], + [('kw','const'),('p',' '),('ty','Color'),('p',' '),('op','='),('p',' '),('kw','struct'),('p',' '),('punc','{'),('p',' '),('prop','name'),('op',':'),('p',' '),('punc','['),('punc',']'),('kw','const'),('p',' '),('ty','u8'),('punc',','),('p',' '),('prop','hex'),('op',':'),('p',' '),('punc','['),('punc',']'),('kw','const'),('p',' '),('ty','u8'),('p',' '),('punc','}'),('punc',';')], + [], + [('kw','fn'),('p',' '),('fnd','resolve'),('punc','('),('var','theme'),('op',':'),('p',' '),('ty','Theme'),('punc',','),('p',' '),('kw','comptime'),('p',' '),('var','key'),('op',':'),('p',' '),('punc','['),('punc',':'),('num','0'),('punc',']'),('kw','const'),('p',' '),('ty','u8'),('punc',')'),('p',' '),('op','!'),('punc','['),('punc',']'),('kw','const'),('p',' '),('ty','u8'),('p',' '),('punc','{')], + [('p',' '),('kw','inline'),('p',' '),('kw','for'),('p',' '),('punc','('),('var','theme'),('op','.'),('prop','colors'),('punc',')'),('p',' '),('op','|'),('var','color'),('op','|'),('p',' '),('punc','{')], + [('p',' '),('kw','if'),('p',' '),('punc','('),('var','std'),('op','.'),('var','mem'),('op','.'),('fnc','eql'),('punc','('),('ty','u8'),('punc',','),('p',' '),('var','color'),('op','.'),('prop','name'),('punc',','),('p',' '),('var','key'),('punc',')'),('punc',')'),('p',' '),('kw','return'),('p',' '),('var','color'),('op','.'),('prop','hex'),('punc',';')], + [('p',' '),('punc','}')], + [('p',' '),('kw','return'),('p',' '),('con','error.MissingColor'),('punc',';')], + [('punc','}')], + [], + [('kw','test'),('p',' '),('str','"resolve bg"'),('p',' '),('punc','{')], + [('p',' '),('kw','var'),('p',' '),('var','arena'),('p',' '),('op','='),('p',' '),('var','std'),('op','.'),('var','heap'),('op','.'),('ty','ArenaAllocator'),('op','.'),('fnc','init'),('punc','('),('var','std'),('op','.'),('var','testing'),('op','.'),('prop','allocator'),('punc',')'),('punc',';')], + [('p',' '),('kw','defer'),('p',' '),('var','arena'),('op','.'),('fnc','deinit'),('punc','()'),('punc',';')], + [('p',' '),('kw','try'),('p',' '),('var','std'),('op','.'),('var','testing'),('op','.'),('fnc','expectEqualStrings'),('punc','('),('str','"#0d0b0a"'),('punc',','),('p',' '),('kw','try'),('p',' '),('fnc','resolve'),('punc','('),('kw','try'),('p',' '),('ty','Theme'),('op','.'),('fnc','init'),('punc','('),('op','&'),('var','arena'),('op','.'),('prop','allocator'),('punc',')'),('punc',','),('p',' '),('str','"bg"'),('punc','))'),('punc',';')], + [('punc','}')], +] + +cols="".join(f'

{n}

{render(s)}
' for n,s in [("Elisp",ELS),("Go",GOS),("Python",PYS),("TypeScript",TSS),("Java",JAS),("C",CS),("C++",CPS),("Rust",RUSTS),("Zig",ZIGS),("Shell",SHS)]) legend_rows=[ ("keyword (bold)","kw","class def if return import"),("builtin","bi","len range print"), ("function — definition (bold)","fnd","resolve cache-get push"),("function — call","fnc","get append fmt.Errorf"), diff --git a/scripts/theme-studio/test_generate.py b/scripts/theme-studio/test_generate.py index e845085e..a4a5f2bf 100644 --- a/scripts/theme-studio/test_generate.py +++ b/scripts/theme-studio/test_generate.py @@ -123,6 +123,35 @@ class AssembledPage(unittest.TestCase): self.assertEqual(generate.HTML.count(""), 1) +class LanguageSamples(unittest.TestCase): + def _tokens(self, lang): + return [tok for line in generate.SAMPLES[lang] for tok in line] + + def test_rust_and_zig_are_available_in_the_language_selector(self): + self.assertIn("Rust", generate.SAMPLES) + self.assertIn("Zig", generate.SAMPLES) + + def test_rust_sample_exercises_language_specific_categories(self): + tokens = self._tokens("Rust") + cats = {k for k, _ in tokens} + text = "".join(t for _, t in tokens) + for cat in ("dec", "ty", "bi", "kw", "op", "var"): + self.assertIn(cat, cats) + self.assertIn("'a", text) + self.assertIn("trait", text) + self.assertIn("vec!", text) + + def test_zig_sample_exercises_language_specific_categories(self): + tokens = self._tokens("Zig") + cats = {k for k, _ in tokens} + text = "".join(t for _, t in tokens) + for cat in ("bi", "kw", "ty", "con", "op", "prop"): + self.assertIn(cat, cats) + self.assertIn("comptime", text) + self.assertIn("@import", text) + self.assertIn("error.MissingColor", text) + + class FacesHelper(unittest.TestCase): def test_strips_prefix_and_derives_label_and_merges_seed(self): # Normal: the prefix comes off the label, and the per-face seed is attached. diff --git a/scripts/theme-studio/theme-studio.html b/scripts/theme-studio/theme-studio.html index 90430a5b..5a9653ea 100644 --- a/scripts/theme-studio/theme-studio.html +++ b/scripts/theme-studio/theme-studio.html @@ -184,7 +184,7 @@