mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
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).
This commit is contained in:
130
src/cli/explain_config.zig
Normal file
130
src/cli/explain_config.zig
Normal file
@@ -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 <option>\n");
|
||||
try stderr_writer.interface.writeAll(" ghostty +explain-config --option=<option>\n");
|
||||
try stderr_writer.interface.writeAll(" ghostty +explain-config --keybind=<action>\n");
|
||||
try stderr_writer.end();
|
||||
return 1;
|
||||
};
|
||||
|
||||
const text = if (keybind_name != null)
|
||||
explainKeybind(name)
|
||||
else if (option_name != null)
|
||||
explainOption(name)
|
||||
else
|
||||
explainOption(name) orelse explainKeybind(name);
|
||||
|
||||
var stdout: std.fs.File = .stdout();
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var stdout_writer = stdout.writer(&buffer);
|
||||
const writer = &stdout_writer.interface;
|
||||
|
||||
if (text) |t| {
|
||||
try writer.writeAll(t);
|
||||
try writer.writeAll("\n");
|
||||
} else {
|
||||
try writer.writeAll("Unknown: '");
|
||||
try writer.writeAll(name);
|
||||
try writer.writeAll("'.\n");
|
||||
try stdout_writer.end();
|
||||
return 1;
|
||||
}
|
||||
|
||||
try stdout_writer.end();
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn explainOption(name: []const u8) ?[]const u8 {
|
||||
const key = std.meta.stringToEnum(ConfigKey, name) orelse return null;
|
||||
return switch (key) {
|
||||
inline else => |tag| {
|
||||
const field_name = @tagName(tag);
|
||||
return if (@hasDecl(help_strings.Config, field_name))
|
||||
@field(help_strings.Config, field_name)
|
||||
else
|
||||
null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn explainKeybind(name: []const u8) ?[]const u8 {
|
||||
const tag = std.meta.stringToEnum(std.meta.Tag(KeybindAction), name) orelse return null;
|
||||
return switch (tag) {
|
||||
inline else => |t| {
|
||||
const field_name = @tagName(t);
|
||||
return if (@hasDecl(help_strings.KeybindAction, field_name))
|
||||
@field(help_strings.KeybindAction, field_name)
|
||||
else
|
||||
null;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -14,6 +14,7 @@ const list_actions = @import("list_actions.zig");
|
||||
const ssh_cache = @import("ssh_cache.zig");
|
||||
const edit_config = @import("edit_config.zig");
|
||||
const show_config = @import("show_config.zig");
|
||||
const explain_config = @import("explain_config.zig");
|
||||
const validate_config = @import("validate_config.zig");
|
||||
const crash_report = @import("crash_report.zig");
|
||||
const show_face = @import("show_face.zig");
|
||||
@@ -54,6 +55,9 @@ pub const Action = enum {
|
||||
/// Dump the config to stdout
|
||||
@"show-config",
|
||||
|
||||
/// Explain a single config option
|
||||
@"explain-config",
|
||||
|
||||
// Validate passed config file
|
||||
@"validate-config",
|
||||
|
||||
@@ -142,6 +146,7 @@ pub const Action = enum {
|
||||
.@"ssh-cache" => try ssh_cache.run(alloc),
|
||||
.@"edit-config" => try edit_config.run(alloc),
|
||||
.@"show-config" => try show_config.run(alloc),
|
||||
.@"explain-config" => try explain_config.run(alloc),
|
||||
.@"validate-config" => try validate_config.run(alloc),
|
||||
.@"crash-report" => try crash_report.run(alloc),
|
||||
.@"show-face" => try show_face.run(alloc),
|
||||
@@ -181,6 +186,7 @@ pub const Action = enum {
|
||||
.@"ssh-cache" => ssh_cache.Options,
|
||||
.@"edit-config" => edit_config.Options,
|
||||
.@"show-config" => show_config.Options,
|
||||
.@"explain-config" => explain_config.Options,
|
||||
.@"validate-config" => validate_config.Options,
|
||||
.@"crash-report" => crash_report.Options,
|
||||
.@"show-face" => show_face.Options,
|
||||
|
||||
@@ -31,7 +31,10 @@ pub const ftplugin =
|
||||
\\" Use syntax keywords for completion
|
||||
\\setlocal omnifunc=syntaxcomplete#Complete
|
||||
\\
|
||||
\\let b:undo_ftplugin = 'setl cms< isk< ofu<'
|
||||
\\" Ask ghostty to explain config keywords
|
||||
\\setlocal keywordprg=ghostty\ +explain-config
|
||||
\\
|
||||
\\let b:undo_ftplugin = 'setl cms< isk< ofu< kp<'
|
||||
\\
|
||||
\\if !exists('current_compiler')
|
||||
\\ compiler ghostty
|
||||
|
||||
Reference in New Issue
Block a user