From 0bfa4615988c0d1bcaa38453375760c60076c5ea Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Fri, 31 Jul 2026 02:21:01 -0300 Subject: themes: one file each, generated from helix and zed, picked by stepping a list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each theme is a .zig file of pure data and nothing enumerates them by hand — fold() walks the container's declarations, so a theme is a file and that is the whole registration. Field by field rather than a wholesale coercion, which makes a missing field a compile error that names it. tools/gen_themes.zig reads helix .toml and zed .json out of vendor/themes and emits one .zig each; build.zig reads that directory, so adding a theme is dropping a file in. Output goes to the build cache rather than the tree, so zig owns the freshness check and a deleted source cannot leave a stale theme behind. Vendored, not read from the genizah: the build stays offline. A source it cannot map is a hard error naming the file and the key, never a silently black-on-black theme. ThemeSel is the clever half. Traits gained an `executes` column, so n/N over that buffer hands the whole line to Exec instead of the leading word to Look — both arms the ordinary builtin, so a stepped row does exactly what the matching mouse button on it would. Stepping the list previews each theme live. The trait is a pane property, not an output-pane branch, so any pane whose lines are commands can opt in. Goldens: leader gains SPC t t; theme's fourth NextColor no longer wraps to helix because the ring is fifteen long. New: themesel. --- src/builtins.zig | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'src/builtins.zig') diff --git a/src/builtins.zig b/src/builtins.zig index 7e20a95b..620c6b1c 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -182,6 +182,38 @@ pub const NextColor = struct { } }; +/// The theme BY NAME — `Theme acme`. The ring grew past the point where +/// cycling to the one you want is reasonable, so this is the way to ask for +/// one, and NextColor stays as the way to browse. Inert without an argument +/// (there is no theme called nothing), which is also why it has no leader path: +/// a key path names a builtin and can never carry the name of a theme. +/// +/// A LINEAR SCAN over a couple of dozen names, on a keystroke: the alternative +/// is a comptime name->index map, which is a second copy of the ring to build +/// for a lookup nobody will ever measure. +pub const Theme = struct { + pub fn run(c: Ctx) void { + const want = std.mem.trim(u8, c.arg orelse return, " \t\r\n"); + for (pardes.themes, 0..) |t, i| { + if (std.mem.eql(u8, t.name, want)) { + c.p.theme_idx = i; + return; + } + } + } +}; + +/// ...and the list of what Theme takes, as a buffer you walk. Its rows are +/// `Theme ` COMMANDS rather than locations, so n/N execute them instead +/// of looking them (output_pane.Traits.executes) and stepping the list wears +/// each theme in turn — the picker is the list, and there is nothing to +/// confirm because arriving already applied it. +pub const ThemeSel = struct { + pub fn run(c: Ctx) void { + output_pane.openThemes(c.p, c.id); + } +}; + pub const Crt = struct { pub fn run(c: Ctx) void { c.p.crt_on = !c.p.crt_on; -- cgit v1.3