summaryrefslogtreecommitdiff
path: root/build.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-31 02:21:01 -0300
committerGabriel Schneider <[email protected]>2026-08-01 15:02:08 -0300
commit0bfa4615988c0d1bcaa38453375760c60076c5ea (patch)
treefab4f972c10b07739aabc9d772c43e102bce2c07 /build.zig
parentbe404e0d4f91548af6ed600b2fd2027ad8a041c7 (diff)
downloadpardes-0bfa4615988c0d1bcaa38453375760c60076c5ea.tar.gz
pardes-0bfa4615988c0d1bcaa38453375760c60076c5ea.zip
themes: one file each, generated from helix and zed, picked by stepping a list
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.
Diffstat (limited to 'build.zig')
-rw-r--r--build.zig59
1 files changed, 58 insertions, 1 deletions
diff --git a/build.zig b/build.zig
index c11ccccd..04516cc8 100644
--- a/build.zig
+++ b/build.zig
@@ -93,7 +93,8 @@ pub fn build(b: *std.Build) void {
else
null;
- // build-time IO, only used to slurp the grammars' highlights.scm queries
+ // build-time IO: slurps the grammars' highlights.scm queries, and reads
+ // vendor/themes to find the theme sources
var threaded: std.Io.Threaded = .init(b.allocator, .{});
defer threaded.deinit();
const io = threaded.io();
@@ -115,6 +116,40 @@ pub fn build(b: *std.Build) void {
.link_libc = true,
});
+ // THEMES. tools/gen_themes.zig turns each vendored helix .toml / zed .json
+ // into one .zig file per theme plus a list.zig that imports them all;
+ // src/pardes.zig folds that list into the ring beside the three it ships.
+ //
+ // Straight into the build cache and handed over as a module, rather than
+ // written back into src/: the output then has no freshness problem to own
+ // (zig re-runs the generator only when a vendored file changes, and a
+ // cached run leaves the compile's inputs byte-identical, so `zig build`
+ // with nothing touched still does nothing), there is nothing to gitignore
+ // and no stale .zig can survive a deleted source.
+ //
+ // The INPUT list is read from the directory here rather than written out,
+ // so adding a theme is dropping a file in — and each file goes in as a
+ // content-hashed argument, which is what makes that new file re-run the
+ // step and nothing else.
+ const theme_gen = b.addExecutable(.{
+ .name = "pardes-gen-themes",
+ .root_module = b.createModule(.{
+ .target = b.graph.host,
+ .optimize = .ReleaseSafe,
+ .root_source_file = b.path("tools/gen_themes.zig"),
+ }),
+ });
+ const run_theme_gen = b.addRunArtifact(theme_gen);
+ const themes_out = run_theme_gen.addOutputDirectoryArg("themes");
+ for (vendoredThemes(b, io)) |name| run_theme_gen.addFileArg(b.path(b.fmt("vendor/themes/{s}", .{name})));
+ const themes_mod = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ .root_source_file = themes_out.path(b, "list.zig"),
+ });
+ root_mod.addImport("generated_themes", themes_mod);
+ hx_core_mod.addImport("generated_themes", themes_mod);
+
// static archives the emcc link needs beyond the zig object (grammars, SDL)
var wasm_libs: std.ArrayList(std.Build.LazyPath) = .empty;
@@ -713,6 +748,28 @@ fn compileGlsl(b: *std.Build, src: []const u8, stage: []const u8, out: []const u
return cmd.addOutputFileArg(out);
}
+/// The theme sources, sorted. SORTED because the run step is cached by its
+/// argv: readdir order is whatever the filesystem feels like, and an argv that
+/// shuffles is a cache miss and a rebuild every time. Anything that is not a
+/// .toml or a .json is skipped, which is what lets the upstream LICENSE files
+/// sit beside the themes they cover.
+fn vendoredThemes(b: *std.Build, io: std.Io) []const []const u8 {
+ var dir = b.build_root.handle.openDir(io, "vendor/themes", .{ .iterate = true }) catch @panic("open vendor/themes");
+ defer dir.close(io);
+ var names: std.ArrayList([]const u8) = .empty;
+ var it = dir.iterate();
+ while (it.next(io) catch @panic("read vendor/themes")) |e| {
+ if (!std.mem.endsWith(u8, e.name, ".toml") and !std.mem.endsWith(u8, e.name, ".json")) continue;
+ names.append(b.allocator, b.dupe(e.name)) catch @panic("OOM");
+ }
+ std.mem.sort([]const u8, names.items, {}, struct {
+ fn lt(_: void, a: []const u8, c: []const u8) bool {
+ return std.mem.order(u8, a, c) == .lt;
+ }
+ }.lt);
+ return names.items;
+}
+
fn detectEmscriptenSysroot(b: *std.Build) ?[]const u8 {
if (b.option([]const u8, "emscripten-sysroot", "path to the Emscripten sysroot")) |path| return path;
if (b.sysroot) |path| return path;