diff options
| author | Gabriel Schneider <[email protected]> | 2026-07-31 02:21:01 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-01 15:02:08 -0300 |
| commit | 0bfa4615988c0d1bcaa38453375760c60076c5ea (patch) | |
| tree | fab4f972c10b07739aabc9d772c43e102bce2c07 /build.zig | |
| parent | be404e0d4f91548af6ed600b2fd2027ad8a041c7 (diff) | |
| download | pardes-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.zig | 59 |
1 files changed, 58 insertions, 1 deletions
@@ -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; |
