mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 08:16:13 +03:00
rewrite generate_help for personal style
- Output to stdin instead of a file - Less nesting - Utilize ranged for loops instead of while loops - Eliminate unnecessary state tracking - Put help in a struct
This commit is contained in:
64
build.zig
64
build.zig
@ -114,6 +114,12 @@ pub fn build(b: *std.Build) !void {
|
|||||||
"Build and install the benchmark executables.",
|
"Build and install the benchmark executables.",
|
||||||
) orelse false;
|
) orelse false;
|
||||||
|
|
||||||
|
const emit_helpgen = b.option(
|
||||||
|
bool,
|
||||||
|
"emit-helpgen",
|
||||||
|
"Build and install the helpgen executable.",
|
||||||
|
) orelse false;
|
||||||
|
|
||||||
// On NixOS, the built binary from `zig build` needs to patch the rpath
|
// On NixOS, the built binary from `zig build` needs to patch the rpath
|
||||||
// into the built binary for it to be portable across the NixOS system
|
// into the built binary for it to be portable across the NixOS system
|
||||||
// it was built for. We default this to true if we can detect we're in
|
// it was built for. We default this to true if we can detect we're in
|
||||||
@ -177,6 +183,10 @@ pub fn build(b: *std.Build) !void {
|
|||||||
// We can use wasmtime to test wasm
|
// We can use wasmtime to test wasm
|
||||||
b.enable_wasmtime = true;
|
b.enable_wasmtime = true;
|
||||||
|
|
||||||
|
// Help exe. This must be run before any dependent executables because
|
||||||
|
// otherwise the build will be cached without emit. That's clunky but meh.
|
||||||
|
if (emit_helpgen) addHelp(b, null);
|
||||||
|
|
||||||
// Add our benchmarks
|
// Add our benchmarks
|
||||||
try benchSteps(b, target, optimize, config, emit_bench);
|
try benchSteps(b, target, optimize, config, emit_bench);
|
||||||
|
|
||||||
@ -197,8 +207,6 @@ pub fn build(b: *std.Build) !void {
|
|||||||
.{version},
|
.{version},
|
||||||
));
|
));
|
||||||
|
|
||||||
createHelp(b);
|
|
||||||
|
|
||||||
// Exe
|
// Exe
|
||||||
if (exe_) |exe| {
|
if (exe_) |exe| {
|
||||||
exe.root_module.addOptions("build_options", exe_options);
|
exe.root_module.addOptions("build_options", exe_options);
|
||||||
@ -477,7 +485,10 @@ pub fn build(b: *std.Build) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// On Mac we can build the embedding library. This only handles the macOS lib.
|
// On Mac we can build the embedding library. This only handles the macOS lib.
|
||||||
if (builtin.target.isDarwin() and target.result.os.tag == .macos) {
|
if (builtin.target.isDarwin() and
|
||||||
|
target.result.os.tag == .macos and
|
||||||
|
config.app_runtime == .none)
|
||||||
|
{
|
||||||
// Create the universal macOS lib.
|
// Create the universal macOS lib.
|
||||||
const macos_lib_step, const macos_lib_path = try createMacOSLib(
|
const macos_lib_step, const macos_lib_path = try createMacOSLib(
|
||||||
b,
|
b,
|
||||||
@ -1100,41 +1111,42 @@ fn addDeps(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addHelp(step);
|
addHelp(b, step);
|
||||||
|
|
||||||
return static_libs;
|
return static_libs;
|
||||||
}
|
}
|
||||||
|
|
||||||
var generate_help_step: *std.Build.Step.Run = undefined;
|
|
||||||
var help_strings: std.Build.LazyPath = undefined;
|
|
||||||
|
|
||||||
/// Generate help files
|
/// Generate help files
|
||||||
fn createHelp(b: *std.Build) void {
|
fn addHelp(
|
||||||
const generate_help = b.addExecutable(.{
|
b: *std.Build,
|
||||||
.name = "generate_help",
|
step_: ?*std.Build.Step.Compile,
|
||||||
.root_source_file = .{ .path = "src/generate_help_strings.zig" },
|
) void {
|
||||||
|
// Our static state between runs. We memoize our help strings
|
||||||
|
// so that we only execute the help generation once.
|
||||||
|
const HelpState = struct {
|
||||||
|
var generated: ?std.Build.LazyPath = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const help_output = HelpState.generated orelse strings: {
|
||||||
|
const help_exe = b.addExecutable(.{
|
||||||
|
.name = "helpgen",
|
||||||
|
.root_source_file = .{ .path = "src/helpgen.zig" },
|
||||||
.target = b.host,
|
.target = b.host,
|
||||||
});
|
});
|
||||||
|
if (step_ == null) b.installArtifact(help_exe);
|
||||||
|
|
||||||
generate_help_step = b.addRunArtifact(generate_help);
|
const help_run = b.addRunArtifact(help_exe);
|
||||||
generate_help_step.step.dependOn(&generate_help.step);
|
HelpState.generated = help_run.captureStdOut();
|
||||||
|
break :strings HelpState.generated.?;
|
||||||
|
};
|
||||||
|
|
||||||
help_strings = generate_help_step.addOutputFileArg("help_strings.zig");
|
if (step_) |step| {
|
||||||
|
help_output.addStepDependencies(&step.step);
|
||||||
if (builtin.target.isDarwin()) {
|
|
||||||
const generated = b.option([]const u8, "help_strings", "generated help file") orelse "help_strings";
|
|
||||||
const write_file = b.addWriteFiles();
|
|
||||||
help_strings = write_file.addCopyFile(help_strings, generated);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add the generated help files to the build.
|
|
||||||
fn addHelp(step: *std.Build.Step.Compile) void {
|
|
||||||
step.step.dependOn(&generate_help_step.step);
|
|
||||||
step.root_module.addAnonymousImport("help_strings", .{
|
step.root_module.addAnonymousImport("help_strings", .{
|
||||||
.root_source_file = help_strings,
|
.root_source_file = help_output,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn benchSteps(
|
fn benchSteps(
|
||||||
b: *std.Build,
|
b: *std.Build,
|
||||||
|
@ -69,6 +69,21 @@ pub const Action = enum {
|
|||||||
.@"list-colors" => try list_colors.run(alloc),
|
.@"list-colors" => try list_colors.run(alloc),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the filename associated with an action. This is a relative
|
||||||
|
/// path from the root src/ directory.
|
||||||
|
pub fn file(comptime self: Action) []const u8 {
|
||||||
|
comptime {
|
||||||
|
const filename = filename: {
|
||||||
|
const tag = @tagName(self);
|
||||||
|
var filename: [tag.len]u8 = undefined;
|
||||||
|
_ = std.mem.replace(u8, tag, "-", "_", &filename);
|
||||||
|
break :filename &filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
return "cli/" ++ filename ++ ".zig";
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
test "parse action none" {
|
test "parse action none" {
|
||||||
|
@ -1,165 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const ziglyph = @import("ziglyph");
|
|
||||||
const Action = @import("cli/action.zig").Action;
|
|
||||||
const Config = @import("config/Config.zig");
|
|
||||||
|
|
||||||
pub fn searchConfigAst(alloc: std.mem.Allocator, output: std.fs.File) !void {
|
|
||||||
var ast = try std.zig.Ast.parse(alloc, @embedFile("config/Config.zig"), .zig);
|
|
||||||
defer ast.deinit(alloc);
|
|
||||||
|
|
||||||
const config: Config = .{};
|
|
||||||
|
|
||||||
const tokens = ast.tokens.items(.tag);
|
|
||||||
|
|
||||||
var set = std.StringHashMap(bool).init(alloc);
|
|
||||||
defer set.deinit();
|
|
||||||
|
|
||||||
try output.writeAll(
|
|
||||||
\\//THIS FILE IS AUTO GENERATED
|
|
||||||
\\//DO NOT MAKE ANY CHANGES TO THIS FILE!
|
|
||||||
);
|
|
||||||
|
|
||||||
try output.writeAll("\n\n");
|
|
||||||
|
|
||||||
inline for (@typeInfo(@TypeOf(config)).Struct.fields) |field| {
|
|
||||||
if (field.name[0] != '_') try set.put(field.name, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
var index: u32 = 0;
|
|
||||||
while (true) : (index += 1) {
|
|
||||||
if (index >= tokens.len) break;
|
|
||||||
const token = tokens[index];
|
|
||||||
|
|
||||||
if (token == .identifier) {
|
|
||||||
const slice = ast.tokenSlice(index);
|
|
||||||
// We need this check because the ast grabs the identifier with @"" in case it's used.
|
|
||||||
const key = if (slice[0] == '@') slice[2 .. slice.len - 1] else slice;
|
|
||||||
|
|
||||||
if (key[0] == '_') continue;
|
|
||||||
|
|
||||||
if (set.get(key)) |value| {
|
|
||||||
if (value) continue;
|
|
||||||
if (tokens[index - 1] != .doc_comment) continue;
|
|
||||||
|
|
||||||
const comment = try consumeDocComments(alloc, ast, index - 1, &tokens);
|
|
||||||
const prop_type = ": " ++ "[:0]const u8 " ++ "= " ++ "\n";
|
|
||||||
|
|
||||||
try output.writeAll(slice);
|
|
||||||
try output.writeAll(prop_type);
|
|
||||||
// const concat = try std.mem.concat(self.alloc, u8, &.{ slice, prop_type });
|
|
||||||
// try output.writeAll(concat);
|
|
||||||
try output.writeAll(comment);
|
|
||||||
try output.writeAll("\n\n");
|
|
||||||
|
|
||||||
try set.put(key, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (token == .eof) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn actionPath(comptime action: Action) []const u8 {
|
|
||||||
return switch (action) {
|
|
||||||
.version => "cli/version.zig",
|
|
||||||
.@"list-fonts" => "cli/list_fonts.zig",
|
|
||||||
.@"list-keybinds" => "cli/list_keybinds.zig",
|
|
||||||
.@"list-themes" => "cli/list_themes.zig",
|
|
||||||
.@"list-colors" => "cli/list_colors.zig",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn searchActionsAst(alloc: std.mem.Allocator, output: std.fs.File) !void {
|
|
||||||
inline for (@typeInfo(Action).Enum.fields) |field| {
|
|
||||||
const action = comptime std.meta.stringToEnum(Action, field.name).?;
|
|
||||||
|
|
||||||
var ast = try std.zig.Ast.parse(alloc, @embedFile(comptime actionPath(action)), .zig);
|
|
||||||
const tokens = ast.tokens.items(.tag);
|
|
||||||
|
|
||||||
var index: u32 = 0;
|
|
||||||
while (true) : (index += 1) {
|
|
||||||
if (tokens[index] == .keyword_fn) {
|
|
||||||
if (std.mem.eql(u8, ast.tokenSlice(index + 1), "run")) {
|
|
||||||
if (tokens[index - 2] != .doc_comment) {
|
|
||||||
std.debug.print("doc comment must be present on run function of the {s} action!", .{field.name});
|
|
||||||
std.process.exit(1);
|
|
||||||
}
|
|
||||||
const comment = try consumeDocComments(alloc, ast, index - 2, &tokens);
|
|
||||||
const prop_type = "@\"+" ++ field.name ++ "\"" ++ ": " ++ "[:0]const u8 " ++ "= " ++ "\n";
|
|
||||||
|
|
||||||
try output.writeAll(prop_type);
|
|
||||||
try output.writeAll(comment);
|
|
||||||
try output.writeAll("\n\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consumeDocComments(alloc: std.mem.Allocator, ast: std.zig.Ast, index: std.zig.Ast.TokenIndex, toks: anytype) ![]const u8 {
|
|
||||||
var lines = std.ArrayList([]const u8).init(alloc);
|
|
||||||
defer lines.deinit();
|
|
||||||
|
|
||||||
const tokens = toks.*;
|
|
||||||
var current_idx = index;
|
|
||||||
|
|
||||||
// We iterate backwards because the doc_comment tokens should be on top of each other in case there are any.
|
|
||||||
while (true) : (current_idx -= 1) {
|
|
||||||
const token = tokens[current_idx];
|
|
||||||
|
|
||||||
if (token != .doc_comment) break;
|
|
||||||
// Insert at 0 so that we don't have the text in reverse.
|
|
||||||
try lines.insert(0, ast.tokenSlice(current_idx)[3..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const prefix = findCommonPrefix(lines);
|
|
||||||
|
|
||||||
var buffer = std.ArrayList(u8).init(alloc);
|
|
||||||
const writer = buffer.writer();
|
|
||||||
|
|
||||||
for (lines.items) |line| {
|
|
||||||
try writer.writeAll(" \\\\");
|
|
||||||
try writer.writeAll(line[@min(prefix, line.len)..]);
|
|
||||||
try writer.writeAll("\n");
|
|
||||||
}
|
|
||||||
try writer.writeAll(",\n");
|
|
||||||
|
|
||||||
return buffer.toOwnedSlice();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn findCommonPrefix(lines: std.ArrayList([]const u8)) usize {
|
|
||||||
var m: usize = std.math.maxInt(usize);
|
|
||||||
for (lines.items) |line| {
|
|
||||||
var n: usize = std.math.maxInt(usize);
|
|
||||||
for (line, 0..) |c, i| {
|
|
||||||
if (c != ' ') {
|
|
||||||
n = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m = @min(m, n);
|
|
||||||
}
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
var arena = std.heap.ArenaAllocator.init(gpa.allocator());
|
|
||||||
const alloc = arena.allocator();
|
|
||||||
|
|
||||||
const args = try std.process.argsAlloc(alloc);
|
|
||||||
defer std.process.argsFree(alloc, args);
|
|
||||||
|
|
||||||
if (args.len != 2) {
|
|
||||||
std.debug.print("invalid number of arguments provided!", .{});
|
|
||||||
std.process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = args[1];
|
|
||||||
|
|
||||||
var output = try std.fs.cwd().createFile(path, .{});
|
|
||||||
defer output.close();
|
|
||||||
|
|
||||||
try searchConfigAst(alloc, output);
|
|
||||||
try searchActionsAst(alloc, output);
|
|
||||||
}
|
|
Reference in New Issue
Block a user