diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-09 10:41:33 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-10 09:17:07 -0300 |
| commit | 9085cb5bfdd0b78ff3a62c0c71fc231dd7b5052a (patch) | |
| tree | 91fce278abc85e4f7491de8b5f02184c8a2d5a56 /tools/gen_themes.zig | |
| parent | 2b1fac6fecbf7d7cc08f9dad24687b05e9968c9a (diff) | |
| download | pardes-9085cb5bfdd0b78ff3a62c0c71fc231dd7b5052a.tar.gz pardes-9085cb5bfdd0b78ff3a62c0c71fc231dd7b5052a.zip | |
replace ArrayLists with bounded storage
Diffstat (limited to 'tools/gen_themes.zig')
| -rw-r--r-- | tools/gen_themes.zig | 93 |
1 files changed, 68 insertions, 25 deletions
diff --git a/tools/gen_themes.zig b/tools/gen_themes.zig index 23bcad75..3add2040 100644 --- a/tools/gen_themes.zig +++ b/tools/gen_themes.zig @@ -78,32 +78,54 @@ pub fn main(init: std.process.Init) !void { const args = try init.minimal.args.toSlice(arena); if (args.len < 3) std.process.fatal("usage: gen-themes <out-dir> <theme-source>...", .{}); - var themes: std.ArrayList(Theme) = .empty; + const sources = args[2..]; + const texts = try arena.alloc([]const u8, sources.len); + var helix_count: usize = 0; + var theme_count: usize = 0; + for (sources, texts) |src, *text| { + text.* = try std.Io.Dir.cwd().readFileAlloc(io, src, arena, .limited(8 << 20)); + if (std.mem.endsWith(u8, src, ".toml")) { + helix_count += 1; + theme_count += 1; + } else if (std.mem.endsWith(u8, src, ".json")) { + theme_count += try zedThemeCount(gpa, src, text.*); + } else { + std.process.fatal("{s}: not a helix .toml or a zed .json", .{src}); + } + } + + const themes = try arena.alloc(Theme, theme_count); + const stems = try arena.alloc([]const u8, helix_count); // TWO passes over the helix half, because `inherits = "other"` names a // SIBLING SOURCE by stem: nothing can be mapped until every file is read, // and the parent may be later in argv (or inherit something itself). var tomls: std.StringHashMapUnmanaged(Toml) = .empty; - var stems: std.ArrayList([]const u8) = .empty; - for (args[2..]) |src| { - const text = try std.Io.Dir.cwd().readFileAlloc(io, src, arena, .limited(8 << 20)); + var theme_index: usize = 0; + var stem_index: usize = 0; + for (sources, texts) |src, text| { if (std.mem.endsWith(u8, src, ".toml")) { const stem = std.fs.path.stem(src); try tomls.put(arena, stem, parseToml(arena, src, text)); - try stems.append(arena, stem); - } else if (std.mem.endsWith(u8, src, ".json")) - try readZed(arena, gpa, src, text, &themes) - else - std.process.fatal("{s}: not a helix .toml or a zed .json", .{src}); + stems[stem_index] = stem; + stem_index += 1; + } else { + theme_index += try readZed(arena, gpa, src, text, themes[theme_index..]); + } + } + std.debug.assert(stem_index == stems.len); + for (stems) |stem| { + themes[theme_index] = try helixTheme(arena, stem, resolve(arena, &tomls, stem, 8)); + theme_index += 1; } - for (stems.items) |stem| try helixTheme(arena, stem, resolve(arena, &tomls, stem, 8), &themes); + std.debug.assert(theme_index == themes.len); // sorted, so the ring's tail is the same on every machine whatever order // the directory happened to be read in - std.mem.sort(Theme, themes.items, {}, byName); + std.mem.sort(Theme, themes, {}, byName); var dir = try std.Io.Dir.cwd().openDir(io, args[1], .{}); defer dir.close(io); var buf: std.Io.Writer.Allocating = .init(arena); - for (themes.items) |t| { + for (themes) |t| { buf.clearRetainingCapacity(); try emit(&buf.writer, t); try write(io, dir, arena, t.name, buf.written()); @@ -113,7 +135,7 @@ pub fn main(init: std.process.Init) !void { // from whoever can. pardes.zig folds its declarations into the ring. buf.clearRetainingCapacity(); try buf.writer.writeAll("// Generated by tools/gen_themes.zig. Do not edit.\n"); - for (themes.items) |t| try buf.writer.print("pub const {s} = @import(\"{s}.zig\");\n", .{ t.name, t.name }); + for (themes) |t| try buf.writer.print("pub const {s} = @import(\"{s}.zig\");\n", .{ t.name, t.name }); try write(io, dir, arena, "list", buf.written()); } @@ -160,13 +182,20 @@ fn rgb(w: *std.Io.Writer, c: Rgb) !void { /// single word (a `Theme <name>` row is executed by splitting on the first /// space) and a legal identifier (it is a decl in list.zig). fn slug(arena: std.mem.Allocator, raw: []const u8) ![]const u8 { - var out: std.ArrayList(u8) = .empty; + const out = try arena.alloc(u8, raw.len); + var len: usize = 0; for (raw) |c| { - if (std.ascii.isAlphanumeric(c)) try out.append(arena, std.ascii.toLower(c)) - // one underscore per run of punctuation, and never a trailing one - else if (out.items.len > 0 and out.items[out.items.len - 1] != '_') try out.append(arena, '_'); + if (std.ascii.isAlphanumeric(c)) { + out[len] = std.ascii.toLower(c); + len += 1; + } else if (len > 0 and out[len - 1] != '_') { + // one underscore per run of punctuation; it is trimmed below when + // the run ends the name + out[len] = '_'; + len += 1; + } } - const s = std.mem.trim(u8, out.items, "_"); + const s = std.mem.trim(u8, out[0..len], "_"); if (s.len == 0 or !std.ascii.isAlphabetic(s[0])) std.process.fatal("theme name {s} does not slug to an identifier", .{raw}); return s; } @@ -284,7 +313,7 @@ fn resolve(arena: std.mem.Allocator, tomls: *const std.StringHashMapUnmanaged(To /// kw/str/comment their scope's fg, else ink (helix: unstyled = ui.text) /// num constant.numeric.fg, else constant.fg, else ink /// lineno ui.linenr.fg, else ink -fn helixTheme(arena: std.mem.Allocator, stem: []const u8, h: Helix, out: *std.ArrayList(Theme)) !void { +fn helixTheme(arena: std.mem.Allocator, stem: []const u8, h: Helix) !Theme { const fg = h.get("ui.text", .fg); const bg = h.get("ui.background", .bg); const ink = fg orelse ansi_default[7]; @@ -324,7 +353,7 @@ fn helixTheme(arena: std.mem.Allocator, stem: []const u8, h: Helix, out: *std.Ar // one line up, which is the reverse-video case again: those two would paint // the highlight in one colour on itself. const named_ink = h.get("ui.selection", .fg); - try out.append(arena, .{ + return .{ .name = try slug(arena, stem), // `palette` stays null: helix leaves the colors a PROGRAM emits alone, // and so do we. @@ -349,7 +378,7 @@ fn helixTheme(arena: std.mem.Allocator, stem: []const u8, h: Helix, out: *std.Ar .scroll_track = h.get("ui.statusline", .bg) orelse page, .scroll_thumb = sel, .palette = null, - }); + }; } /// helix lets a theme name one of the terminal's sixteen colors instead of an @@ -510,7 +539,19 @@ fn table(s: []const u8) Pair { // take them: a light theme is only usable if the shell's own output is // repainted for it, and pardes's acme-light is the hand-written proof. -fn readZed(arena: std.mem.Allocator, gpa: std.mem.Allocator, path: []const u8, text: []const u8, out: *std.ArrayList(Theme)) !void { +fn zedThemeCount(gpa: std.mem.Allocator, path: []const u8, text: []const u8) !usize { + const parsed = std.json.parseFromSlice(std.json.Value, gpa, text, .{}) catch + std.process.fatal("{s}: not valid JSON", .{path}); + defer parsed.deinit(); + const root = switch (parsed.value) { + .object => |o| o, + else => std.process.fatal("{s}: top level is not an object", .{path}), + }; + const list = root.get("themes") orelse std.process.fatal("{s}: no `themes` array", .{path}); + return list.array.items.len; +} + +fn readZed(arena: std.mem.Allocator, gpa: std.mem.Allocator, path: []const u8, text: []const u8, out: []Theme) !usize { const parsed = std.json.parseFromSlice(std.json.Value, gpa, text, .{}) catch std.process.fatal("{s}: not valid JSON", .{path}); defer parsed.deinit(); @@ -519,7 +560,8 @@ fn readZed(arena: std.mem.Allocator, gpa: std.mem.Allocator, path: []const u8, t else => std.process.fatal("{s}: top level is not an object", .{path}), }; const list = root.get("themes") orelse std.process.fatal("{s}: no `themes` array", .{path}); - for (list.array.items) |variant| { + std.debug.assert(list.array.items.len <= out.len); + for (list.array.items, 0..) |variant, index| { const v = variant.object; const z: Zed = .{ .path = path, @@ -531,7 +573,7 @@ fn readZed(arena: std.mem.Allocator, gpa: std.mem.Allocator, path: []const u8, t pal[i] = z.need(try std.fmt.allocPrint(arena, "terminal.ansi.{s}", .{n})); pal[i + 8] = z.need(try std.fmt.allocPrint(arena, "terminal.ansi.bright_{s}", .{n})); } - try out.append(arena, .{ + out[index] = .{ // `_zed` on every one of them: see the naming note at the top — // zed re-ships gruvbox, ayu and one, so the source is part of the // name rather than a suffix that appears only on the eight that @@ -563,8 +605,9 @@ fn readZed(arena: std.mem.Allocator, gpa: std.mem.Allocator, path: []const u8, t .scroll_track = z.need("scrollbar.thumb.border"), .scroll_thumb = z.need("scrollbar.thumb.background"), .palette = pal, - }); + }; } + return list.array.items.len; } /// zed's order, which is also the ANSI one: 0-7 plain, 8-15 the `bright_` half |
