mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-24 12:46:10 +03:00

A typo in the fish completions (that was likely copied to the zsh and bash completions) prevented CLI actions from using enums as option values because the completions tried to access non-existent fields from type introspection. This doesn't cause any problems _now_ because no CLI action uses an enum as an option value. However as soon as you try and add one the completions fail to compile. This patch fixes the incorrect field reference. It also adds the ability to have _optional_ enums as option values.
145 lines
5.8 KiB
Zig
145 lines
5.8 KiB
Zig
const std = @import("std");
|
|
|
|
const Config = @import("../config/Config.zig");
|
|
const Action = @import("../cli/action.zig").Action;
|
|
|
|
/// A fish completions configuration that contains all the available commands
|
|
/// and options.
|
|
pub const fish_completions = comptimeGenerateFishCompletions();
|
|
|
|
fn comptimeGenerateFishCompletions() []const u8 {
|
|
comptime {
|
|
@setEvalBranchQuota(50000);
|
|
var counter = std.io.countingWriter(std.io.null_writer);
|
|
try writeFishCompletions(&counter.writer());
|
|
|
|
var buf: [counter.bytes_written]u8 = undefined;
|
|
var stream = std.io.fixedBufferStream(&buf);
|
|
try writeFishCompletions(stream.writer());
|
|
const final = buf;
|
|
return final[0..stream.getWritten().len];
|
|
}
|
|
}
|
|
|
|
fn writeFishCompletions(writer: anytype) !void {
|
|
{
|
|
try writer.writeAll("set -l commands \"");
|
|
var count: usize = 0;
|
|
for (@typeInfo(Action).Enum.fields) |field| {
|
|
if (std.mem.eql(u8, "help", field.name)) continue;
|
|
if (std.mem.eql(u8, "version", field.name)) continue;
|
|
if (count > 0) try writer.writeAll(" ");
|
|
try writer.writeAll("+");
|
|
try writer.writeAll(field.name);
|
|
count += 1;
|
|
}
|
|
try writer.writeAll("\"\n");
|
|
}
|
|
|
|
try writer.writeAll("complete -c ghostty -f\n");
|
|
|
|
try writer.writeAll("complete -c ghostty -s e -l help -f\n");
|
|
try writer.writeAll("complete -c ghostty -n \"not __fish_seen_subcommand_from $commands\" -l version -f\n");
|
|
|
|
for (@typeInfo(Config).Struct.fields) |field| {
|
|
if (field.name[0] == '_') continue;
|
|
|
|
try writer.writeAll("complete -c ghostty -n \"not __fish_seen_subcommand_from $commands\" -l ");
|
|
try writer.writeAll(field.name);
|
|
try writer.writeAll(if (field.type != bool) " -r" else " ");
|
|
if (std.mem.startsWith(u8, field.name, "font-family"))
|
|
try writer.writeAll(" -f -a \"(ghostty +list-fonts | grep '^[A-Z]')\"")
|
|
else if (std.mem.eql(u8, "theme", field.name))
|
|
try writer.writeAll(" -f -a \"(ghostty +list-themes | sed -E 's/^(.*) \\(.*\\$/\\1/')\"")
|
|
else if (std.mem.eql(u8, "working-directory", field.name))
|
|
try writer.writeAll(" -f -k -a \"(__fish_complete_directories)\"")
|
|
else {
|
|
try writer.writeAll(if (field.type != Config.RepeatablePath) " -f" else " -F");
|
|
switch (@typeInfo(field.type)) {
|
|
.Bool => try writer.writeAll(" -a \"true false\""),
|
|
.Enum => |info| {
|
|
try writer.writeAll(" -a \"");
|
|
for (info.fields, 0..) |f, i| {
|
|
if (i > 0) try writer.writeAll(" ");
|
|
try writer.writeAll(f.name);
|
|
}
|
|
try writer.writeAll("\"");
|
|
},
|
|
.Struct => |info| {
|
|
if (!@hasDecl(field.type, "parseCLI") and info.layout == .@"packed") {
|
|
try writer.writeAll(" -a \"");
|
|
for (info.fields, 0..) |f, i| {
|
|
if (i > 0) try writer.writeAll(" ");
|
|
try writer.writeAll(f.name);
|
|
try writer.writeAll(" no-");
|
|
try writer.writeAll(f.name);
|
|
}
|
|
try writer.writeAll("\"");
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
try writer.writeAll("\n");
|
|
}
|
|
|
|
{
|
|
try writer.writeAll("complete -c ghostty -n \"string match -q -- '+*' (commandline -pt)\" -f -a \"");
|
|
var count: usize = 0;
|
|
for (@typeInfo(Action).Enum.fields) |field| {
|
|
if (std.mem.eql(u8, "help", field.name)) continue;
|
|
if (std.mem.eql(u8, "version", field.name)) continue;
|
|
if (count > 0) try writer.writeAll(" ");
|
|
try writer.writeAll("+");
|
|
try writer.writeAll(field.name);
|
|
count += 1;
|
|
}
|
|
try writer.writeAll("\"\n");
|
|
}
|
|
|
|
for (@typeInfo(Action).Enum.fields) |field| {
|
|
if (std.mem.eql(u8, "help", field.name)) continue;
|
|
if (std.mem.eql(u8, "version", field.name)) continue;
|
|
|
|
const options = @field(Action, field.name).options();
|
|
for (@typeInfo(options).Struct.fields) |opt| {
|
|
if (opt.name[0] == '_') continue;
|
|
try writer.writeAll("complete -c ghostty -n \"__fish_seen_subcommand_from +" ++ field.name ++ "\" -l ");
|
|
try writer.writeAll(opt.name);
|
|
try writer.writeAll(if (opt.type != bool) " -r" else "");
|
|
|
|
// special case +validate_config --config-file
|
|
if (std.mem.eql(u8, "config-file", opt.name)) {
|
|
try writer.writeAll(" -F");
|
|
} else try writer.writeAll(" -f");
|
|
|
|
switch (@typeInfo(opt.type)) {
|
|
.Bool => try writer.writeAll(" -a \"true false\""),
|
|
.Enum => |info| {
|
|
try writer.writeAll(" -a \"");
|
|
for (info.fields, 0..) |f, i| {
|
|
if (i > 0) try writer.writeAll(" ");
|
|
try writer.writeAll(f.name);
|
|
}
|
|
try writer.writeAll("\"");
|
|
},
|
|
.Optional => |optional| {
|
|
switch (@typeInfo(optional.child)) {
|
|
.Enum => |info| {
|
|
try writer.writeAll(" -a \"");
|
|
for (info.fields, 0..) |f, i| {
|
|
if (i > 0) try writer.writeAll(" ");
|
|
try writer.writeAll(f.name);
|
|
}
|
|
try writer.writeAll("\"");
|
|
},
|
|
else => {},
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
try writer.writeAll("\n");
|
|
}
|
|
}
|
|
}
|