summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/builtins.zig14
-rw-r--r--src/pardes.zig19
2 files changed, 29 insertions, 4 deletions
diff --git a/src/builtins.zig b/src/builtins.zig
index 620c6b1c..6593be7d 100644
--- a/src/builtins.zig
+++ b/src/builtins.zig
@@ -176,6 +176,13 @@ pub const Colors = struct {
}
};
+/// One step along the ring. With 228 themes in it this is no longer a way to
+/// REACH a theme — ThemeSel is — but it is still the way to browse one, and the
+/// browse got better rather than worse: the generated half is sorted by name, so
+/// the neighbours of wherever you are are that theme's own variants (light,
+/// hard, soft, the whole gruvbox family in a row). Kept as the topbar word and
+/// SPC t n it has always been; a ring you can walk off the end of in three
+/// clicks was never what made it useful.
pub const NextColor = struct {
pub fn run(c: Ctx) void {
c.p.theme_idx = (c.p.theme_idx + 1) % pardes.themes.len;
@@ -188,9 +195,10 @@ pub const NextColor = struct {
/// (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.
+/// A LINEAR SCAN over 228 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. 228 short string compares is microseconds, and it
+/// happens once per theme change, not once per frame.
pub const Theme = struct {
pub fn run(c: Ctx) void {
const want = std.mem.trim(u8, c.arg orelse return, " \t\r\n");
diff --git a/src/pardes.zig b/src/pardes.zig
index ced4c2f9..fbf48f05 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -326,7 +326,7 @@ const curated = struct {
/// only ever a signature to whoever walks it, never a value in its own way.
fn fold(comptime C: type) []const Theme {
comptime {
- @setEvalBranchQuota(20000); // themes x fields, and each field a @field lookup
+ @setEvalBranchQuota(400000); // 228 themes x 15 fields, each field a @field lookup, and the array re-concatenated per theme
var out: []const Theme = &.{};
for (@typeInfo(C).@"struct".decls) |d| {
var t: Theme = undefined;
@@ -341,8 +341,25 @@ fn fold(comptime C: type) []const Theme {
/// helix and zed sources in vendor/themes (build.zig runs it and hands the
/// result over as a module). Adding one is dropping a file in there — there is
/// no list here to append to, which is the whole point of folding the files.
+/// 228 of them: everything helix and zed ship, because "which of these is worth
+/// having" is the user's call and not the build's. That length is why ThemeSel
+/// exists — NextColor is a browse, not a way to arrive anywhere in particular.
pub const themes = fold(curated) ++ fold(@import("generated_themes"));
+// Two themes answering to one name is a bug: `Theme <name>` resolves by name
+// and would silently pick whichever came first, and ThemeSel would list the
+// loser as a row that does nothing. The generated half cannot collide with
+// ITSELF — one file per theme, all imported into one struct, so zig's own
+// redeclaration error already catches that — which leaves exactly the cross
+// pair to check here, three times 225 rather than 228 squared. This is the
+// check that makes helix's `acme.toml` vendored as `acme_helix.toml`.
+comptime {
+ @setEvalBranchQuota(20000);
+ const ours = fold(curated).len;
+ for (themes[0..ours]) |c| for (themes[ours..]) |g| if (std.mem.eql(u8, c.name, g.name))
+ @compileError("theme name \"" ++ c.name ++ "\" is both ours and generated; rename the vendored source");
+}
+
// ---- the boundary types ----
pub const Color = union(enum) { default, index: u8, rgb: [3]u8 };