From cef1f19d24d6aff9839dc67ae125c2a358b093de Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Sun, 15 Mar 2026 20:46:58 -0400 Subject: [PATCH] cli: add +explain-config This is a new CLI action that prints an option or keybind's help documentation to stdout. ghostty +explain-config font-size ghostty +explain-config copy_to_clipboard ghostty +explain-config --option=font-size ghostty +explain-config --keybind=copy_to_clipboard The --option and --keybind flags perform a specific lookup. A string passed as a positional argument attempts to look up the name first as an option and then as a keybind. Our vim plugin uses this with &keywordprg, which allows you to look up the documentation for the config option or keybind under the cursor (K). --- src/cli/explain_config.zig | 130 +++++++++++++++++++++++++++++++++++++ src/cli/ghostty.zig | 6 ++ src/extra/vim.zig | 5 +- 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/cli/explain_config.zig diff --git a/src/cli/explain_config.zig b/src/cli/explain_config.zig new file mode 100644 index 000000000..115ef6fb8 --- /dev/null +++ b/src/cli/explain_config.zig @@ -0,0 +1,130 @@ +const std = @import("std"); +const args = @import("args.zig"); +const Allocator = std.mem.Allocator; +const Action = @import("ghostty.zig").Action; +const help_strings = @import("help_strings"); +const Config = @import("../config/Config.zig"); +const ConfigKey = @import("../config/key.zig").Key; +const KeybindAction = @import("../input/Binding.zig").Action; + +pub const Options = struct { + /// The config option to explain. For example: + /// + /// ghostty +explain-config --option=font-size + option: ?[]const u8 = null, + + /// The keybind action to explain. For example: + /// + /// ghostty +explain-config --keybind=copy_to_clipboard + keybind: ?[]const u8 = null, + + pub fn deinit(self: Options) void { + _ = self; + } + + /// Enables `-h` and `--help` to work. + pub fn help(self: Options) !void { + _ = self; + return Action.help_error; + } +}; + +/// The `explain-config` command prints the documentation for a single +/// Ghostty configuration option or keybind action. +/// +/// Examples: +/// +/// ghostty +explain-config font-size +/// ghostty +explain-config copy_to_clipboard +/// ghostty +explain-config --option=font-size +/// ghostty +explain-config --keybind=copy_to_clipboard +/// +/// Flags: +/// +/// * `--option`: The name of the configuration option to explain. +/// * `--keybind`: The name of the keybind action to explain. +pub fn run(alloc: Allocator) !u8 { + var option_name: ?[]const u8 = null; + var keybind_name: ?[]const u8 = null; + var positional: ?[]const u8 = null; + var iter = try args.argsIterator(alloc); + defer iter.deinit(); + + while (iter.next()) |arg| { + if (std.mem.startsWith(u8, arg, "--option=")) { + option_name = arg["--option=".len..]; + } else if (std.mem.startsWith(u8, arg, "--keybind=")) { + keybind_name = arg["--keybind=".len..]; + } else if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) { + return Action.help_error; + } else if (!std.mem.startsWith(u8, arg, "-")) { + positional = arg; + } + } + + // Resolve what to look up. Explicit flags go directly to their + // respective lookup. A bare positional argument tries config + // options first, then keybind actions as a fallback. + const name = keybind_name orelse option_name orelse positional orelse { + var stderr: std.fs.File = .stderr(); + var buffer: [4096]u8 = undefined; + var stderr_writer = stderr.writer(&buffer); + try stderr_writer.interface.writeAll("Usage: ghostty +explain-config