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 - A new :opt:`cursor_trail_color` setting to independently control the color of
cursor trails (:pull:`8830`) 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 - Wayland: Fix incorrect window size calculation when transitioning from
full screen to non-full screen with client side decorations (:iss:`8826`) 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 map f1 clear_terminal to_cursor active
# Same as above except cleared lines are moved into scrollback # Same as above except cleared lines are moved into scrollback
map f1 clear_terminal to_cursor_scroll active 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: def clear_terminal(self, action: str, only_active: bool) -> None:
if only_active: if only_active:
@ -1333,6 +1335,9 @@ class Boss:
elif action == 'to_cursor_scroll': elif action == 'to_cursor_scroll':
for w in windows: for w in windows:
w.scroll_prompt_to_top(clear_scrollback=False) w.scroll_prompt_to_top(clear_scrollback=False)
elif action == 'last_command':
for w in windows:
w.screen.erase_last_command()
else: else:
self.show_error(_('Unknown clear type'), _('The clear type: {} is unknown').format(action)) 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_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_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_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[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_MACOS_SECURE_KEYBOARD_ENTRY]) { call_boss(toggle_macos_secure_keyboard_entry, NULL); }
if (cocoa_pending_actions[TOGGLE_FULLSCREEN]) { call_boss(toggle_fullscreen, 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_TERMINAL_AND_SCROLLBACK,
CLEAR_SCROLLBACK, CLEAR_SCROLLBACK,
CLEAR_SCREEN, CLEAR_SCREEN,
CLEAR_LAST_COMMAND,
RELOAD_CONFIG, RELOAD_CONFIG,
TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY, TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY,
TOGGLE_FULLSCREEN, TOGGLE_FULLSCREEN,

View file

@ -255,6 +255,7 @@ PENDING(reset_terminal, RESET_TERMINAL)
PENDING(clear_terminal_and_scrollback, CLEAR_TERMINAL_AND_SCROLLBACK) PENDING(clear_terminal_and_scrollback, CLEAR_TERMINAL_AND_SCROLLBACK)
PENDING(clear_scrollback, CLEAR_SCROLLBACK) PENDING(clear_scrollback, CLEAR_SCROLLBACK)
PENDING(clear_screen, CLEAR_SCREEN) PENDING(clear_screen, CLEAR_SCREEN)
PENDING(clear_last_command, CLEAR_LAST_COMMAND)
PENDING(reload_config, RELOAD_CONFIG) PENDING(reload_config, RELOAD_CONFIG)
PENDING(toggle_macos_secure_keyboard_entry, TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY) PENDING(toggle_macos_secure_keyboard_entry, TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY)
PENDING(toggle_fullscreen, TOGGLE_FULLSCREEN) PENDING(toggle_fullscreen, TOGGLE_FULLSCREEN)
@ -278,6 +279,7 @@ PENDING(quit, QUIT)
item.action == @selector(close_window:) || item.action == @selector(close_window:) ||
item.action == @selector(reset_terminal:) || item.action == @selector(reset_terminal:) ||
item.action == @selector(clear_terminal_and_scrollback:) || item.action == @selector(clear_terminal_and_scrollback:) ||
item.action == @selector(clear_last_command:) ||
item.action == @selector(clear_scrollback:) || item.action == @selector(clear_scrollback:) ||
item.action == @selector(clear_screen:) || item.action == @selector(clear_screen:) ||
item.action == @selector(previous_tab:) || item.action == @selector(previous_tab:) ||
@ -314,7 +316,7 @@ typedef struct {
typedef struct { typedef struct {
GlobalShortcut new_os_window, close_os_window, close_tab, edit_config_file, reload_config; 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 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 toggle_macos_secure_keyboard_entry, toggle_fullscreen, open_kitty_website;
GlobalShortcut hide_macos_app, hide_macos_other_apps, minimize_macos_window, quit; GlobalShortcut hide_macos_app, hide_macos_other_apps, minimize_macos_window, quit;
} GlobalShortcuts; } 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); 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_tab); else Q(next_tab); else Q(previous_tab);
else Q(new_window); else Q(close_window); else Q(reset_terminal); 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(clear_terminal_and_scrollback); else Q(clear_scrollback); else Q(clear_screen); else Q(clear_last_command);
else Q(toggle_macos_secure_keyboard_entry); else Q(toggle_fullscreen); else Q(open_kitty_website); else Q(reload_config); else Q(toggle_macos_secure_keyboard_entry); else Q(toggle_fullscreen);
else Q(hide_macos_app); else Q(hide_macos_other_apps); else Q(minimize_macos_window); else Q(quit); 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 #undef Q
if (gs == NULL) { PyErr_SetString(PyExc_KeyError, "Unknown shortcut name"); return NULL; } if (gs == NULL) { PyErr_SetString(PyExc_KeyError, "Unknown shortcut name"); return NULL; }
int cocoa_mods; 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 to Start", clear_terminal_and_scrollback);
MENU_ITEM(editMenu, @"Clear Scrollback", clear_scrollback); MENU_ITEM(editMenu, @"Clear Scrollback", clear_scrollback);
MENU_ITEM(editMenu, @"Clear Screen", clear_screen); MENU_ITEM(editMenu, @"Clear Screen", clear_screen);
MENU_ITEM(editMenu, @"Clear Last Command", clear_last_command);
[editMenu release]; [editMenu release];
NSMenuItem* windowMenuItem = 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') val = get_macos_shortcut_for(func_map, 'clear_terminal to_cursor_scroll active', lookup_name='clear_screen')
if val is not None: if val is not None:
global_shortcuts['clear_screen'] = val 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') val = get_macos_shortcut_for(func_map, 'load_config_file', lookup_name='reload_config')
if val is not None: if val is not None:
global_shortcuts['reload_config'] = val 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 map f1 clear_terminal to_cursor active
# Same as above except cleared lines are moved into scrollback # Same as above except cleared lines are moved into scrollback
map f1 clear_terminal to_cursor_scroll active 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 If you want to operate on all kitty windows instead of just the current one, use
:italic:`all` instead of :italic:`active`. :italic:`all` instead of :italic:`active`.
@ -4416,6 +4418,11 @@ map('Clear scrollback',
only='macos', only='macos',
) )
map('Clear the last command',
'clear_last_command cmd+l clear_terminal last_command active',
only='macos',
)
map('Clear screen', map('Clear screen',
'clear_screen cmd+ctrl+l clear_terminal to_cursor_scroll active', 'clear_screen cmd+ctrl+l clear_terminal to_cursor_scroll active',
only='macos', 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=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=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=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=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=12, key=44), definition='load_config_file'))
defaults.map.append(KeyDefinition(trigger=SingleKey(mods=10, key=44), definition='debug_config')) 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] args = ['reset', True]
else: else:
action = vals[0].lower() 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') log_error(f'{action} is unknown for clear_terminal, using reset')
action = 'reset' action = 'reset'
args = [action, vals[1].lower() == 'active'] args = [action, vals[1].lower() == 'active']