mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 01:06:08 +03:00
input: defind Command struct and default commands
This commit is contained in:
@ -279,6 +279,12 @@ typedef struct {
|
||||
ghostty_input_mods_e mods;
|
||||
} ghostty_input_trigger_s;
|
||||
|
||||
typedef struct {
|
||||
const char* action;
|
||||
const char* title;
|
||||
const char* description;
|
||||
} ghostty_command_s;
|
||||
|
||||
typedef enum {
|
||||
GHOSTTY_BUILD_MODE_DEBUG,
|
||||
GHOSTTY_BUILD_MODE_RELEASE_SAFE,
|
||||
|
@ -5,6 +5,7 @@ const mouse = @import("input/mouse.zig");
|
||||
const key = @import("input/key.zig");
|
||||
const keyboard = @import("input/keyboard.zig");
|
||||
|
||||
pub const command = @import("input/command.zig");
|
||||
pub const function_keys = @import("input/function_keys.zig");
|
||||
pub const keycodes = @import("input/keycodes.zig");
|
||||
pub const kitty = @import("input/kitty.zig");
|
||||
@ -12,6 +13,7 @@ pub const kitty = @import("input/kitty.zig");
|
||||
pub const ctrlOrSuper = key.ctrlOrSuper;
|
||||
pub const Action = key.Action;
|
||||
pub const Binding = @import("input/Binding.zig");
|
||||
pub const Command = command.Command;
|
||||
pub const Link = @import("input/Link.zig");
|
||||
pub const Key = key.Key;
|
||||
pub const KeyboardLayout = keyboard.Layout;
|
||||
|
@ -1017,15 +1017,6 @@ pub const Action = union(enum) {
|
||||
}
|
||||
};
|
||||
|
||||
// A key for the C API to execute an action. This must be kept in sync
|
||||
// with include/ghostty.h.
|
||||
pub const Key = enum(c_int) {
|
||||
copy_to_clipboard,
|
||||
paste_from_clipboard,
|
||||
new_tab,
|
||||
new_window,
|
||||
};
|
||||
|
||||
/// Trigger is the associated key state that can trigger an action.
|
||||
/// This is an extern struct because this is also used in the C API.
|
||||
///
|
||||
|
393
src/input/command.zig
Normal file
393
src/input/command.zig
Normal file
@ -0,0 +1,393 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Action = @import("binding.zig").Action;
|
||||
|
||||
/// 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: [*:0]const u8,
|
||||
title: [*:0]const u8,
|
||||
description: [*:0]const u8,
|
||||
};
|
||||
|
||||
/// Convert this command to a C struct.
|
||||
pub fn comptimeCval(self: Command) C {
|
||||
assert(@inComptime());
|
||||
|
||||
return .{
|
||||
.action = std.fmt.comptimePrint("{s}", .{self.action}),
|
||||
.title = self.title,
|
||||
.description = self.description,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const defaults: []const Command = defaults: {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
assert(i == count);
|
||||
const final = result;
|
||||
break :defaults &final;
|
||||
};
|
||||
|
||||
/// Defaults in C-compatible form.
|
||||
pub const defaultsC: []const Command.C = defaults: {
|
||||
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 = "Reset Terminal",
|
||||
.description = "Reset the terminal to a clean state.",
|
||||
}},
|
||||
|
||||
.copy_to_clipboard => comptime &.{.{
|
||||
.action = .copy_to_clipboard,
|
||||
.title = "Copy to Clipboard",
|
||||
.description = "Copy the selected text to the clipboard.",
|
||||
}},
|
||||
|
||||
.copy_url_to_clipboard => comptime &.{.{
|
||||
.action = .copy_url_to_clipboard,
|
||||
.title = "Copy URL to Clipboard",
|
||||
.description = "Copy the URL under the cursor to the clipboard.",
|
||||
}},
|
||||
|
||||
.paste_from_clipboard => comptime &.{.{
|
||||
.action = .paste_from_clipboard,
|
||||
.title = "Paste from Clipboard",
|
||||
.description = "Paste the contents of the clipboard.",
|
||||
}},
|
||||
|
||||
.paste_from_selection => comptime &.{.{
|
||||
.action = .paste_from_selection,
|
||||
.title = "Paste from Selection",
|
||||
.description = "Paste the contents of the selection clipboard.",
|
||||
}},
|
||||
|
||||
.increase_font_size => comptime &.{.{
|
||||
.action = .{ .increase_font_size = 1 },
|
||||
.title = "Increase Font Size",
|
||||
.description = "Increase the font size by 1 point.",
|
||||
}},
|
||||
|
||||
.decrease_font_size => comptime &.{.{
|
||||
.action = .{ .decrease_font_size = 1 },
|
||||
.title = "Decrease Font Size",
|
||||
.description = "Decrease the font size by 1 point.",
|
||||
}},
|
||||
|
||||
.reset_font_size => comptime &.{.{
|
||||
.action = .reset_font_size,
|
||||
.title = "Reset Font Size",
|
||||
.description = "Reset the font size to the default.",
|
||||
}},
|
||||
|
||||
.clear_screen => comptime &.{.{
|
||||
.action = .clear_screen,
|
||||
.title = "Clear Screen",
|
||||
.description = "Clear the screen and scrollback.",
|
||||
}},
|
||||
|
||||
.select_all => comptime &.{.{
|
||||
.action = .select_all,
|
||||
.title = "Select All",
|
||||
.description = "Select all text on the screen.",
|
||||
}},
|
||||
|
||||
.scroll_to_top => comptime &.{.{
|
||||
.action = .scroll_to_top,
|
||||
.title = "Scroll to Top",
|
||||
.description = "Scroll to the top of the screen.",
|
||||
}},
|
||||
|
||||
.scroll_to_bottom => comptime &.{.{
|
||||
.action = .scroll_to_bottom,
|
||||
.title = "Scroll to Bottom",
|
||||
.description = "Scroll to the bottom of the screen.",
|
||||
}},
|
||||
|
||||
.scroll_page_up => comptime &.{.{
|
||||
.action = .scroll_page_up,
|
||||
.title = "Scroll Page Up",
|
||||
.description = "Scroll the screen up by a page.",
|
||||
}},
|
||||
|
||||
.scroll_page_down => comptime &.{.{
|
||||
.action = .scroll_page_down,
|
||||
.title = "Scroll Page Down",
|
||||
.description = "Scroll the screen down by a page.",
|
||||
}},
|
||||
|
||||
.write_screen_file => comptime &.{
|
||||
.{
|
||||
.action = .{ .write_screen_file = .paste },
|
||||
.title = "Copy Screen to Temporary File and Paste Path",
|
||||
.description = "Copy the screen contents to a temporary file and paste the path to the file.",
|
||||
},
|
||||
.{
|
||||
.action = .{ .write_screen_file = .open },
|
||||
.title = "Copy Screen to Temporary File and Open",
|
||||
.description = "Copy the screen contents to a temporary file and open it.",
|
||||
},
|
||||
},
|
||||
|
||||
.write_selection_file => comptime &.{
|
||||
.{
|
||||
.action = .{ .write_selection_file = .paste },
|
||||
.title = "Copy Selection to Temporary File and Paste Path",
|
||||
.description = "Copy the selection contents to a temporary file and paste the path to the file.",
|
||||
},
|
||||
.{
|
||||
.action = .{ .write_selection_file = .open },
|
||||
.title = "Copy Selection to Temporary File and Open",
|
||||
.description = "Copy the selection contents to a temporary file and open it.",
|
||||
},
|
||||
},
|
||||
|
||||
.new_window => comptime &.{.{
|
||||
.action = .new_window,
|
||||
.title = "New Window",
|
||||
.description = "Open a new window.",
|
||||
}},
|
||||
|
||||
.new_tab => comptime &.{.{
|
||||
.action = .new_tab,
|
||||
.title = "New Tab",
|
||||
.description = "Open a new tab.",
|
||||
}},
|
||||
|
||||
.move_tab => comptime &.{
|
||||
.{
|
||||
.action = .{ .move_tab = -1 },
|
||||
.title = "Move Tab Left",
|
||||
.description = "Move the current tab to the left.",
|
||||
},
|
||||
.{
|
||||
.action = .{ .move_tab = 1 },
|
||||
.title = "Move Tab Right",
|
||||
.description = "Move the current tab to the right.",
|
||||
},
|
||||
},
|
||||
|
||||
.toggle_tab_overview => comptime &.{.{
|
||||
.action = .toggle_tab_overview,
|
||||
.title = "Toggle Tab Overview",
|
||||
.description = "Toggle the tab overview.",
|
||||
}},
|
||||
|
||||
.prompt_surface_title => comptime &.{.{
|
||||
.action = .prompt_surface_title,
|
||||
.title = "Change Title...",
|
||||
.description = "Prompt for a new title for the current terminal.",
|
||||
}},
|
||||
|
||||
.new_split => comptime &.{
|
||||
.{
|
||||
.action = .{ .new_split = .left },
|
||||
.title = "Split Left",
|
||||
.description = "Split the terminal to the left.",
|
||||
},
|
||||
.{
|
||||
.action = .{ .new_split = .right },
|
||||
.title = "Split Right",
|
||||
.description = "Split the terminal to the right.",
|
||||
},
|
||||
.{
|
||||
.action = .{ .new_split = .up },
|
||||
.title = "Split Up",
|
||||
.description = "Split the terminal up.",
|
||||
},
|
||||
.{
|
||||
.action = .{ .new_split = .down },
|
||||
.title = "Split Down",
|
||||
.description = "Split the terminal down.",
|
||||
},
|
||||
},
|
||||
|
||||
.toggle_split_zoom => comptime &.{.{
|
||||
.action = .toggle_split_zoom,
|
||||
.title = "Toggle Split Zoom",
|
||||
.description = "Toggle the zoom state of the current split.",
|
||||
}},
|
||||
|
||||
.equalize_splits => comptime &.{.{
|
||||
.action = .equalize_splits,
|
||||
.title = "Equalize Splits",
|
||||
.description = "Equalize the size of all splits.",
|
||||
}},
|
||||
|
||||
.reset_window_size => comptime &.{.{
|
||||
.action = .reset_window_size,
|
||||
.title = "Reset Window Size",
|
||||
.description = "Reset the window size to the default.",
|
||||
}},
|
||||
|
||||
.inspector => comptime &.{.{
|
||||
.action = .{ .inspector = .toggle },
|
||||
.title = "Toggle Inspector",
|
||||
.description = "Toggle the inspector.",
|
||||
}},
|
||||
|
||||
.open_config => comptime &.{.{
|
||||
.action = .open_config,
|
||||
.title = "Open Config",
|
||||
.description = "Open the config file.",
|
||||
}},
|
||||
|
||||
.reload_config => comptime &.{.{
|
||||
.action = .reload_config,
|
||||
.title = "Reload Config",
|
||||
.description = "Reload the config file.",
|
||||
}},
|
||||
|
||||
.close_surface => comptime &.{.{
|
||||
.action = .close_surface,
|
||||
.title = "Close Terminal",
|
||||
.description = "Close the current terminal.",
|
||||
}},
|
||||
|
||||
.close_tab => comptime &.{.{
|
||||
.action = .close_tab,
|
||||
.title = "Close Tab",
|
||||
.description = "Close the current tab.",
|
||||
}},
|
||||
|
||||
.close_window => comptime &.{.{
|
||||
.action = .close_window,
|
||||
.title = "Close Window",
|
||||
.description = "Close the current window.",
|
||||
}},
|
||||
|
||||
.close_all_windows => comptime &.{.{
|
||||
.action = .close_all_windows,
|
||||
.title = "Close All Windows",
|
||||
.description = "Close all windows.",
|
||||
}},
|
||||
|
||||
.toggle_maximize => comptime &.{.{
|
||||
.action = .toggle_maximize,
|
||||
.title = "Toggle Maximize",
|
||||
.description = "Toggle the maximized state of the current window.",
|
||||
}},
|
||||
|
||||
.toggle_fullscreen => comptime &.{.{
|
||||
.action = .toggle_fullscreen,
|
||||
.title = "Toggle Fullscreen",
|
||||
.description = "Toggle the fullscreen state of the current window.",
|
||||
}},
|
||||
|
||||
.toggle_window_decorations => comptime &.{.{
|
||||
.action = .toggle_window_decorations,
|
||||
.title = "Toggle Window Decorations",
|
||||
.description = "Toggle the window decorations.",
|
||||
}},
|
||||
|
||||
.toggle_secure_input => comptime &.{.{
|
||||
.action = .toggle_secure_input,
|
||||
.title = "Toggle Secure Input",
|
||||
.description = "Toggle secure input mode.",
|
||||
}},
|
||||
|
||||
.quit => comptime &.{.{
|
||||
.action = .quit,
|
||||
.title = "Quit",
|
||||
.description = "Quit the application.",
|
||||
}},
|
||||
|
||||
// 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,
|
||||
.text,
|
||||
.cursor_key,
|
||||
.scroll_page_fractional,
|
||||
.scroll_page_lines,
|
||||
.adjust_selection,
|
||||
.jump_to_prompt,
|
||||
.write_scrollback_file,
|
||||
.goto_tab,
|
||||
.goto_split,
|
||||
.resize_split,
|
||||
.crash,
|
||||
=> comptime &.{},
|
||||
|
||||
// No commands because I'm not sure they make sense in a command
|
||||
// palette context.
|
||||
.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);
|
||||
}
|
Reference in New Issue
Block a user