diff options
| author | sl0p <sl0p@sl0p.foo> | 2026-08-13 22:00:03 +0200 |
|---|---|---|
| committer | sl0p <sl0p@sl0p.foo> | 2026-08-13 22:00:03 +0200 |
| commit | 2c1b855c3cf8cf8841bf25cd939e478a7f0bfac8 (patch) | |
| tree | 7cd6274303ba1da1f8bd57371aec336dd687fb58 /vendor/libghostty-vt/src/input/command.zig | |
| download | slosh-2c1b855c3cf8cf8841bf25cd939e478a7f0bfac8.tar.gz slosh-2c1b855c3cf8cf8841bf25cd939e478a7f0bfac8.tar.xz slosh-2c1b855c3cf8cf8841bf25cd939e478a7f0bfac8.zip | |
sl0ptty: M0 — libghostty-vt vendored, one pane, compositor, diff renderer
Design contract in DESIGN.md. libghostty-vt vendored fresh from ghostty
main@bedb6f2 (zig 0.16), built to a 14MB static lib.
M0: pty spawn (no libutil), a lib-vt terminal per pane, a screen cell buffer,
a diff emitter that repaints only what changed, raw mode, SIGWINCH, and a
~120Hz frame cap that coalesces pty reads.
Also the seed of the headless harness: --headless dumps the composited screen
without a tty, so composition is testable in milliseconds.
tests/live_m0.py covers what headless cannot: raw mode, the diff emitter,
resize propagation, and the quit path. 6/6 green.
Diffstat (limited to 'vendor/libghostty-vt/src/input/command.zig')
| -rw-r--r-- | vendor/libghostty-vt/src/input/command.zig | 768 |
1 files changed, 768 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/input/command.zig b/vendor/libghostty-vt/src/input/command.zig new file mode 100644 index 0000000..e75ede2 --- /dev/null +++ b/vendor/libghostty-vt/src/input/command.zig @@ -0,0 +1,768 @@ +const std = @import("std"); +const assert = @import("../quirks.zig").inlineAssert; +const Allocator = std.mem.Allocator; +const Action = @import("Binding.zig").Action; +const i18n = @import("../os/i18n.zig"); + +/// A command is a named binding action that can be executed from +/// something like a command palette. +/// +/// A command must be associated with a binding; all commands can be +/// mapped to traditional `keybind` configurations. This restriction +/// makes it so that there is nothing special about commands and likewise +/// it makes it trivial and consistent to define custom commands. +/// +/// For apprt implementers: a command palette doesn't have to make use +/// of all the fields here. We try to provide as much information as +/// possible to make it easier to implement a command palette in the way +/// that makes the most sense for the application. +pub const Command = struct { + action: Action, + title: [:0]const u8, + description: [:0]const u8 = "", + + /// ghostty_command_s + pub const C = extern struct { + action_key: [*:0]const u8, + action: [*:0]const u8, + title: [*:0]const u8, + description: [*:0]const u8, + }; + + pub fn clone(self: *const Command, alloc: Allocator) Allocator.Error!Command { + return .{ + .action = try self.action.clone(alloc), + .title = try alloc.dupeZ(u8, self.title), + .description = try alloc.dupeZ(u8, self.description), + }; + } + + pub fn equal(self: Command, other: Command) bool { + if (self.action.hash() != other.action.hash()) return false; + if (!std.mem.eql(u8, self.title, other.title)) return false; + if (!std.mem.eql(u8, self.description, other.description)) return false; + return true; + } + + /// Convert this command to a C struct at comptime. + pub fn comptimeCval(self: Command) C { + assert(@inComptime()); + + return .{ + .action_key = @tagName(self.action), + .action = std.fmt.comptimePrint("{f}", .{self.action}), + .title = self.title, + .description = self.description, + }; + } + + /// Convert this command to a C struct at runtime. + /// + /// This shares memory with the original command. + /// + /// The action string is allocated using the provided allocator. You can + /// free the slice directly if you need to but we recommend an arena + /// for this. + pub fn cval(self: Command, alloc: Allocator) Allocator.Error!C { + var buf: std.Io.Writer.Allocating = .init(alloc); + defer buf.deinit(); + self.action.format(&buf.writer) catch return error.OutOfMemory; + const action = try buf.toOwnedSliceSentinel(0); + + return .{ + .action_key = @tagName(self.action), + .action = action.ptr, + .title = self.title, + .description = self.description, + }; + } + + pub fn translated(self: Command) Command { + return .{ + .action = self.action, + .title = std.mem.span(i18n._(self.title)), + .description = std.mem.span(i18n._(self.description)), + }; + } + + /// Implements a comparison function for std.mem.sortUnstable + /// and similar functions. The sorting is defined by Ghostty + /// to be what we prefer. If a caller wants some other sorting, + /// they should do it themselves. + pub fn lessThan(_: void, lhs: Command, rhs: Command) bool { + return std.ascii.orderIgnoreCase(lhs.title, rhs.title) == .lt; + } +}; + +pub const defaults: []const Command = defaults: { + @setEvalBranchQuota(100_000); + + var count: usize = 0; + for (@typeInfo(Action.Key).@"enum".fields) |field| { + const action = @field(Action.Key, field.name); + count += actionCommands(action).len; + } + + var result: [count]Command = undefined; + var i: usize = 0; + for (@typeInfo(Action.Key).@"enum".fields) |field| { + const action = @field(Action.Key, field.name); + const commands = actionCommands(action); + for (commands) |cmd| { + result[i] = cmd; + i += 1; + } + } + + std.mem.sortUnstable(Command, &result, {}, Command.lessThan); + + assert(i == count); + const final = result; + break :defaults &final; +}; + +/// Defaults in C-compatible form. +pub const defaultsC: []const Command.C = defaults: { + @setEvalBranchQuota(100_000); + var result: [defaults.len]Command.C = undefined; + for (defaults, 0..) |cmd, i| result[i] = cmd.comptimeCval(); + const final = result; + break :defaults &final; +}; + +/// Returns the set of commands associated with this action key by +/// default. Not all actions should have commands. As a general guideline, +/// an action should have a command only if it is useful and reasonable +/// to appear in a command palette. +fn actionCommands(action: Action.Key) []const Command { + // This is implemented as a function and switch rather than a + // flat comptime const because we want to ensure we get a compiler + // error when a new binding is added so that the contributor has + // to consider whether that new binding should have commands or not. + const result: []const Command = switch (action) { + // Note: the use of `comptime` prefix on the return values + // ensures that the data returned is all in the binary and + // and not pointing to the stack. + + .reset => comptime &.{.{ + .action = .reset, + .title = i18n.N_("Reset Terminal"), + .description = i18n.N_("Reset the terminal to a clean state."), + }}, + + .copy_to_clipboard => comptime &.{ .{ + .action = .{ .copy_to_clipboard = .mixed }, + .title = i18n.N_("Copy to Clipboard"), + .description = i18n.N_("Copy the selected text to the clipboard in both plain and styled formats."), + }, .{ + .action = .{ .copy_to_clipboard = .plain }, + .title = i18n.N_("Copy Selection as Plain Text to Clipboard"), + .description = i18n.N_("Copy the selected text as plain text to the clipboard."), + }, .{ + .action = .{ .copy_to_clipboard = .vt }, + .title = i18n.N_("Copy Selection as ANSI Sequences to Clipboard"), + .description = i18n.N_("Copy the selected text as ANSI escape sequences to the clipboard."), + }, .{ + .action = .{ .copy_to_clipboard = .html }, + .title = i18n.N_("Copy Selection as HTML to Clipboard"), + .description = i18n.N_("Copy the selected text as HTML to the clipboard."), + } }, + + .copy_url_to_clipboard => comptime &.{.{ + .action = .copy_url_to_clipboard, + .title = i18n.N_("Copy URL to Clipboard"), + .description = i18n.N_("Copy the URL under the cursor to the clipboard."), + }}, + + .copy_title_to_clipboard => comptime &.{.{ + .action = .copy_title_to_clipboard, + .title = i18n.N_("Copy Terminal Title to Clipboard"), + .description = i18n.N_("Copy the terminal title to the clipboard. If the terminal title is not set this has no effect."), + }}, + + .paste_from_clipboard => comptime &.{.{ + .action = .paste_from_clipboard, + .title = i18n.N_("Paste from Clipboard"), + .description = i18n.N_("Paste the contents of the main clipboard."), + }}, + + .paste_from_selection => comptime &.{.{ + .action = .paste_from_selection, + .title = i18n.N_("Paste from Selection"), + .description = i18n.N_("Paste the contents of the selection clipboard."), + }}, + + .start_search => comptime &.{.{ + .action = .start_search, + .title = i18n.N_("Start Search"), + .description = i18n.N_("Start a search if one isn't already active."), + }}, + + .search_selection => comptime &.{.{ + .action = .search_selection, + .title = i18n.N_("Search Selection"), + .description = i18n.N_("Start a search for the current text selection."), + }}, + + .end_search => comptime &.{.{ + .action = .end_search, + .title = i18n.N_("End Search"), + .description = i18n.N_("End the current search if any and hide any GUI elements."), + }}, + + .navigate_search => comptime &.{ .{ + .action = .{ .navigate_search = .next }, + .title = i18n.N_("Next Search Result"), + .description = i18n.N_("Navigate to the next search result, if any."), + }, .{ + .action = .{ .navigate_search = .previous }, + .title = i18n.N_("Previous Search Result"), + .description = i18n.N_("Navigate to the previous search result, if any."), + } }, + + .increase_font_size => comptime &.{.{ + .action = .{ .increase_font_size = 1 }, + .title = i18n.N_("Increase Font Size"), + .description = i18n.N_("Increase the font size by 1 point."), + }}, + + .decrease_font_size => comptime &.{.{ + .action = .{ .decrease_font_size = 1 }, + .title = i18n.N_("Decrease Font Size"), + .description = i18n.N_("Decrease the font size by 1 point."), + }}, + + .reset_font_size => comptime &.{.{ + .action = .reset_font_size, + .title = i18n.N_("Reset Font Size"), + .description = i18n.N_("Reset the font size to the default."), + }}, + + .clear_screen => comptime &.{.{ + .action = .clear_screen, + .title = i18n.N_("Clear Screen"), + .description = i18n.N_("Clear the screen and scrollback."), + }}, + + .select_all => comptime &.{.{ + .action = .select_all, + .title = i18n.N_("Select All"), + .description = i18n.N_("Select all text on the screen."), + }}, + + .scroll_to_top => comptime &.{.{ + .action = .scroll_to_top, + .title = i18n.N_("Scroll to Top"), + .description = i18n.N_("Scroll to the top of the screen."), + }}, + + .scroll_to_bottom => comptime &.{.{ + .action = .scroll_to_bottom, + .title = i18n.N_("Scroll to Bottom"), + .description = i18n.N_("Scroll to the bottom of the screen."), + }}, + + .scroll_to_selection => comptime &.{.{ + .action = .scroll_to_selection, + .title = i18n.N_("Scroll to Selection"), + .description = i18n.N_("Scroll to the selected text."), + }}, + + .scroll_page_up => comptime &.{.{ + .action = .scroll_page_up, + .title = i18n.N_("Scroll Page Up"), + .description = i18n.N_("Scroll the screen up by a page."), + }}, + + .scroll_page_down => comptime &.{.{ + .action = .scroll_page_down, + .title = i18n.N_("Scroll Page Down"), + .description = i18n.N_("Scroll the screen down by a page."), + }}, + + .write_screen_file => comptime &.{ + .{ + .action = .{ .write_screen_file = .copy }, + .title = i18n.N_("Copy Screen to Temporary File and Copy Path"), + .description = i18n.N_("Copy the screen contents to a temporary file and copy the path to the clipboard."), + }, + .{ + .action = .{ .write_screen_file = .paste }, + .title = i18n.N_("Copy Screen to Temporary File and Paste Path"), + .description = i18n.N_("Copy the screen contents to a temporary file and paste the path to the file."), + }, + .{ + .action = .{ .write_screen_file = .open }, + .title = i18n.N_("Copy Screen to Temporary File and Open"), + .description = i18n.N_("Copy the screen contents to a temporary file and open it."), + }, + + .{ + .action = .{ .write_screen_file = .{ + .action = .copy, + .emit = .html, + } }, + .title = i18n.N_("Copy Screen as HTML to Temporary File and Copy Path"), + .description = i18n.N_("Copy the screen contents as HTML to a temporary file and copy the path to the clipboard."), + }, + .{ + .action = .{ .write_screen_file = .{ + .action = .paste, + .emit = .html, + } }, + .title = i18n.N_("Copy Screen as HTML to Temporary File and Paste Path"), + .description = i18n.N_("Copy the screen contents as HTML to a temporary file and paste the path to the file."), + }, + .{ + .action = .{ .write_screen_file = .{ + .action = .open, + .emit = .html, + } }, + .title = i18n.N_("Copy Screen as HTML to Temporary File and Open"), + .description = i18n.N_("Copy the screen contents as HTML to a temporary file and open it."), + }, + + .{ + .action = .{ .write_screen_file = .{ + .action = .copy, + .emit = .vt, + } }, + .title = i18n.N_("Copy Screen as ANSI Sequences to Temporary File and Copy Path"), + .description = i18n.N_("Copy the screen contents as ANSI escape sequences to a temporary file and copy the path to the clipboard."), + }, + .{ + .action = .{ .write_screen_file = .{ + .action = .paste, + .emit = .vt, + } }, + .title = i18n.N_("Copy Screen as ANSI Sequences to Temporary File and Paste Path"), + .description = i18n.N_("Copy the screen contents as ANSI escape sequences to a temporary file and paste the path to the file."), + }, + .{ + .action = .{ .write_screen_file = .{ + .action = .open, + .emit = .vt, + } }, + .title = i18n.N_("Copy Screen as ANSI Sequences to Temporary File and Open"), + .description = i18n.N_("Copy the screen contents as ANSI escape sequences to a temporary file and open it."), + }, + }, + + .write_selection_file => comptime &.{ + .{ + .action = .{ .write_selection_file = .copy }, + .title = i18n.N_("Copy Selection to Temporary File and Copy Path"), + .description = i18n.N_("Copy the selection contents to a temporary file and copy the path to the clipboard."), + }, + .{ + .action = .{ .write_selection_file = .paste }, + .title = i18n.N_("Copy Selection to Temporary File and Paste Path"), + .description = i18n.N_("Copy the selection contents to a temporary file and paste the path to the file."), + }, + .{ + .action = .{ .write_selection_file = .open }, + .title = i18n.N_("Copy Selection to Temporary File and Open"), + .description = i18n.N_("Copy the selection contents to a temporary file and open it."), + }, + + .{ + .action = .{ .write_selection_file = .{ + .action = .copy, + .emit = .html, + } }, + .title = i18n.N_("Copy Selection as HTML to Temporary File and Copy Path"), + .description = i18n.N_("Copy the selection contents as HTML to a temporary file and copy the path to the clipboard."), + }, + .{ + .action = .{ .write_selection_file = .{ + .action = .paste, + .emit = .html, + } }, + .title = i18n.N_("Copy Selection as HTML to Temporary File and Paste Path"), + .description = i18n.N_("Copy the selection contents as HTML to a temporary file and paste the path to the file."), + }, + .{ + .action = .{ .write_selection_file = .{ + .action = .open, + .emit = .html, + } }, + .title = i18n.N_("Copy Selection as HTML to Temporary File and Open"), + .description = i18n.N_("Copy the selection contents as HTML to a temporary file and open it."), + }, + + .{ + .action = .{ .write_selection_file = .{ + .action = .copy, + .emit = .vt, + } }, + .title = i18n.N_("Copy Selection as ANSI Sequences to Temporary File and Copy Path"), + .description = i18n.N_("Copy the selection contents as ANSI escape sequences to a temporary file and copy the path to the clipboard."), + }, + .{ + .action = .{ .write_selection_file = .{ + .action = .paste, + .emit = .vt, + } }, + .title = i18n.N_("Copy Selection as ANSI Sequences to Temporary File and Paste Path"), + .description = i18n.N_("Copy the selection contents as ANSI escape sequences to a temporary file and paste the path to the file."), + }, + .{ + .action = .{ .write_selection_file = .{ + .action = .open, + .emit = .vt, + } }, + .title = i18n.N_("Copy Selection as ANSI Sequences to Temporary File and Open"), + .description = i18n.N_("Copy the selection contents as ANSI escape sequences to a temporary file and open it."), + }, + }, + + .new_window => comptime &.{.{ + .action = .new_window, + .title = i18n.N_("New Window"), + .description = i18n.N_("Open a new window."), + }}, + + .new_tab => comptime &.{.{ + .action = .new_tab, + .title = i18n.N_("New Tab"), + .description = i18n.N_("Open a new tab."), + }}, + + .move_tab => comptime &.{ + .{ + .action = .{ .move_tab = -1 }, + .title = i18n.N_("Move Tab Left"), + .description = i18n.N_("Move the current tab to the left."), + }, + .{ + .action = .{ .move_tab = 1 }, + .title = i18n.N_("Move Tab Right"), + .description = i18n.N_("Move the current tab to the right."), + }, + }, + + .move_tab_to_new_window => comptime &.{.{ + .action = .move_tab_to_new_window, + .title = i18n.N_("Move Tab to New Window"), + .description = i18n.N_("Move the current tab to a new window."), + }}, + + .toggle_tab_overview => comptime &.{.{ + .action = .toggle_tab_overview, + .title = i18n.N_("Toggle Tab Overview"), + .description = i18n.N_("Toggle the tab overview."), + }}, + + .prompt_surface_title => comptime &.{.{ + .action = .prompt_surface_title, + .title = i18n.N_("Change Terminal Title…"), + .description = i18n.N_("Prompt for a new title for the current terminal."), + }}, + + .prompt_tab_title => comptime &.{.{ + .action = .prompt_tab_title, + .title = i18n.N_("Change Tab Title…"), + .description = i18n.N_("Prompt for a new title for the current tab."), + }}, + + .prompt_window_title => comptime &.{.{ + .action = .prompt_window_title, + .title = "Change Window Title…", + .description = "Prompt for a new title for the current window.", + }}, + + .new_split => comptime &.{ + .{ + .action = .{ .new_split = .left }, + .title = i18n.N_("Split Left"), + .description = i18n.N_("Split the terminal to the left."), + }, + .{ + .action = .{ .new_split = .right }, + .title = i18n.N_("Split Right"), + .description = i18n.N_("Split the terminal to the right."), + }, + .{ + .action = .{ .new_split = .up }, + .title = i18n.N_("Split Up"), + .description = i18n.N_("Split the terminal up."), + }, + .{ + .action = .{ .new_split = .down }, + .title = i18n.N_("Split Down"), + .description = i18n.N_("Split the terminal down."), + }, + }, + + .goto_split => comptime &.{ + .{ + .action = .{ .goto_split = .previous }, + .title = i18n.N_("Focus Split: Previous"), + .description = i18n.N_("Focus the previous split, if any."), + }, + .{ + .action = .{ .goto_split = .next }, + .title = i18n.N_("Focus Split: Next"), + .description = i18n.N_("Focus the next split, if any."), + }, + .{ + .action = .{ .goto_split = .left }, + .title = i18n.N_("Focus Split: Left"), + .description = i18n.N_("Focus the split to the left, if it exists."), + }, + .{ + .action = .{ .goto_split = .right }, + .title = i18n.N_("Focus Split: Right"), + .description = i18n.N_("Focus the split to the right, if it exists."), + }, + .{ + .action = .{ .goto_split = .up }, + .title = i18n.N_("Focus Split: Up"), + .description = i18n.N_("Focus the split above, if it exists."), + }, + .{ + .action = .{ .goto_split = .down }, + .title = i18n.N_("Focus Split: Down"), + .description = i18n.N_("Focus the split below, if it exists."), + }, + }, + + .goto_window => comptime &.{ + .{ + .action = .{ .goto_window = .previous }, + .title = i18n.N_("Focus Window: Previous"), + .description = i18n.N_("Focus the previous window, if any."), + }, + .{ + .action = .{ .goto_window = .next }, + .title = i18n.N_("Focus Window: Next"), + .description = i18n.N_("Focus the next window, if any."), + }, + }, + + .toggle_split_zoom => comptime &.{.{ + .action = .toggle_split_zoom, + .title = i18n.N_("Toggle Split Zoom"), + .description = i18n.N_("Toggle the zoom state of the current split."), + }}, + + .toggle_readonly => comptime &.{.{ + .action = .toggle_readonly, + .title = i18n.N_("Toggle Read-Only Mode"), + .description = i18n.N_("Toggle read-only mode for the current surface."), + }}, + + .equalize_splits => comptime &.{.{ + .action = .equalize_splits, + .title = i18n.N_("Equalize Splits"), + .description = i18n.N_("Equalize the size of all splits."), + }}, + + .reset_window_size => comptime &.{.{ + .action = .reset_window_size, + .title = i18n.N_("Reset Window Size"), + .description = i18n.N_("Reset the window size to the default."), + }}, + + .inspector => comptime &.{.{ + .action = .{ .inspector = .toggle }, + .title = i18n.N_("Toggle Inspector"), + .description = i18n.N_("Toggle the inspector."), + }}, + + .show_gtk_inspector => comptime &.{.{ + .action = .show_gtk_inspector, + .title = i18n.N_("Show the GTK Inspector"), + .description = i18n.N_("Show the GTK inspector."), + }}, + + .show_on_screen_keyboard => comptime &.{.{ + .action = .show_on_screen_keyboard, + .title = i18n.N_("Show On-Screen Keyboard"), + .description = i18n.N_("Show the on-screen keyboard if present."), + }}, + + .open_config => comptime &.{ + .{ + .action = .{ .open_config = .os_open }, + .title = i18n.N_("Open Config Using OS editor"), + .description = i18n.N_("Open the config file with the OS's default editor."), + }, + .{ + .action = .{ .open_config = .new_window }, + .title = i18n.N_("Open Config in New Terminal Window"), + .description = i18n.N_("Open the config file in a new window using $EDITOR or $VISUAL."), + }, + }, + + .reload_config => comptime &.{.{ + .action = .reload_config, + .title = i18n.N_("Reload Config"), + .description = i18n.N_("Reload the config file."), + }}, + + .close_surface => comptime &.{.{ + .action = .close_surface, + .title = i18n.N_("Close Terminal"), + .description = i18n.N_("Close the current terminal."), + }}, + + .close_tab => comptime &.{ + .{ + .action = .{ .close_tab = .this }, + .title = i18n.N_("Close Tab"), + .description = i18n.N_("Close the current tab."), + }, + .{ + .action = .{ .close_tab = .other }, + .title = i18n.N_("Close Other Tabs"), + .description = i18n.N_("Close all tabs in this window except the current one."), + }, + .{ + .action = .{ .close_tab = .right }, + .title = i18n.N_("Close Tabs to the Right"), + .description = i18n.N_("Close all tabs to the right of the current one."), + }, + }, + + .close_window => comptime &.{.{ + .action = .close_window, + .title = i18n.N_("Close Window"), + .description = i18n.N_("Close the current window."), + }}, + + .close_all_windows => comptime &.{.{ + .action = .close_all_windows, + .title = i18n.N_("Close All Windows"), + .description = i18n.N_("Close all windows."), + }}, + + .toggle_maximize => comptime &.{.{ + .action = .toggle_maximize, + .title = i18n.N_("Toggle Maximize"), + .description = i18n.N_("Toggle the maximized state of the current window."), + }}, + + .toggle_fullscreen => comptime &.{.{ + .action = .toggle_fullscreen, + .title = i18n.N_("Toggle Fullscreen"), + .description = i18n.N_("Toggle the fullscreen state of the current window."), + }}, + + .toggle_window_decorations => comptime &.{.{ + .action = .toggle_window_decorations, + .title = i18n.N_("Toggle Window Decorations"), + .description = i18n.N_("Toggle the window decorations."), + }}, + + .toggle_window_float_on_top => comptime &.{.{ + .action = .toggle_window_float_on_top, + .title = i18n.N_("Toggle Float on Top"), + .description = i18n.N_("Toggle the float on top state of the current window."), + }}, + + .toggle_secure_input => comptime &.{.{ + .action = .toggle_secure_input, + .title = i18n.N_("Toggle Secure Input"), + .description = i18n.N_("Toggle secure input mode."), + }}, + + .toggle_mouse_reporting => comptime &.{.{ + .action = .toggle_mouse_reporting, + .title = i18n.N_("Toggle Mouse Reporting"), + .description = i18n.N_("Toggle whether mouse events are reported to terminal applications."), + }}, + + .toggle_background_opacity => comptime &.{.{ + .action = .toggle_background_opacity, + .title = i18n.N_("Toggle Background Opacity"), + .description = i18n.N_("Toggle the background opacity of a window that started transparent."), + }}, + + .check_for_updates => comptime &.{.{ + .action = .check_for_updates, + .title = i18n.N_("Check for Updates"), + .description = i18n.N_("Check for updates to the application."), + }}, + + .undo => comptime &.{.{ + .action = .undo, + .title = i18n.N_("Undo"), + .description = i18n.N_("Undo the last action."), + }}, + + .redo => comptime &.{.{ + .action = .redo, + .title = i18n.N_("Redo"), + .description = i18n.N_("Redo the last undone action."), + }}, + + .quit => comptime &.{.{ + .action = .quit, + .title = i18n.N_("Quit"), + .description = i18n.N_("Quit the application."), + }}, + + .text => comptime &.{.{ + .action = .{ .text = "👻" }, + .title = i18n.N_("Ghostty"), + .description = i18n.N_("Put a little Ghostty in your terminal."), + }}, + + // No commands because they're parameterized and there + // aren't obvious values users would use. It is possible that + // these may have commands in the future if there are very + // common values that users tend to use. + .csi, + .esc, + .cursor_key, + .set_font_size, + .set_surface_title, + .set_tab_title, + .set_window_title, + .search, + .scroll_to_row, + .scroll_page_fractional, + .scroll_page_lines, + .adjust_selection, + .jump_to_prompt, + .write_scrollback_file, + .goto_tab, + .resize_split, + .activate_key_table, + .activate_key_table_once, + .deactivate_key_table, + .deactivate_all_key_tables, + .end_key_sequence, + .crash, + => comptime &.{}, + + // No commands because I'm not sure they make sense in a command + // palette context. + .toggle_command_palette, + .toggle_quick_terminal, + .toggle_visibility, + .previous_tab, + .next_tab, + .last_tab, + => comptime &.{}, + + // No commands for obvious reasons + .ignore, + .unbind, + => comptime &.{}, + }; + + // All generated commands should have the same action as the + // action passed in. + for (result) |cmd| assert(cmd.action == action); + + return result; +} + +test "command defaults" { + // This just ensures that defaults is analyzed and works. + const testing = std.testing; + try testing.expect(defaults.len > 0); + try testing.expectEqual(defaults.len, defaultsC.len); +} |
