diff --git a/src/cli/action.zig b/src/cli/action.zig index a0fa216eb..2e3afb1c0 100644 --- a/src/cli/action.zig +++ b/src/cli/action.zig @@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator; const help_strings = @import("help_strings"); const list_fonts = @import("list_fonts.zig"); +const help = @import("help.zig"); const version = @import("version.zig"); const list_keybinds = @import("list_keybinds.zig"); const list_themes = @import("list_themes.zig"); @@ -15,6 +16,9 @@ pub const Action = enum { /// Output the version and exit version, + /// Output help information for the CLI or configuration + help, + /// List available fonts @"list-fonts", @@ -48,18 +52,33 @@ pub const Action = enum { /// Detect the action from any iterator, used primarily for tests. pub fn detectIter(iter: anytype) Error!?Action { + var pending_help: bool = false; var pending: ?Action = null; while (iter.next()) |arg| { // Special case, --version always outputs the version no // matter what, no matter what other args exist. if (std.mem.eql(u8, arg, "--version")) return .version; + // --help matches "help" but if a subcommand is specified + // then we match the subcommand. + if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) { + pending_help = true; + continue; + } + // Commands must start with "+" if (arg.len == 0 or arg[0] != '+') continue; if (pending != null) return Error.MultipleActions; pending = std.meta.stringToEnum(Action, arg[1..]) orelse return Error.InvalidAction; } + // If we have an action, we always return that action, even if we've + // seen "--help" or "-h" because the action may have its own help text. + if (pending != null) return pending; + + // If we've seen "--help" or "-h" then we return the help action. + if (pending_help) return .help; + return pending; } @@ -95,6 +114,7 @@ pub const Action = enum { fn runMain(self: Action, alloc: Allocator) !u8 { return switch (self) { .version => try version.run(alloc), + .help => try help.run(alloc), .@"list-fonts" => try list_fonts.run(alloc), .@"list-keybinds" => try list_keybinds.run(alloc), .@"list-themes" => try list_themes.run(alloc), diff --git a/src/cli/help.zig b/src/cli/help.zig new file mode 100644 index 000000000..e75b1b617 --- /dev/null +++ b/src/cli/help.zig @@ -0,0 +1,71 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const args = @import("args.zig"); +const Action = @import("action.zig").Action; + +// Note that this options struct doesn't implement the `help` decl like +// other actions. That is because the help command is special and wants to +// handle its own logic around help detection. +pub const Options = struct { + /// This must be registered so that it isn't an error to pass `--help` + help: bool = false, + + pub fn deinit(self: Options) void { + _ = self; + } +}; + +/// The `help` command shows general help about Ghostty. You can also +/// specify `--help` or `-h` along with any action such as `+list-themes` +/// to see help for a specific action. +pub fn run(alloc: Allocator) !u8 { + var opts: Options = .{}; + defer opts.deinit(); + + { + var iter = try std.process.argsWithAllocator(alloc); + defer iter.deinit(); + try args.parse(Options, alloc, &opts, &iter); + } + + const stdout = std.io.getStdOut().writer(); + try stdout.writeAll( + \\Usage: ghostty [+action] [options] + \\ + \\Run the Ghostty terminal emulator or a specific helper action. + \\ + \\If no `+action` is specified, run the Ghostty terminal emulator. + \\All configuration keys are available as command line options. + \\To specify a configuration key, use the `--=` syntax + \\where key and value are the same format you'd put into a configuration + \\file. For example, `--font-size=12` or `--font-family="Fira Code"`. + \\ + \\To see a list of all available configuration options, please see + \\the `src/config/Config.zig` file. A future update will allow seeing + \\the list of configuration options from the command line. + \\ + \\A special command line argument `-e ` can be used to run + \\the specific command inside the terminal emulator. For example, + \\`ghostty -e top` will run the `top` command inside the terminal. + \\ + \\On macOS, launching the terminal emulator from the CLI is not + \\supported and only actions are supported. + \\ + \\Available actions: + \\ + \\ + ); + + inline for (@typeInfo(Action).Enum.fields) |field| { + try stdout.print(" +{s}\n", .{field.name}); + } + + try stdout.writeAll( + \\ + \\Specify `+ --help` to see the help for a specific action, + \\where `` is one of actions listed below. + \\ + ); + + return 0; +}