macOS: Add default mapping and global menubar action to erase last command

This is "Cmd+L" from Terminal.app
This commit is contained in:
Kovid Goyal 2025-08-07 09:15:12 +05:30
parent b0439d4183
commit c86ec79e32
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
9 changed files with 30 additions and 5 deletions

View file

@ -112,6 +112,9 @@ Detailed list of changes
- A new :opt:`cursor_trail_color` setting to independently control the color of
cursor trails (:pull:`8830`)
- macOS: Add the default :kbd:`Cmd+L` mapping from Terminal.app to erase the
last command and its output (:disc:`6040`)
- Wayland: Fix incorrect window size calculation when transitioning from
full screen to non-full screen with client side decorations (:iss:`8826`)

View file

@ -1306,6 +1306,8 @@ class Boss:
map f1 clear_terminal to_cursor active
# Same as above except cleared lines are moved into scrollback
map f1 clear_terminal to_cursor_scroll active
# Erase the last command and its output (needs shell integration to work)
map f1 clear_terminal last_command active
''')
def clear_terminal(self, action: str, only_active: bool) -> None:
if only_active:
@ -1333,6 +1335,9 @@ class Boss:
elif action == 'to_cursor_scroll':
for w in windows:
w.scroll_prompt_to_top(clear_scrollback=False)
elif action == 'last_command':
for w in windows:
w.screen.erase_last_command()
else:
self.show_error(_('Unknown clear type'), _('The clear type: {} is unknown').format(action))

View file

@ -1234,6 +1234,7 @@ process_cocoa_pending_actions(void) {
if (cocoa_pending_actions[CLEAR_TERMINAL_AND_SCROLLBACK]) { call_boss(clear_terminal, "sO", "to_cursor", Py_True ); }
if (cocoa_pending_actions[CLEAR_SCROLLBACK]) { call_boss(clear_terminal, "sO", "scrollback", Py_True ); }
if (cocoa_pending_actions[CLEAR_SCREEN]) { call_boss(clear_terminal, "sO", "to_cursor_scroll", Py_True ); }
if (cocoa_pending_actions[CLEAR_LAST_COMMAND]) { call_boss(clear_terminal, "sO", "last_command", Py_True ); }
if (cocoa_pending_actions[RELOAD_CONFIG]) { call_boss(load_config_file, NULL); }
if (cocoa_pending_actions[TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY]) { call_boss(toggle_macos_secure_keyboard_entry, NULL); }
if (cocoa_pending_actions[TOGGLE_FULLSCREEN]) { call_boss(toggle_fullscreen, NULL); }

View file

@ -27,6 +27,7 @@ typedef enum {
CLEAR_TERMINAL_AND_SCROLLBACK,
CLEAR_SCROLLBACK,
CLEAR_SCREEN,
CLEAR_LAST_COMMAND,
RELOAD_CONFIG,
TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY,
TOGGLE_FULLSCREEN,

View file

@ -255,6 +255,7 @@ PENDING(reset_terminal, RESET_TERMINAL)
PENDING(clear_terminal_and_scrollback, CLEAR_TERMINAL_AND_SCROLLBACK)
PENDING(clear_scrollback, CLEAR_SCROLLBACK)
PENDING(clear_screen, CLEAR_SCREEN)
PENDING(clear_last_command, CLEAR_LAST_COMMAND)
PENDING(reload_config, RELOAD_CONFIG)
PENDING(toggle_macos_secure_keyboard_entry, TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY)
PENDING(toggle_fullscreen, TOGGLE_FULLSCREEN)
@ -278,6 +279,7 @@ PENDING(quit, QUIT)
item.action == @selector(close_window:) ||
item.action == @selector(reset_terminal:) ||
item.action == @selector(clear_terminal_and_scrollback:) ||
item.action == @selector(clear_last_command:) ||
item.action == @selector(clear_scrollback:) ||
item.action == @selector(clear_screen:) ||
item.action == @selector(previous_tab:) ||
@ -314,7 +316,7 @@ typedef struct {
typedef struct {
GlobalShortcut new_os_window, close_os_window, close_tab, edit_config_file, reload_config;
GlobalShortcut previous_tab, next_tab, new_tab, new_window, close_window, reset_terminal;
GlobalShortcut clear_terminal_and_scrollback, clear_screen, clear_scrollback;
GlobalShortcut clear_terminal_and_scrollback, clear_screen, clear_scrollback, clear_last_command;
GlobalShortcut toggle_macos_secure_keyboard_entry, toggle_fullscreen, open_kitty_website;
GlobalShortcut hide_macos_app, hide_macos_other_apps, minimize_macos_window, quit;
} GlobalShortcuts;
@ -331,9 +333,10 @@ cocoa_set_global_shortcut(PyObject *self UNUSED, PyObject *args) {
Q(new_os_window); else Q(close_os_window); else Q(close_tab); else Q(edit_config_file);
else Q(new_tab); else Q(next_tab); else Q(previous_tab);
else Q(new_window); else Q(close_window); else Q(reset_terminal);
else Q(clear_terminal_and_scrollback); else Q(clear_scrollback); else Q(clear_screen); else Q(reload_config);
else Q(toggle_macos_secure_keyboard_entry); else Q(toggle_fullscreen); else Q(open_kitty_website);
else Q(hide_macos_app); else Q(hide_macos_other_apps); else Q(minimize_macos_window); else Q(quit);
else Q(clear_terminal_and_scrollback); else Q(clear_scrollback); else Q(clear_screen); else Q(clear_last_command);
else Q(reload_config); else Q(toggle_macos_secure_keyboard_entry); else Q(toggle_fullscreen);
else Q(open_kitty_website); else Q(hide_macos_app); else Q(hide_macos_other_apps);
else Q(minimize_macos_window); else Q(quit);
#undef Q
if (gs == NULL) { PyErr_SetString(PyExc_KeyError, "Unknown shortcut name"); return NULL; }
int cocoa_mods;
@ -787,6 +790,7 @@ cocoa_create_global_menu(void) {
MENU_ITEM(editMenu, @"Clear to Start", clear_terminal_and_scrollback);
MENU_ITEM(editMenu, @"Clear Scrollback", clear_scrollback);
MENU_ITEM(editMenu, @"Clear Screen", clear_screen);
MENU_ITEM(editMenu, @"Clear Last Command", clear_last_command);
[editMenu release];
NSMenuItem* windowMenuItem =

View file

@ -207,6 +207,9 @@ def set_cocoa_global_shortcuts(opts: Options) -> dict[str, SingleKey]:
val = get_macos_shortcut_for(func_map, 'clear_terminal to_cursor_scroll active', lookup_name='clear_screen')
if val is not None:
global_shortcuts['clear_screen'] = val
val = get_macos_shortcut_for(func_map, 'clear_terminal last_command active', lookup_name='clear_last_command')
if val is not None:
global_shortcuts['clear_last_command'] = val
val = get_macos_shortcut_for(func_map, 'load_config_file', lookup_name='reload_config')
if val is not None:
global_shortcuts['reload_config'] = val

View file

@ -4361,6 +4361,8 @@ You can create shortcuts to clear/reset the terminal. For example::
map f1 clear_terminal to_cursor active
# Same as above except cleared lines are moved into scrollback
map f1 clear_terminal to_cursor_scroll active
# Erase the last command and its output (needs shell integration to work)
map f1 clear_terminal last_command active
If you want to operate on all kitty windows instead of just the current one, use
:italic:`all` instead of :italic:`active`.
@ -4416,6 +4418,11 @@ map('Clear scrollback',
only='macos',
)
map('Clear the last command',
'clear_last_command cmd+l clear_terminal last_command active',
only='macos',
)
map('Clear screen',
'clear_screen cmd+ctrl+l clear_terminal to_cursor_scroll active',
only='macos',

View file

@ -979,6 +979,7 @@ if is_macos:
defaults.map.append(KeyDefinition(trigger=SingleKey(mods=10, key=114), definition='clear_terminal reset active'))
defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=107), definition='clear_terminal to_cursor active'))
defaults.map.append(KeyDefinition(trigger=SingleKey(mods=10, key=107), definition='clear_terminal scrollback active'))
defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=108), definition='clear_terminal last_command active'))
defaults.map.append(KeyDefinition(trigger=SingleKey(mods=12, key=108), definition='clear_terminal to_cursor_scroll active'))
defaults.map.append(KeyDefinition(trigger=SingleKey(mods=12, key=44), definition='load_config_file'))
defaults.map.append(KeyDefinition(trigger=SingleKey(mods=10, key=44), definition='debug_config'))

View file

@ -213,7 +213,7 @@ def clear_terminal(func: str, rest: str) -> FuncArgsType:
args = ['reset', True]
else:
action = vals[0].lower()
if action not in ('reset', 'scroll', 'scrollback', 'clear', 'to_cursor', 'to_cursor_scroll'):
if action not in ('reset', 'scroll', 'scrollback', 'clear', 'to_cursor', 'to_cursor_scroll', 'last_command'):
log_error(f'{action} is unknown for clear_terminal, using reset')
action = 'reset'
args = [action, vals[1].lower() == 'active']