fish: add descriptions to fish shell completions (#9551)

Add descriptions to fish shell completions 

Claude Code used to understand the codebase and reason through the edge
cases (several iterations)

- Using first sentence from the Config to add description for each
config, even if sentence spans few lines
- Several options can share the same description, code doesn't duplicate
description, see screenshot in the posted issue thread below

Fixes [#9531](https://github.com/ghostty-org/ghostty/issues/9531)
pull/8200/head
Jon Parise 2025-11-20 09:15:35 -05:00 committed by GitHub
commit a4e65f02b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 66 additions and 2 deletions

View File

@ -55,7 +55,7 @@ pub fn build(b: *std.Build) !void {
); );
// Ghostty resources like terminfo, shell integration, themes, etc. // Ghostty resources like terminfo, shell integration, themes, etc.
const resources = try buildpkg.GhosttyResources.init(b, &config); const resources = try buildpkg.GhosttyResources.init(b, &config, &deps);
const i18n = if (config.i18n) try buildpkg.GhosttyI18n.init(b, &config) else null; const i18n = if (config.i18n) try buildpkg.GhosttyI18n.init(b, &config) else null;
// Ghostty executable, the actual runnable Ghostty program. // Ghostty executable, the actual runnable Ghostty program.

View File

@ -6,10 +6,11 @@ const assert = std.debug.assert;
const buildpkg = @import("main.zig"); const buildpkg = @import("main.zig");
const Config = @import("Config.zig"); const Config = @import("Config.zig");
const RunStep = std.Build.Step.Run; const RunStep = std.Build.Step.Run;
const SharedDeps = @import("SharedDeps.zig");
steps: []*std.Build.Step, steps: []*std.Build.Step,
pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources { pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !GhosttyResources {
var steps: std.ArrayList(*std.Build.Step) = .empty; var steps: std.ArrayList(*std.Build.Step) = .empty;
errdefer steps.deinit(b.allocator); errdefer steps.deinit(b.allocator);
@ -26,6 +27,8 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
}); });
build_data_exe.linkLibC(); build_data_exe.linkLibC();
deps.help_strings.addImport(build_data_exe);
// Terminfo // Terminfo
terminfo: { terminfo: {
const os_tag = cfg.target.result.os.tag; const os_tag = cfg.target.result.os.tag;

View File

@ -3,6 +3,7 @@ const std = @import("std");
const Config = @import("../config/Config.zig"); const Config = @import("../config/Config.zig");
const Action = @import("../cli.zig").ghostty.Action; const Action = @import("../cli.zig").ghostty.Action;
const help_strings = @import("help_strings");
/// A fish completions configuration that contains all the available commands /// A fish completions configuration that contains all the available commands
/// and options. /// and options.
@ -81,6 +82,15 @@ fn writeCompletions(writer: *std.Io.Writer) !void {
else => {}, else => {},
} }
} }
if (@hasDecl(help_strings.Config, field.name)) {
const help = @field(help_strings.Config, field.name);
const desc = getDescription(help);
try writer.writeAll(" -d \"");
try writer.writeAll(desc);
try writer.writeAll("\"");
}
try writer.writeAll("\n"); try writer.writeAll("\n");
} }
@ -143,3 +153,54 @@ fn writeCompletions(writer: *std.Io.Writer) !void {
} }
} }
} }
fn getDescription(comptime help: []const u8) []const u8 {
var out: [help.len * 2]u8 = undefined;
var len: usize = 0;
var prev_was_space = false;
for (help, 0..) |c, i| {
switch (c) {
'.' => {
out[len] = '.';
len += 1;
if (i + 1 >= help.len) break;
const next = help[i + 1];
if (next == ' ' or next == '\n') break;
},
'\n' => {
if (!prev_was_space and len > 0) {
out[len] = ' ';
len += 1;
prev_was_space = true;
}
},
'"' => {
out[len] = '\\';
out[len + 1] = '"';
len += 2;
prev_was_space = false;
},
else => {
out[len] = c;
len += 1;
prev_was_space = (c == ' ');
},
}
}
return out[0..len];
}
test "getDescription" {
const testing = std.testing;
const input = "First sentence with \"quotes\"\nand newlines. Second sentence.";
const expected = "First sentence with \\\"quotes\\\" and newlines.";
comptime {
const result = getDescription(input);
try testing.expectEqualStrings(expected, result);
}
}