diff --git a/docs/changelog.rst b/docs/changelog.rst index 5d24abe58..a1bf08eb5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -210,6 +210,9 @@ To update |kitty|, :doc:`follow the instructions `. --program` option work when using the ``self`` :option:`kitty +kitten hints --linenum-action` (:iss:`3931`) +- Allow defining aliases for more general actions, not just kittens + (:pull:`4260`) + 0.22.2 [2021-08-02] ---------------------- diff --git a/docs/launch.rst b/docs/launch.rst index 27a2498e6..6fb29d64b 100644 --- a/docs/launch.rst +++ b/docs/launch.rst @@ -38,6 +38,18 @@ To pass the contents of the current screen and scrollback to the started process There are many more powerful options, refer to the complete list below. +.. note:: + To avoid duplicating launch actions with frequently used parameters, you can + use :opt:`action_alias` to define launch action aliases. For example:: + + action_alias launch_tab launch --cwd=current --type=tab + map f1 launch_tab vim + map f2 launch_tab emacs + + The :kbd:`F1` key will now open vim in a new tab with the current windows + working directory + + The piping environment -------------------------- diff --git a/docs/open_actions.rst b/docs/open_actions.rst index 6e6d1d8b3..f820bfc94 100644 --- a/docs/open_actions.rst +++ b/docs/open_actions.rst @@ -58,6 +58,12 @@ some special variables, documented below: ``FRAGMENT`` The fragment (unquoted), if any of the URL or the empty string. + +.. note:: + You can use the :opt:`action_alias` option just as in kitty.conf to + define aliases for frequently used actions. + + .. _matching_criteria: Matching criteria diff --git a/kitty/boss.py b/kitty/boss.py index 75b1f9d88..a976ad2ad 100755 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -46,7 +46,7 @@ from .keys import get_shortcut, shortcut_matches from .layout.base import set_layout_options from .notify import notification_activated from .options.types import Options -from .options.utils import MINIMUM_FONT_SIZE, SubSequenceMap +from .options.utils import MINIMUM_FONT_SIZE, KeyMap, SubSequenceMap from .os_window_size import initial_window_size_func from .rgb import color_from_int from .session import Session, create_sessions, get_os_window_sizing_data @@ -62,7 +62,7 @@ from .utils import ( remove_socket_file, safe_print, set_primary_selection, single_instance, startup_notification_handler ) -from .window import MatchPatternType, Window, CommandOutput +from .window import CommandOutput, MatchPatternType, Window class OSWindowDict(TypedDict): @@ -238,7 +238,7 @@ class Boss: self.current_visual_select: Optional[VisualSelect] = None self.startup_cursor_text_color = opts.cursor_text_color self.pending_sequences: Optional[SubSequenceMap] = None - self.default_pending_action: Optional[KeyAction] = None + self.default_pending_action: Tuple[KeyAction, ...] = () self.cached_values = cached_values self.os_window_map: Dict[int, TabManager] = {} self.os_window_death_actions: Dict[int, Callable[[], None]] = {} @@ -259,7 +259,7 @@ class Boss: ) set_boss(self) self.args = args - self.global_shortcuts_map = {v: KeyAction(k) for k, v in global_shortcuts.items()} + self.global_shortcuts_map: KeyMap = {v: (KeyAction(k),) for k, v in global_shortcuts.items()} self.global_shortcuts = global_shortcuts self.mouse_handler: Optional[Callable[[WindowSystemMouseEvent], None]] = None self.update_keymap() @@ -895,7 +895,7 @@ class Boss: t = self.active_tab return None if t is None else t.active_window - def set_pending_sequences(self, sequences: SubSequenceMap, default_pending_action: Optional[KeyAction] = None) -> None: + def set_pending_sequences(self, sequences: SubSequenceMap, default_pending_action: Tuple[KeyAction, ...] = ()) -> None: self.pending_sequences = sequences self.default_pending_action = default_pending_action set_in_sequence_mode(True) @@ -905,17 +905,18 @@ class Boss: key_action = get_shortcut(self.keymap, ev) if key_action is None: sequences = get_shortcut(get_options().sequence_map, ev) - if sequences and not isinstance(sequences, KeyAction): + if sequences and not isinstance(sequences, tuple): self.set_pending_sequences(sequences) return True if self.global_shortcuts_map and get_shortcut(self.global_shortcuts_map, ev): return True - elif isinstance(key_action, KeyAction): - return self.dispatch_action(key_action) + elif isinstance(key_action, tuple): + return self.combine(key_action) return False def clear_pending_sequences(self) -> None: - self.pending_sequences = self.default_pending_action = None + self.pending_sequences = None + self.default_pending_action = () set_in_sequence_mode(False) def process_sequence(self, ev: KeyEvent) -> None: @@ -939,7 +940,7 @@ class Boss: matched_action = matched_action or self.default_pending_action self.clear_pending_sequences() if matched_action is not None: - self.dispatch_action(matched_action) + self.combine(matched_action) def cancel_current_visual_select(self) -> None: if self.current_visual_select: @@ -982,11 +983,11 @@ class Boss: window.screen.set_window_char(ch) self.current_visual_select.window_ids.append(window.id) for mods in (0, GLFW_MOD_CONTROL, GLFW_MOD_CONTROL | GLFW_MOD_SHIFT, GLFW_MOD_SUPER, GLFW_MOD_ALT, GLFW_MOD_SHIFT): - pending_sequences[(SingleKey(mods=mods, key=ord(ch.lower())),)] = ac + pending_sequences[(SingleKey(mods=mods, key=ord(ch.lower())),)] = (ac,) if ch in string.digits: - pending_sequences[(SingleKey(mods=mods, key=fmap[f'KP_{ch}']),)] = ac + pending_sequences[(SingleKey(mods=mods, key=fmap[f'KP_{ch}']),)] = (ac,) if len(self.current_visual_select.window_ids) > 1: - self.set_pending_sequences(pending_sequences, default_pending_action=KeyAction('visual_window_select_action_trigger', (0,))) + self.set_pending_sequences(pending_sequences, default_pending_action=(KeyAction('visual_window_select_action_trigger', (0,)),)) redirect_mouse_handling(True) self.mouse_handler = self.visual_window_select_mouse_handler else: @@ -1130,9 +1131,12 @@ class Boss: map kitty_mod+e combine : new_window : next_layout ''') - def combine(self, *actions: KeyAction) -> None: + def combine(self, actions: Tuple[KeyAction, ...], window_for_dispatch: Optional[Window] = None, dispatch_type: str = 'KeyPress') -> bool: + consumed = False for key_action in actions: - self.dispatch_action(key_action) + if self.dispatch_action(key_action, window_for_dispatch, dispatch_type): + consumed = True + return consumed def on_focus(self, os_window_id: int, focused: bool) -> None: tm = self.os_window_map.get(os_window_id) @@ -1363,10 +1367,7 @@ class Boss: return overlay_window @ac('misc', 'Run the specified kitten. See :doc:`/kittens/custom` for details') - def kitten(self, kitten: str, *args: str) -> None: - import shlex - cmdline = args[0] if args else '' - kargs = shlex.split(cmdline) if cmdline else [] + def kitten(self, kitten: str, *kargs: str) -> None: self._run_kitten(kitten, kargs) def run_kitten(self, kitten: str, *args: str) -> None: diff --git a/kitty/config.py b/kitty/config.py index fff56a019..a9937de6b 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -11,7 +11,8 @@ from .conf.utils import BadLine, load_config as _load_config, parse_config_base from .constants import cache_dir, defconf from .options.types import Options, defaults, option_names from .options.utils import ( - KeyDefinition, KeyMap, MouseMap, MouseMapping, SequenceMap + ActionAlias, KeyDefinition, KeyMap, MouseMap, MouseMapping, SequenceMap, + build_action_aliases ) from .typing import TypedDict from .utils import log_error @@ -21,9 +22,6 @@ def option_names_for_completion() -> Tuple[str, ...]: return option_names -no_op_actions = frozenset({'noop', 'no-op', 'no_op'}) - - def build_ansi_color_table(opts: Optional[Options] = None) -> int: if opts is None: opts = defaults @@ -93,18 +91,18 @@ def prepare_config_file_for_editing() -> str: return defconf -def finalize_keys(opts: Options) -> None: +def finalize_keys(opts: Options, alias_map: Dict[str, ActionAlias]) -> None: defns: List[KeyDefinition] = [] for d in opts.map: if d is None: # clear_all_shortcuts defns = [] # type: ignore else: - defns.append(d.resolve_and_copy(opts.kitty_mod, opts.kitten_alias)) + defns.append(d.resolve_and_copy(opts.kitty_mod, alias_map)) keymap: KeyMap = {} sequence_map: SequenceMap = {} for defn in defns: - is_no_op = defn.action.func in no_op_actions + is_no_op = defn.is_no_op if defn.is_sequence: keymap.pop(defn.trigger, None) s = sequence_map.setdefault(defn.trigger, {}) @@ -113,32 +111,32 @@ def finalize_keys(opts: Options) -> None: if not s: del sequence_map[defn.trigger] else: - s[defn.rest] = defn.action + s[defn.rest] = defn.actions else: sequence_map.pop(defn.trigger, None) if is_no_op: keymap.pop(defn.trigger, None) else: - keymap[defn.trigger] = defn.action + keymap[defn.trigger] = defn.actions opts.keymap = keymap opts.sequence_map = sequence_map -def finalize_mouse_mappings(opts: Options) -> None: +def finalize_mouse_mappings(opts: Options, alias_map: Dict[str, ActionAlias]) -> None: defns: List[MouseMapping] = [] for d in opts.mouse_map: if d is None: # clear_all_mouse_actions defns = [] # type: ignore else: - defns.append(d.resolve_and_copy(opts.kitty_mod, opts.kitten_alias)) + defns.append(d.resolve_and_copy(opts.kitty_mod, alias_map)) mousemap: MouseMap = {} for defn in defns: - is_no_op = defn.action.func in no_op_actions + is_no_op = defn.is_no_op if is_no_op: mousemap.pop(defn.trigger, None) else: - mousemap[defn.trigger] = defn.action + mousemap[defn.trigger] = defn.actions opts.mousemap = mousemap @@ -161,10 +159,13 @@ def load_config(*paths: str, overrides: Optional[Iterable[str]] = None, accumula opts_dict, paths = _load_config(defaults, partial(parse_config, accumulate_bad_lines=accumulate_bad_lines), merge_result_dicts, *paths, overrides=overrides) opts = Options(opts_dict) - finalize_keys(opts) - finalize_mouse_mappings(opts) + alias_map = build_action_aliases(opts.kitten_alias, 'kitten') + alias_map.update(build_action_aliases(opts.action_alias)) + finalize_keys(opts, alias_map) + finalize_mouse_mappings(opts, alias_map) # delete no longer needed definitions, replacing with empty placeholders opts.kitten_alias = {} + opts.action_alias = {} opts.mouse_map = [] opts.map = [] if opts.background_opacity < 1.0 and opts.macos_titlebar_color: diff --git a/kitty/debug_config.py b/kitty/debug_config.py index c096ab966..e1b956137 100644 --- a/kitty/debug_config.py +++ b/kitty/debug_config.py @@ -27,7 +27,7 @@ from .rgb import color_as_sharp from .types import MouseEvent, SingleKey from .typing import SequenceMap -ShortcutMap = Dict[Tuple[SingleKey, ...], KeyAction] +ShortcutMap = Dict[Tuple[SingleKey, ...], Tuple[KeyAction, ...]] def green(x: str) -> str: @@ -54,7 +54,7 @@ def mod_to_names(mods: int) -> Generator[str, None, None]: yield name -def print_shortcut(key_sequence: Iterable[SingleKey], action: KeyAction, print: Callable[..., None]) -> None: +def print_shortcut(key_sequence: Iterable[SingleKey], actions: Iterable[KeyAction], print: Callable[..., None]) -> None: from .fast_data_types import glfw_get_key_name keys = [] for key_spec in key_sequence: @@ -66,7 +66,8 @@ def print_shortcut(key_sequence: Iterable[SingleKey], action: KeyAction, print: names.append(kname or f'{key}') keys.append('+'.join(names)) - print('\t' + ' > '.join(keys), action) + for action in actions: + print('\t' + ' > '.join(keys), action) def print_shortcut_changes(defns: ShortcutMap, text: str, changes: Set[Tuple[SingleKey, ...]], print: Callable[..., None]) -> None: @@ -87,7 +88,7 @@ def compare_keymaps(final: ShortcutMap, initial: ShortcutMap, print: Callable[.. def flatten_sequence_map(m: SequenceMap) -> ShortcutMap: - ans: Dict[Tuple[SingleKey, ...], KeyAction] = {} + ans = {} for key_spec, rest_map in m.items(): for r, action in rest_map.items(): ans[(key_spec,) + (r)] = action @@ -99,11 +100,12 @@ def compare_mousemaps(final: MouseMap, initial: MouseMap, print: Callable[..., N removed = set(initial) - set(final) changed = {k for k in set(final) & set(initial) if final[k] != initial[k]} - def print_mouse_action(trigger: MouseEvent, action: KeyAction) -> None: + def print_mouse_action(trigger: MouseEvent, actions: Tuple[KeyAction, ...]) -> None: names = list(mod_to_names(trigger.mods)) + [f'b{trigger.button+1}'] when = {-1: 'repeat', 1: 'press', 2: 'doublepress', 3: 'triplepress'}.get(trigger.repeat_count, trigger.repeat_count) grabbed = 'grabbed' if trigger.grabbed else 'ungrabbed' - print('\t', '+'.join(names), when, grabbed, action) + for action in actions: + print('\t', '+'.join(names), when, grabbed, action) def print_changes(defns: MouseMap, changes: Set[MouseEvent], text: str) -> None: if changes: diff --git a/kitty/keys.py b/kitty/keys.py index 9cb485a45..13f27e8e8 100644 --- a/kitty/keys.py +++ b/kitty/keys.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # License: GPL v3 Copyright: 2016, Kovid Goyal -from typing import Optional, Union +from typing import Optional, Union, Tuple from .conf.utils import KeyAction from .fast_data_types import ( @@ -22,7 +22,7 @@ def keyboard_mode_name(screen: ScreenType) -> str: return 'application' if screen.cursor_key_mode else 'normal' -def get_shortcut(keymap: Union[KeyMap, SequenceMap], ev: KeyEvent) -> Optional[Union[KeyAction, SubSequenceMap]]: +def get_shortcut(keymap: Union[KeyMap, SequenceMap], ev: KeyEvent) -> Optional[Union[Tuple[KeyAction, ...], SubSequenceMap]]: mods = ev.mods & mod_mask ans = keymap.get(SingleKey(mods, False, ev.key)) if ans is None and ev.shifted_key and mods & GLFW_MOD_SHIFT: diff --git a/kitty/main.py b/kitty/main.py index 21e48158f..8d49b834b 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -112,8 +112,10 @@ def get_macos_shortcut_for(opts: Options, function: str = 'new_os_window', args: ans = None candidates = [] for k, v in opts.keymap.items(): - if v.func == function and v.args == args: - candidates.append(k) + if len(v) == 1: + q = v[0] + if q.func == function and q.args == args: + candidates.append(k) if candidates: from .fast_data_types import cocoa_set_global_shortcut alt_mods = GLFW_MOD_ALT, GLFW_MOD_ALT | GLFW_MOD_SHIFT diff --git a/kitty/open_actions.py b/kitty/open_actions.py index c79601c1a..b2837d44d 100644 --- a/kitty/open_actions.py +++ b/kitty/open_actions.py @@ -6,14 +6,16 @@ import os import posixpath from contextlib import suppress from typing import ( - Any, Generator, Iterable, List, NamedTuple, Optional, Tuple, cast + Any, Dict, Iterable, Iterator, List, NamedTuple, Optional, Tuple, cast ) from urllib.parse import ParseResult, unquote, urlparse from .conf.utils import KeyAction, to_cmdline_implementation from .constants import config_dir from .guess_mime_type import guess_type -from .options.utils import parse_key_action +from .options.utils import ( + ActionAlias, action_alias, parse_key_actions, resolve_aliases_in_action +) from .types import run_once from .typing import MatchType from .utils import expandvars, log_error @@ -29,9 +31,11 @@ class OpenAction(NamedTuple): actions: Tuple[KeyAction, ...] -def parse(lines: Iterable[str]) -> Generator[OpenAction, None, None]: +def parse(lines: Iterable[str]) -> Iterator[OpenAction]: match_criteria: List[MatchCriteria] = [] actions: List[KeyAction] = [] + alias_map: Dict[str, ActionAlias] = {} + entries = [] for line in lines: line = line.strip() @@ -39,7 +43,7 @@ def parse(lines: Iterable[str]) -> Generator[OpenAction, None, None]: continue if not line: if match_criteria and actions: - yield OpenAction(tuple(match_criteria), tuple(actions)) + entries.append((tuple(match_criteria), tuple(actions))) match_criteria = [] actions = [] continue @@ -50,18 +54,22 @@ def parse(lines: Iterable[str]) -> Generator[OpenAction, None, None]: key = key.lower() if key == 'action': with to_cmdline_implementation.filter_env_vars('URL', 'FILE_PATH', 'FILE', 'FRAGMENT'): - x = parse_key_action(rest) - if x is not None: - actions.append(x) + for x in parse_key_actions(rest): + actions.append(x) elif key in ('mime', 'ext', 'protocol', 'file', 'path', 'url', 'fragment_matches'): if key != 'url': rest = rest.lower() match_criteria.append(MatchCriteria(cast(MatchType, key), rest)) + elif key == 'action_alias': + for (alias_name, args) in action_alias(rest): + alias_map[alias_name] = ActionAlias(args[0], args=tuple(args[1:])) else: log_error(f'Ignoring malformed open actions line: {line}') + for (mc, ac) in entries: + yield OpenAction(mc, tuple(resolve_aliases_in_action(a, alias_map) for a in ac)) if match_criteria and actions: - yield OpenAction(tuple(match_criteria), tuple(actions)) + yield OpenAction(tuple(match_criteria), tuple(resolve_aliases_in_action(a, alias_map) for a in actions)) def url_matches_criterion(purl: 'ParseResult', url: str, unquoted_path: str, mc: MatchCriteria) -> bool: @@ -142,7 +150,7 @@ def url_matches_criteria(purl: 'ParseResult', url: str, unquoted_path: str, crit return True -def actions_for_url_from_list(url: str, actions: Iterable[OpenAction]) -> Generator[KeyAction, None, None]: +def actions_for_url_from_list(url: str, actions: Iterable[OpenAction]) -> Iterator[KeyAction]: try: purl = urlparse(url) except Exception: @@ -184,7 +192,7 @@ def load_open_actions() -> Tuple[OpenAction, ...]: return tuple(parse(f)) -def actions_for_url(url: str, actions_spec: Optional[str] = None) -> Generator[KeyAction, None, None]: +def actions_for_url(url: str, actions_spec: Optional[str] = None) -> Iterator[KeyAction]: if actions_spec is None: actions = load_open_actions() else: diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 76b99fcfa..52eb475ab 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -2870,15 +2870,23 @@ for instance, to remove the default shortcuts. ''' ) +opt('+action_alias', 'launch_tab launch --type=tab --cwd=current', + option_type='action_alias', + add_to_default=False, + long_text=''' +Define aliases to avoid repeating the same options in multiple mappings. Aliases +can be defined for the :ref:`launch `, :ref:`kitten ` +and :ref:`action-remote_control` actions. For example, the above alias allows you +to create mappings to launch a new tab without duplication:: + + map f1 launch_tab vim + map f2 launch_tab emacs +''') + opt('+kitten_alias', 'hints hints --hints-offset=0', option_type='kitten_alias', add_to_default=False, - long_text=''' -You can create aliases for kitten names, this allows overriding the defaults for -kitten options and can also be used to shorten repeated mappings of the same -kitten with a specific group of options. For example, the above alias changes -the default value of :option:`kitty +kitten hints --hints-offset` to zero for -all mappings, including the builtin ones. + long_text='''Deprecated, use :opt:`action_alias` instead. ''' ) diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 69e1424f9..8bf2b6176 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -6,13 +6,13 @@ from kitty.conf.utils import ( unit_float ) from kitty.options.utils import ( - active_tab_title_template, adjust_baseline, adjust_line_height, allow_hyperlinks, + action_alias, active_tab_title_template, adjust_baseline, adjust_line_height, allow_hyperlinks, allow_remote_control, box_drawing_scale, clear_all_mouse_actions, clear_all_shortcuts, clipboard_control, config_or_absolute_path, copy_on_select, cursor_text_color, deprecated_hide_window_decorations_aliases, deprecated_macos_show_window_title_in_menubar_alias, deprecated_send_text, disable_ligatures, edge_width, env, font_features, hide_window_decorations, - kitten_alias, macos_option_as_alt, macos_titlebar_color, optional_edge_width, parse_map, - parse_mouse_map, resize_draw_strategy, scrollback_lines, scrollback_pager_history_size, symbol_map, + macos_option_as_alt, macos_titlebar_color, optional_edge_width, parse_map, parse_mouse_map, + resize_draw_strategy, scrollback_lines, scrollback_pager_history_size, symbol_map, tab_activity_symbol, tab_bar_edge, tab_bar_margin_height, tab_bar_min_tabs, tab_fade, tab_font_style, tab_separator, tab_title_template, to_cursor_shape, to_font_size, to_layout_names, to_modifiers, url_prefixes, url_style, visual_window_select_characters, watcher, @@ -22,6 +22,10 @@ from kitty.options.utils import ( class Parser: + def action_alias(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + for k, v in action_alias(val): + ans["action_alias"][k] = v + def active_border_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['active_border_color'] = to_color_or_none(val) @@ -1006,7 +1010,7 @@ class Parser: ans['italic_font'] = str(val) def kitten_alias(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: - for k, v in kitten_alias(val): + for k, v in action_alias(val): ans["kitten_alias"][k] = v def kitty_mod(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: @@ -1309,6 +1313,7 @@ class Parser: def create_result_dict() -> typing.Dict[str, typing.Any]: return { + 'action_alias': {}, 'env': {}, 'font_features': {}, 'kitten_alias': {}, diff --git a/kitty/options/types.py b/kitty/options/types.py index 63df3109a..c45879e4b 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -43,6 +43,7 @@ else: choices_for_tab_switch_strategy = str option_names = ( # {{{ + 'action_alias', 'active_border_color', 'active_tab_background', 'active_tab_font_style', @@ -583,6 +584,7 @@ class Options: window_padding_width: FloatEdges = FloatEdges(left=0, top=0, right=0, bottom=0) window_resize_step_cells: int = 2 window_resize_step_lines: int = 2 + action_alias: typing.Dict[str, typing.List[str]] = {} env: typing.Dict[str, str] = {} font_features: typing.Dict[str, typing.Tuple[kitty.fonts.FontFeature, ...]] = {} kitten_alias: typing.Dict[str, typing.List[str]] = {} @@ -698,6 +700,7 @@ class Options: defaults = Options() +defaults.action_alias = {} defaults.env = {} defaults.font_features = {} defaults.kitten_alias = {} @@ -705,252 +708,252 @@ defaults.symbol_map = {} defaults.watcher = {} defaults.map = [ # copy_to_clipboard - KeyDefinition(False, KeyAction('copy_to_clipboard'), 1024, False, 99, ()), + KeyDefinition(False, (KeyAction('copy_to_clipboard'),), 1024, False, 99, ()), # paste_from_clipboard - KeyDefinition(False, KeyAction('paste_from_clipboard'), 1024, False, 118, ()), + KeyDefinition(False, (KeyAction('paste_from_clipboard'),), 1024, False, 118, ()), # paste_from_selection - KeyDefinition(False, KeyAction('paste_from_selection'), 1024, False, 115, ()), + KeyDefinition(False, (KeyAction('paste_from_selection'),), 1024, False, 115, ()), # paste_from_selection - KeyDefinition(False, KeyAction('paste_from_selection'), 1, False, 57348, ()), + KeyDefinition(False, (KeyAction('paste_from_selection'),), 1, False, 57348, ()), # pass_selection_to_program - KeyDefinition(False, KeyAction('pass_selection_to_program'), 1024, False, 111, ()), + KeyDefinition(False, (KeyAction('pass_selection_to_program'),), 1024, False, 111, ()), # scroll_line_up - KeyDefinition(False, KeyAction('scroll_line_up'), 1024, False, 57352, ()), + KeyDefinition(False, (KeyAction('scroll_line_up'),), 1024, False, 57352, ()), # scroll_line_up - KeyDefinition(False, KeyAction('scroll_line_up'), 1024, False, 107, ()), + KeyDefinition(False, (KeyAction('scroll_line_up'),), 1024, False, 107, ()), # scroll_line_down - KeyDefinition(False, KeyAction('scroll_line_down'), 1024, False, 57353, ()), + KeyDefinition(False, (KeyAction('scroll_line_down'),), 1024, False, 57353, ()), # scroll_line_down - KeyDefinition(False, KeyAction('scroll_line_down'), 1024, False, 106, ()), + KeyDefinition(False, (KeyAction('scroll_line_down'),), 1024, False, 106, ()), # scroll_page_up - KeyDefinition(False, KeyAction('scroll_page_up'), 1024, False, 57354, ()), + KeyDefinition(False, (KeyAction('scroll_page_up'),), 1024, False, 57354, ()), # scroll_page_down - KeyDefinition(False, KeyAction('scroll_page_down'), 1024, False, 57355, ()), + KeyDefinition(False, (KeyAction('scroll_page_down'),), 1024, False, 57355, ()), # scroll_home - KeyDefinition(False, KeyAction('scroll_home'), 1024, False, 57356, ()), + KeyDefinition(False, (KeyAction('scroll_home'),), 1024, False, 57356, ()), # scroll_end - KeyDefinition(False, KeyAction('scroll_end'), 1024, False, 57357, ()), + KeyDefinition(False, (KeyAction('scroll_end'),), 1024, False, 57357, ()), # scroll_to_previous_prompt - KeyDefinition(False, KeyAction('scroll_to_prompt', (-1,)), 1024, False, 122, ()), + KeyDefinition(False, (KeyAction('scroll_to_prompt', (-1,)),), 1024, False, 122, ()), # scroll_to_next_prompt - KeyDefinition(False, KeyAction('scroll_to_prompt', (1,)), 1024, False, 120, ()), + KeyDefinition(False, (KeyAction('scroll_to_prompt', (1,)),), 1024, False, 120, ()), # show_scrollback - KeyDefinition(False, KeyAction('show_scrollback'), 1024, False, 104, ()), + KeyDefinition(False, (KeyAction('show_scrollback'),), 1024, False, 104, ()), # show_last_command_output - KeyDefinition(False, KeyAction('show_last_command_output'), 1024, False, 103, ()), + KeyDefinition(False, (KeyAction('show_last_command_output'),), 1024, False, 103, ()), # new_window - KeyDefinition(False, KeyAction('new_window'), 1024, False, 57345, ()), + KeyDefinition(False, (KeyAction('new_window'),), 1024, False, 57345, ()), # new_os_window - KeyDefinition(False, KeyAction('new_os_window'), 1024, False, 110, ()), + KeyDefinition(False, (KeyAction('new_os_window'),), 1024, False, 110, ()), # close_window - KeyDefinition(False, KeyAction('close_window'), 1024, False, 119, ()), + KeyDefinition(False, (KeyAction('close_window'),), 1024, False, 119, ()), # next_window - KeyDefinition(False, KeyAction('next_window'), 1024, False, 93, ()), + KeyDefinition(False, (KeyAction('next_window'),), 1024, False, 93, ()), # previous_window - KeyDefinition(False, KeyAction('previous_window'), 1024, False, 91, ()), + KeyDefinition(False, (KeyAction('previous_window'),), 1024, False, 91, ()), # move_window_forward - KeyDefinition(False, KeyAction('move_window_forward'), 1024, False, 102, ()), + KeyDefinition(False, (KeyAction('move_window_forward'),), 1024, False, 102, ()), # move_window_backward - KeyDefinition(False, KeyAction('move_window_backward'), 1024, False, 98, ()), + KeyDefinition(False, (KeyAction('move_window_backward'),), 1024, False, 98, ()), # move_window_to_top - KeyDefinition(False, KeyAction('move_window_to_top'), 1024, False, 96, ()), + KeyDefinition(False, (KeyAction('move_window_to_top'),), 1024, False, 96, ()), # start_resizing_window - KeyDefinition(False, KeyAction('start_resizing_window'), 1024, False, 114, ()), + KeyDefinition(False, (KeyAction('start_resizing_window'),), 1024, False, 114, ()), # first_window - KeyDefinition(False, KeyAction('first_window'), 1024, False, 49, ()), + KeyDefinition(False, (KeyAction('first_window'),), 1024, False, 49, ()), # second_window - KeyDefinition(False, KeyAction('second_window'), 1024, False, 50, ()), + KeyDefinition(False, (KeyAction('second_window'),), 1024, False, 50, ()), # third_window - KeyDefinition(False, KeyAction('third_window'), 1024, False, 51, ()), + KeyDefinition(False, (KeyAction('third_window'),), 1024, False, 51, ()), # fourth_window - KeyDefinition(False, KeyAction('fourth_window'), 1024, False, 52, ()), + KeyDefinition(False, (KeyAction('fourth_window'),), 1024, False, 52, ()), # fifth_window - KeyDefinition(False, KeyAction('fifth_window'), 1024, False, 53, ()), + KeyDefinition(False, (KeyAction('fifth_window'),), 1024, False, 53, ()), # sixth_window - KeyDefinition(False, KeyAction('sixth_window'), 1024, False, 54, ()), + KeyDefinition(False, (KeyAction('sixth_window'),), 1024, False, 54, ()), # seventh_window - KeyDefinition(False, KeyAction('seventh_window'), 1024, False, 55, ()), + KeyDefinition(False, (KeyAction('seventh_window'),), 1024, False, 55, ()), # eighth_window - KeyDefinition(False, KeyAction('eighth_window'), 1024, False, 56, ()), + KeyDefinition(False, (KeyAction('eighth_window'),), 1024, False, 56, ()), # ninth_window - KeyDefinition(False, KeyAction('ninth_window'), 1024, False, 57, ()), + KeyDefinition(False, (KeyAction('ninth_window'),), 1024, False, 57, ()), # tenth_window - KeyDefinition(False, KeyAction('tenth_window'), 1024, False, 48, ()), + KeyDefinition(False, (KeyAction('tenth_window'),), 1024, False, 48, ()), # focus_visible_window - KeyDefinition(False, KeyAction('focus_visible_window'), 1024, False, 57370, ()), + KeyDefinition(False, (KeyAction('focus_visible_window'),), 1024, False, 57370, ()), # swap_with_window - KeyDefinition(False, KeyAction('swap_with_window'), 1024, False, 57371, ()), + KeyDefinition(False, (KeyAction('swap_with_window'),), 1024, False, 57371, ()), # next_tab - KeyDefinition(False, KeyAction('next_tab'), 1024, False, 57351, ()), + KeyDefinition(False, (KeyAction('next_tab'),), 1024, False, 57351, ()), # next_tab - KeyDefinition(False, KeyAction('next_tab'), 4, False, 57346, ()), + KeyDefinition(False, (KeyAction('next_tab'),), 4, False, 57346, ()), # previous_tab - KeyDefinition(False, KeyAction('previous_tab'), 1024, False, 57350, ()), + KeyDefinition(False, (KeyAction('previous_tab'),), 1024, False, 57350, ()), # previous_tab - KeyDefinition(False, KeyAction('previous_tab'), 5, False, 57346, ()), + KeyDefinition(False, (KeyAction('previous_tab'),), 5, False, 57346, ()), # new_tab - KeyDefinition(False, KeyAction('new_tab'), 1024, False, 116, ()), + KeyDefinition(False, (KeyAction('new_tab'),), 1024, False, 116, ()), # close_tab - KeyDefinition(False, KeyAction('close_tab'), 1024, False, 113, ()), + KeyDefinition(False, (KeyAction('close_tab'),), 1024, False, 113, ()), # move_tab_forward - KeyDefinition(False, KeyAction('move_tab_forward'), 1024, False, 46, ()), + KeyDefinition(False, (KeyAction('move_tab_forward'),), 1024, False, 46, ()), # move_tab_backward - KeyDefinition(False, KeyAction('move_tab_backward'), 1024, False, 44, ()), + KeyDefinition(False, (KeyAction('move_tab_backward'),), 1024, False, 44, ()), # set_tab_title - KeyDefinition(False, KeyAction('set_tab_title'), 1026, False, 116, ()), + KeyDefinition(False, (KeyAction('set_tab_title'),), 1026, False, 116, ()), # next_layout - KeyDefinition(False, KeyAction('next_layout'), 1024, False, 108, ()), + KeyDefinition(False, (KeyAction('next_layout'),), 1024, False, 108, ()), # increase_font_size - KeyDefinition(False, KeyAction('change_font_size', (True, '+', 2.0)), 1024, False, 61, ()), + KeyDefinition(False, (KeyAction('change_font_size', (True, '+', 2.0)),), 1024, False, 61, ()), # increase_font_size - KeyDefinition(False, KeyAction('change_font_size', (True, '+', 2.0)), 1024, False, 43, ()), + KeyDefinition(False, (KeyAction('change_font_size', (True, '+', 2.0)),), 1024, False, 43, ()), # increase_font_size - KeyDefinition(False, KeyAction('change_font_size', (True, '+', 2.0)), 1024, False, 57413, ()), + KeyDefinition(False, (KeyAction('change_font_size', (True, '+', 2.0)),), 1024, False, 57413, ()), # decrease_font_size - KeyDefinition(False, KeyAction('change_font_size', (True, '-', 2.0)), 1024, False, 45, ()), + KeyDefinition(False, (KeyAction('change_font_size', (True, '-', 2.0)),), 1024, False, 45, ()), # decrease_font_size - KeyDefinition(False, KeyAction('change_font_size', (True, '-', 2.0)), 1024, False, 57412, ()), + KeyDefinition(False, (KeyAction('change_font_size', (True, '-', 2.0)),), 1024, False, 57412, ()), # reset_font_size - KeyDefinition(False, KeyAction('change_font_size', (True, None, 0.0)), 1024, False, 57347, ()), + KeyDefinition(False, (KeyAction('change_font_size', (True, None, 0.0)),), 1024, False, 57347, ()), # open_url - KeyDefinition(False, KeyAction('open_url_with_hints'), 1024, False, 101, ()), + KeyDefinition(False, (KeyAction('open_url_with_hints'),), 1024, False, 101, ()), # insert_selected_path - KeyDefinition(True, KeyAction('kitten', ('hints', '--type path --program -')), 1024, False, 112, (SingleKey(mods=0, is_native=False, key=102),)), + KeyDefinition(True, (KeyAction('kitten', ('hints', '--type', 'path', '--program', '-')),), 1024, False, 112, (SingleKey(mods=0, is_native=False, key=102),)), # open_selected_path - KeyDefinition(True, KeyAction('kitten', ('hints', '--type path')), 1024, False, 112, (SingleKey(mods=1, is_native=False, key=102),)), + KeyDefinition(True, (KeyAction('kitten', ('hints', '--type', 'path')),), 1024, False, 112, (SingleKey(mods=1, is_native=False, key=102),)), # insert_selected_line - KeyDefinition(True, KeyAction('kitten', ('hints', '--type line --program -')), 1024, False, 112, (SingleKey(mods=0, is_native=False, key=108),)), + KeyDefinition(True, (KeyAction('kitten', ('hints', '--type', 'line', '--program', '-')),), 1024, False, 112, (SingleKey(mods=0, is_native=False, key=108),)), # insert_selected_word - KeyDefinition(True, KeyAction('kitten', ('hints', '--type word --program -')), 1024, False, 112, (SingleKey(mods=0, is_native=False, key=119),)), + KeyDefinition(True, (KeyAction('kitten', ('hints', '--type', 'word', '--program', '-')),), 1024, False, 112, (SingleKey(mods=0, is_native=False, key=119),)), # insert_selected_hash - KeyDefinition(True, KeyAction('kitten', ('hints', '--type hash --program -')), 1024, False, 112, (SingleKey(mods=0, is_native=False, key=104),)), + KeyDefinition(True, (KeyAction('kitten', ('hints', '--type', 'hash', '--program', '-')),), 1024, False, 112, (SingleKey(mods=0, is_native=False, key=104),)), # goto_file_line - KeyDefinition(True, KeyAction('kitten', ('hints', '--type linenum')), 1024, False, 112, (SingleKey(mods=0, is_native=False, key=110),)), + KeyDefinition(True, (KeyAction('kitten', ('hints', '--type', 'linenum')),), 1024, False, 112, (SingleKey(mods=0, is_native=False, key=110),)), # open_selected_hyperlink - KeyDefinition(True, KeyAction('kitten', ('hints', '--type hyperlink')), 1024, False, 112, (SingleKey(mods=0, is_native=False, key=121),)), + KeyDefinition(True, (KeyAction('kitten', ('hints', '--type', 'hyperlink')),), 1024, False, 112, (SingleKey(mods=0, is_native=False, key=121),)), # toggle_fullscreen - KeyDefinition(False, KeyAction('toggle_fullscreen'), 1024, False, 57374, ()), + KeyDefinition(False, (KeyAction('toggle_fullscreen'),), 1024, False, 57374, ()), # toggle_maximized - KeyDefinition(False, KeyAction('toggle_maximized'), 1024, False, 57373, ()), + KeyDefinition(False, (KeyAction('toggle_maximized'),), 1024, False, 57373, ()), # input_unicode_character - KeyDefinition(False, KeyAction('kitten', ('unicode_input',)), 1024, False, 117, ()), + KeyDefinition(False, (KeyAction('kitten', ('unicode_input',)),), 1024, False, 117, ()), # edit_config_file - KeyDefinition(False, KeyAction('edit_config_file'), 1024, False, 57365, ()), + KeyDefinition(False, (KeyAction('edit_config_file'),), 1024, False, 57365, ()), # kitty_shell - KeyDefinition(False, KeyAction('kitty_shell', ('window',)), 1024, False, 57344, ()), + KeyDefinition(False, (KeyAction('kitty_shell', ('window',)),), 1024, False, 57344, ()), # increase_background_opacity - KeyDefinition(True, KeyAction('set_background_opacity', ('+0.1',)), 1024, False, 97, (SingleKey(mods=0, is_native=False, key=109),)), + KeyDefinition(True, (KeyAction('set_background_opacity', ('+0.1',)),), 1024, False, 97, (SingleKey(mods=0, is_native=False, key=109),)), # decrease_background_opacity - KeyDefinition(True, KeyAction('set_background_opacity', ('-0.1',)), 1024, False, 97, (SingleKey(mods=0, is_native=False, key=108),)), + KeyDefinition(True, (KeyAction('set_background_opacity', ('-0.1',)),), 1024, False, 97, (SingleKey(mods=0, is_native=False, key=108),)), # full_background_opacity - KeyDefinition(True, KeyAction('set_background_opacity', ('1',)), 1024, False, 97, (SingleKey(mods=0, is_native=False, key=49),)), + KeyDefinition(True, (KeyAction('set_background_opacity', ('1',)),), 1024, False, 97, (SingleKey(mods=0, is_native=False, key=49),)), # reset_background_opacity - KeyDefinition(True, KeyAction('set_background_opacity', ('default',)), 1024, False, 97, (SingleKey(mods=0, is_native=False, key=100),)), + KeyDefinition(True, (KeyAction('set_background_opacity', ('default',)),), 1024, False, 97, (SingleKey(mods=0, is_native=False, key=100),)), # reset_terminal - KeyDefinition(False, KeyAction('clear_terminal', ('reset', True)), 1024, False, 57349, ()), + KeyDefinition(False, (KeyAction('clear_terminal', ('reset', True)),), 1024, False, 57349, ()), # reload_config_file - KeyDefinition(False, KeyAction('load_config_file'), 1024, False, 57368, ()), + KeyDefinition(False, (KeyAction('load_config_file'),), 1024, False, 57368, ()), # debug_config - KeyDefinition(False, KeyAction('debug_config'), 1024, False, 57369, ()), + KeyDefinition(False, (KeyAction('debug_config'),), 1024, False, 57369, ()), ] if is_macos: - defaults.map.append(KeyDefinition(False, KeyAction('copy_to_clipboard'), 8, False, 99, ())) - defaults.map.append(KeyDefinition(False, KeyAction('paste_from_clipboard'), 8, False, 118, ())) - defaults.map.append(KeyDefinition(False, KeyAction('scroll_line_up'), 10, False, 57354, ())) - defaults.map.append(KeyDefinition(False, KeyAction('scroll_line_up'), 8, False, 57352, ())) - defaults.map.append(KeyDefinition(False, KeyAction('scroll_line_down'), 10, False, 57355, ())) - defaults.map.append(KeyDefinition(False, KeyAction('scroll_line_down'), 8, False, 57353, ())) - defaults.map.append(KeyDefinition(False, KeyAction('scroll_page_up'), 8, False, 57354, ())) - defaults.map.append(KeyDefinition(False, KeyAction('scroll_page_down'), 8, False, 57355, ())) - defaults.map.append(KeyDefinition(False, KeyAction('scroll_home'), 8, False, 57356, ())) - defaults.map.append(KeyDefinition(False, KeyAction('scroll_end'), 8, False, 57357, ())) - defaults.map.append(KeyDefinition(False, KeyAction('new_window'), 8, False, 57345, ())) - defaults.map.append(KeyDefinition(False, KeyAction('new_os_window'), 8, False, 110, ())) - defaults.map.append(KeyDefinition(False, KeyAction('close_window'), 9, False, 100, ())) - defaults.map.append(KeyDefinition(False, KeyAction('start_resizing_window'), 8, False, 114, ())) - defaults.map.append(KeyDefinition(False, KeyAction('first_window'), 8, False, 49, ())) - defaults.map.append(KeyDefinition(False, KeyAction('second_window'), 8, False, 50, ())) - defaults.map.append(KeyDefinition(False, KeyAction('third_window'), 8, False, 51, ())) - defaults.map.append(KeyDefinition(False, KeyAction('fourth_window'), 8, False, 52, ())) - defaults.map.append(KeyDefinition(False, KeyAction('fifth_window'), 8, False, 53, ())) - defaults.map.append(KeyDefinition(False, KeyAction('sixth_window'), 8, False, 54, ())) - defaults.map.append(KeyDefinition(False, KeyAction('seventh_window'), 8, False, 55, ())) - defaults.map.append(KeyDefinition(False, KeyAction('eighth_window'), 8, False, 56, ())) - defaults.map.append(KeyDefinition(False, KeyAction('ninth_window'), 8, False, 57, ())) - defaults.map.append(KeyDefinition(False, KeyAction('next_tab'), 9, False, 93, ())) - defaults.map.append(KeyDefinition(False, KeyAction('previous_tab'), 9, False, 91, ())) - defaults.map.append(KeyDefinition(False, KeyAction('new_tab'), 8, False, 116, ())) - defaults.map.append(KeyDefinition(False, KeyAction('close_tab'), 8, False, 119, ())) - defaults.map.append(KeyDefinition(False, KeyAction('close_os_window'), 9, False, 119, ())) - defaults.map.append(KeyDefinition(False, KeyAction('set_tab_title'), 9, False, 105, ())) - defaults.map.append(KeyDefinition(False, KeyAction('change_font_size', (True, '+', 2.0)), 8, False, 43, ())) - defaults.map.append(KeyDefinition(False, KeyAction('change_font_size', (True, '+', 2.0)), 8, False, 61, ())) - defaults.map.append(KeyDefinition(False, KeyAction('change_font_size', (True, '+', 2.0)), 9, False, 61, ())) - defaults.map.append(KeyDefinition(False, KeyAction('change_font_size', (True, '-', 2.0)), 8, False, 45, ())) - defaults.map.append(KeyDefinition(False, KeyAction('change_font_size', (True, '-', 2.0)), 9, False, 45, ())) - defaults.map.append(KeyDefinition(False, KeyAction('change_font_size', (True, None, 0.0)), 8, False, 48, ())) - defaults.map.append(KeyDefinition(False, KeyAction('kitten', ('unicode_input',)), 12, False, 32, ())) - defaults.map.append(KeyDefinition(False, KeyAction('edit_config_file'), 8, False, 44, ())) - defaults.map.append(KeyDefinition(False, KeyAction('clear_terminal', ('reset', True)), 10, False, 114, ())) - defaults.map.append(KeyDefinition(False, KeyAction('load_config_file'), 12, False, 44, ())) - defaults.map.append(KeyDefinition(False, KeyAction('debug_config'), 10, False, 44, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('copy_to_clipboard'),), 8, False, 99, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('paste_from_clipboard'),), 8, False, 118, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('scroll_line_up'),), 10, False, 57354, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('scroll_line_up'),), 8, False, 57352, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('scroll_line_down'),), 10, False, 57355, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('scroll_line_down'),), 8, False, 57353, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('scroll_page_up'),), 8, False, 57354, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('scroll_page_down'),), 8, False, 57355, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('scroll_home'),), 8, False, 57356, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('scroll_end'),), 8, False, 57357, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('new_window'),), 8, False, 57345, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('new_os_window'),), 8, False, 110, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('close_window'),), 9, False, 100, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('start_resizing_window'),), 8, False, 114, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('first_window'),), 8, False, 49, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('second_window'),), 8, False, 50, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('third_window'),), 8, False, 51, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('fourth_window'),), 8, False, 52, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('fifth_window'),), 8, False, 53, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('sixth_window'),), 8, False, 54, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('seventh_window'),), 8, False, 55, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('eighth_window'),), 8, False, 56, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('ninth_window'),), 8, False, 57, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('next_tab'),), 9, False, 93, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('previous_tab'),), 9, False, 91, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('new_tab'),), 8, False, 116, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('close_tab'),), 8, False, 119, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('close_os_window'),), 9, False, 119, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('set_tab_title'),), 9, False, 105, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('change_font_size', (True, '+', 2.0)),), 8, False, 43, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('change_font_size', (True, '+', 2.0)),), 8, False, 61, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('change_font_size', (True, '+', 2.0)),), 9, False, 61, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('change_font_size', (True, '-', 2.0)),), 8, False, 45, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('change_font_size', (True, '-', 2.0)),), 9, False, 45, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('change_font_size', (True, None, 0.0)),), 8, False, 48, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('kitten', ('unicode_input',)),), 12, False, 32, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('edit_config_file'),), 8, False, 44, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('clear_terminal', ('reset', True)),), 10, False, 114, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('load_config_file'),), 12, False, 44, ())) + defaults.map.append(KeyDefinition(False, (KeyAction('debug_config'),), 10, False, 44, ())) defaults.mouse_map = [ # click_url_or_select - MouseMapping(0, 0, -2, False, KeyAction('mouse_handle_click', ('selection', 'link', 'prompt'))), + MouseMapping(0, 0, -2, False, (KeyAction('mouse_handle_click', ('selection', 'link', 'prompt')),)), # click_url_or_select_grabbed - MouseMapping(0, 1, -2, True, KeyAction('mouse_handle_click', ('selection', 'link', 'prompt'))), + MouseMapping(0, 1, -2, True, (KeyAction('mouse_handle_click', ('selection', 'link', 'prompt')),)), # click_url_or_select_grabbed - MouseMapping(0, 1, -2, False, KeyAction('mouse_handle_click', ('selection', 'link', 'prompt'))), + MouseMapping(0, 1, -2, False, (KeyAction('mouse_handle_click', ('selection', 'link', 'prompt')),)), # click_url - MouseMapping(0, 5, -1, True, KeyAction('mouse_handle_click', ('link',))), + MouseMapping(0, 5, -1, True, (KeyAction('mouse_handle_click', ('link',)),)), # click_url - MouseMapping(0, 5, -1, False, KeyAction('mouse_handle_click', ('link',))), + MouseMapping(0, 5, -1, False, (KeyAction('mouse_handle_click', ('link',)),)), # click_url_discard - MouseMapping(0, 5, 1, True, KeyAction('discard_event')), + MouseMapping(0, 5, 1, True, (KeyAction('discard_event'),)), # paste_selection - MouseMapping(2, 0, -1, False, KeyAction('paste_from_selection')), + MouseMapping(2, 0, -1, False, (KeyAction('paste_from_selection'),)), # start_simple_selection - MouseMapping(0, 0, 1, False, KeyAction('mouse_selection', (0,))), + MouseMapping(0, 0, 1, False, (KeyAction('mouse_selection', (0,)),)), # start_rectangle_selection - MouseMapping(0, 6, 1, False, KeyAction('mouse_selection', (2,))), + MouseMapping(0, 6, 1, False, (KeyAction('mouse_selection', (2,)),)), # select_word - MouseMapping(0, 0, 2, False, KeyAction('mouse_selection', (3,))), + MouseMapping(0, 0, 2, False, (KeyAction('mouse_selection', (3,)),)), # select_line - MouseMapping(0, 0, 3, False, KeyAction('mouse_selection', (4,))), + MouseMapping(0, 0, 3, False, (KeyAction('mouse_selection', (4,)),)), # select_line_from_point - MouseMapping(0, 6, 3, False, KeyAction('mouse_selection', (5,))), + MouseMapping(0, 6, 3, False, (KeyAction('mouse_selection', (5,)),)), # extend_selection - MouseMapping(1, 0, 1, False, KeyAction('mouse_selection', (1,))), + MouseMapping(1, 0, 1, False, (KeyAction('mouse_selection', (1,)),)), # paste_selection_grabbed - MouseMapping(2, 1, -1, True, KeyAction('paste_selection')), + MouseMapping(2, 1, -1, True, (KeyAction('paste_selection'),)), # paste_selection_grabbed - MouseMapping(2, 1, -1, False, KeyAction('paste_selection')), + MouseMapping(2, 1, -1, False, (KeyAction('paste_selection'),)), # paste_selection_grabbed - MouseMapping(2, 1, 1, True, KeyAction('discard_event')), + MouseMapping(2, 1, 1, True, (KeyAction('discard_event'),)), # start_simple_selection_grabbed - MouseMapping(0, 1, 1, True, KeyAction('mouse_selection', (0,))), + MouseMapping(0, 1, 1, True, (KeyAction('mouse_selection', (0,)),)), # start_simple_selection_grabbed - MouseMapping(0, 1, 1, False, KeyAction('mouse_selection', (0,))), + MouseMapping(0, 1, 1, False, (KeyAction('mouse_selection', (0,)),)), # start_rectangle_selection_grabbed - MouseMapping(0, 7, 1, True, KeyAction('mouse_selection', (2,))), + MouseMapping(0, 7, 1, True, (KeyAction('mouse_selection', (2,)),)), # start_rectangle_selection_grabbed - MouseMapping(0, 7, 1, False, KeyAction('mouse_selection', (2,))), + MouseMapping(0, 7, 1, False, (KeyAction('mouse_selection', (2,)),)), # select_word_grabbed - MouseMapping(0, 1, 2, True, KeyAction('mouse_selection', (3,))), + MouseMapping(0, 1, 2, True, (KeyAction('mouse_selection', (3,)),)), # select_word_grabbed - MouseMapping(0, 1, 2, False, KeyAction('mouse_selection', (3,))), + MouseMapping(0, 1, 2, False, (KeyAction('mouse_selection', (3,)),)), # select_line_grabbed - MouseMapping(0, 1, 3, True, KeyAction('mouse_selection', (4,))), + MouseMapping(0, 1, 3, True, (KeyAction('mouse_selection', (4,)),)), # select_line_grabbed - MouseMapping(0, 1, 3, False, KeyAction('mouse_selection', (4,))), + MouseMapping(0, 1, 3, False, (KeyAction('mouse_selection', (4,)),)), # select_line_from_point_grabbed - MouseMapping(0, 7, 3, True, KeyAction('mouse_selection', (5,))), + MouseMapping(0, 7, 3, True, (KeyAction('mouse_selection', (5,)),)), # select_line_from_point_grabbed - MouseMapping(0, 7, 3, False, KeyAction('mouse_selection', (5,))), + MouseMapping(0, 7, 3, False, (KeyAction('mouse_selection', (5,)),)), # extend_selection_grabbed - MouseMapping(1, 1, 1, True, KeyAction('mouse_selection', (1,))), + MouseMapping(1, 1, 1, True, (KeyAction('mouse_selection', (1,)),)), # extend_selection_grabbed - MouseMapping(1, 1, 1, False, KeyAction('mouse_selection', (1,))), + MouseMapping(1, 1, 1, False, (KeyAction('mouse_selection', (1,)),)), # show_clicked_cmd_output_ungrabbed - MouseMapping(1, 5, 1, False, KeyAction('mouse_show_command_output')), + MouseMapping(1, 5, 1, False, (KeyAction('mouse_show_command_output'),)), ] diff --git a/kitty/options/utils.py b/kitty/options/utils.py index e8344b726..25a82dd1f 100644 --- a/kitty/options/utils.py +++ b/kitty/options/utils.py @@ -6,17 +6,19 @@ import os import re import sys from typing import ( - Any, Callable, Container, Dict, Iterable, List, NamedTuple, Optional, - Sequence, Tuple, Union + Any, Callable, Container, Dict, Iterable, Iterator, List, NamedTuple, + Optional, Sequence, Tuple, Union ) import kitty.fast_data_types as defines from kitty.conf.utils import ( - KeyAction, KeyFuncWrapper, positive_float, positive_int, - python_string, to_bool, to_cmdline, to_color, uniq, unit_float + KeyAction, KeyFuncWrapper, positive_float, positive_int, python_string, + to_bool, to_cmdline, to_color, uniq, unit_float ) from kitty.constants import config_dir, is_macos -from kitty.fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, Color +from kitty.fast_data_types import ( + CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, Color +) from kitty.fonts import FontFeature from kitty.key_names import ( character_key_name_aliases, functional_key_name_aliases, @@ -26,10 +28,10 @@ from kitty.rgb import color_as_int from kitty.types import FloatEdges, MouseEvent, SingleKey from kitty.utils import expandvars, log_error -KeyMap = Dict[SingleKey, KeyAction] -MouseMap = Dict[MouseEvent, KeyAction] +KeyMap = Dict[SingleKey, Tuple[KeyAction, ...]] +MouseMap = Dict[MouseEvent, Tuple[KeyAction, ...]] KeySequence = Tuple[SingleKey, ...] -SubSequenceMap = Dict[KeySequence, KeyAction] +SubSequenceMap = Dict[KeySequence, Tuple[KeyAction, ...]] SequenceMap = Dict[SingleKey, SubSequenceMap] MINIMUM_FONT_SIZE = 4 default_tab_separator = ' ┇' @@ -58,14 +60,6 @@ def shlex_parse(func: str, rest: str) -> FuncArgsType: return func, to_cmdline(rest) -@func_with_args('combine') -def combine_parse(func: str, rest: str) -> FuncArgsType: - sep, rest = rest.split(maxsplit=1) - parts = re.split(r'\s*' + re.escape(sep) + r'\s*', rest) - args = tuple(map(parse_key_action, filter(None, parts))) - return func, args - - def parse_send_text_bytes(text: str) -> bytes: return python_string(text).encode('utf-8') @@ -91,7 +85,7 @@ def kitten_parse(func: str, rest: str) -> FuncArgsType: else: args = rest.split(maxsplit=2)[1:] func = 'kitten' - return func, args + return func, [args[0]] + (to_cmdline(args[1]) if len(args) > 1 else []) @func_with_args('goto_tab') @@ -772,11 +766,14 @@ def watcher(val: str, current_val: Container[str]) -> Iterable[Tuple[str, str]]: yield val, val -def kitten_alias(val: str) -> Iterable[Tuple[str, List[str]]]: - parts = val.split(maxsplit=2) - if len(parts) >= 2: - name = parts.pop(0) - yield name, parts +def action_alias(val: str) -> Iterable[Tuple[str, List[str]]]: + parts = val.split(maxsplit=1) + if len(parts) > 1: + alias_name, rest = parts + yield alias_name, to_cmdline(rest) + + +kitten_alias = action_alias def symbol_map(val: str) -> Iterable[Tuple[Tuple[int, int], str]]: @@ -806,57 +803,89 @@ def symbol_map(val: str) -> Iterable[Tuple[Tuple[int, int], str]]: yield (a, b), family -def parse_key_action(action: str, action_type: str = 'map') -> Optional[KeyAction]: +def parse_combine(rest: str, action_type: str = 'map') -> Iterator[KeyAction]: + sep, rest = rest.split(maxsplit=1) + parts = re.split(r'\s*' + re.escape(sep) + r'\s*', rest) + for x in parts: + if x: + yield from parse_key_actions(x, action_type) + + +def parse_key_actions(action: str, action_type: str = 'map') -> Iterator[KeyAction]: parts = action.strip().split(maxsplit=1) func = parts[0] if len(parts) == 1: - return KeyAction(func, ()) + yield KeyAction(func, ()) + return rest = parts[1] - parser = func_with_args.get(func) - if parser is not None: - try: - func, args = parser(func, rest) - except Exception as err: - log_error(f'Ignoring invalid {action_type} action: {action} with err: {err}') - else: - return KeyAction(func, tuple(args)) + if func == 'combine': + yield from parse_combine(rest, action_type) else: - log_error(f'Ignoring unknown {action_type} action: {action}') - return None + parser = func_with_args.get(func) + if parser is not None: + try: + func, args = parser(func, rest) + except Exception as err: + log_error(f'Ignoring invalid {action_type} action: {action} with err: {err}') + else: + yield KeyAction(func, tuple(args)) + else: + log_error(f'Ignoring unknown {action_type} action: {action}') + + +class ActionAlias(NamedTuple): + func_name: str + args: Tuple[str, ...] + second_arg_test: Optional[Callable[[Any], bool]] = None + + +def build_action_aliases(raw: Dict[str, List[str]], first_arg_replacement: str = '') -> Dict[str, List[ActionAlias]]: + ans: Dict[str, List[ActionAlias]] = {} + if first_arg_replacement: + for alias_name, args in raw.items(): + ans.setdefault('kitten', []).append(ActionAlias('kitten', tuple(args), alias_name.__eq__)) + else: + for alias_name, args in raw.items(): + ans[alias_name] = [ActionAlias(args[0], tuple(args[1:]))] + return ans + + +def resolve_aliases_in_action(action: KeyAction, aliases: Dict[str, List[ActionAlias]]) -> KeyAction: + for alias in aliases.get(action.func, ()): + if alias.second_arg_test is None: + return action._replace(func=alias.func_name, args=alias.args) + if action.args and alias.second_arg_test(action.args[0]): + return action._replace(func=alias.func_name, args=alias.args) + return action class BaseDefinition: - action: KeyAction + actions: Tuple[KeyAction, ...] + no_op_actions = frozenset(('noop', 'no-op', 'no_op')) - def resolve_kitten_aliases(self, aliases: Dict[str, List[str]]) -> KeyAction: - if not self.action.args or not aliases: - return self.action - kitten = self.action.args[0] - rest = str(self.action.args[1] if len(self.action.args) > 1 else '') - changed = False - for key, expanded in aliases.items(): - if key == kitten: - changed = True - kitten = expanded[0] - if len(expanded) > 1: - rest = expanded[1] + ' ' + rest - return self.action._replace(args=(kitten, rest.rstrip())) if changed else self.action + @property + def is_no_op(self) -> bool: + return len(self.actions) == 1 and self.actions[0].func in self.no_op_actions + + def resolve_aliases(self, aliases: Dict[str, List[ActionAlias]]) -> Tuple[KeyAction, ...]: + self.actions = tuple(resolve_aliases_in_action(a, aliases) for a in self.actions) + return self.actions class MouseMapping(BaseDefinition): - def __init__(self, button: int, mods: int, repeat_count: int, grabbed: bool, action: KeyAction): + def __init__(self, button: int, mods: int, repeat_count: int, grabbed: bool, actions: Tuple[KeyAction, ...]): self.button = button self.mods = mods self.repeat_count = repeat_count self.grabbed = grabbed - self.action = action + self.actions = actions def __repr__(self) -> str: - return f'MouseMapping({self.button}, {self.mods}, {self.repeat_count}, {self.grabbed}, {self.action})' + return f'MouseMapping({self.button}, {self.mods}, {self.repeat_count}, {self.grabbed}, {self.actions})' - def resolve_and_copy(self, kitty_mod: int, aliases: Dict[str, List[str]]) -> 'MouseMapping': - return MouseMapping(self.button, defines.resolve_key_mods(kitty_mod, self.mods), self.repeat_count, self.grabbed, self.resolve_kitten_aliases(aliases)) + def resolve_and_copy(self, kitty_mod: int, aliases: Dict[str, List[ActionAlias]]) -> 'MouseMapping': + return MouseMapping(self.button, defines.resolve_key_mods(kitty_mod, self.mods), self.repeat_count, self.grabbed, self.resolve_aliases(aliases)) @property def trigger(self) -> MouseEvent: @@ -865,21 +894,21 @@ class MouseMapping(BaseDefinition): class KeyDefinition(BaseDefinition): - def __init__(self, is_sequence: bool, action: KeyAction, mods: int, is_native: bool, key: int, rest: Tuple[SingleKey, ...] = ()): + def __init__(self, is_sequence: bool, actions: Tuple[KeyAction, ...], mods: int, is_native: bool, key: int, rest: Tuple[SingleKey, ...] = ()): self.is_sequence = is_sequence - self.action = action + self.actions = actions self.trigger = SingleKey(mods, is_native, key) self.rest = rest def __repr__(self) -> str: - return f'KeyDefinition({self.is_sequence}, {self.action}, {self.trigger.mods}, {self.trigger.is_native}, {self.trigger.key}, {self.rest})' + return f'KeyDefinition({self.is_sequence}, {self.actions}, {self.trigger.mods}, {self.trigger.is_native}, {self.trigger.key}, {self.rest})' - def resolve_and_copy(self, kitty_mod: int, aliases: Dict[str, List[str]]) -> 'KeyDefinition': + def resolve_and_copy(self, kitty_mod: int, aliases: Dict[str, List[ActionAlias]]) -> 'KeyDefinition': def r(k: SingleKey) -> SingleKey: mods = defines.resolve_key_mods(kitty_mod, k.mods) return k._replace(mods=mods) return KeyDefinition( - self.is_sequence, self.resolve_kitten_aliases(aliases), + self.is_sequence, self.resolve_aliases(aliases), defines.resolve_key_mods(kitty_mod, self.trigger.mods), self.trigger.is_native, self.trigger.key, tuple(map(r, self.rest))) @@ -920,12 +949,11 @@ def parse_map(val: str) -> Iterable[KeyDefinition]: log_error(f'Shortcut: {sc} has unknown key, ignoring') return try: - paction = parse_key_action(action) + paction = tuple(parse_key_actions(action)) except Exception: - log_error('Invalid shortcut action: {}. Ignoring.'.format( - action)) + log_error(f'Invalid shortcut action: {action}. Ignoring.') else: - if paction is not None: + if paction: if is_sequence: if trigger is not None: yield KeyDefinition(True, paction, trigger[0], trigger[1], trigger[2], rest) @@ -965,11 +993,11 @@ def parse_mouse_map(val: str) -> Iterable[MouseMapping]: log_error(f'Mouse modes: {modes} not recognized, ignoring') return try: - paction = parse_key_action(action, 'mouse_map') + paction = tuple(parse_key_actions(action, 'mouse_map')) except Exception: log_error(f'Invalid mouse action: {action}. Ignoring.') return - if paction is None: + if not paction: return for mode in sorted(specified_modes): yield MouseMapping(button, mods, count, mode == 'grabbed', paction) diff --git a/kitty/window.py b/kitty/window.py index f9ccb81ed..cacc3ebd4 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -646,7 +646,7 @@ class Window: action = get_options().mousemap.get(ev) if action is None: return False - return get_boss().dispatch_action(action, window_for_dispatch=self, dispatch_type='MouseEvent') + return get_boss().combine(action, window_for_dispatch=self, dispatch_type='MouseEvent') def open_url(self, url: str, hyperlink_id: int, cwd: Optional[str] = None) -> None: opts = get_options() diff --git a/kitty_tests/__init__.py b/kitty_tests/__init__.py index ddbc96613..1296e9044 100644 --- a/kitty_tests/__init__.py +++ b/kitty_tests/__init__.py @@ -69,11 +69,12 @@ class Callbacks: def on_mouse_event(self, event): ev = MouseEvent(**event) - action = self.opts.mousemap.get(ev) - if action is None: + actions = self.opts.mousemap.get(ev) + if not actions: return False self.current_mouse_button = ev.button - getattr(self, action.func)(*action.args) + for action in actions: + getattr(self, action.func)(*action.args) self.current_mouse_button = 0 return True @@ -115,8 +116,8 @@ class BaseTest(TestCase): if options: final_options.update(options) options = Options(merge_result_dicts(defaults._asdict(), final_options)) - finalize_keys(options) - finalize_mouse_mappings(options) + finalize_keys(options, {}) + finalize_mouse_mappings(options, {}) set_options(options) return options diff --git a/kitty_tests/options.py b/kitty_tests/options.py index 508bc8c30..32fff3db1 100644 --- a/kitty_tests/options.py +++ b/kitty_tests/options.py @@ -31,9 +31,10 @@ class TestConfParsing(BaseTest): return ans def keys_for_func(opts, name): - for key, action in opts.keymap.items(): - if action.func == name: - yield key + for key, actions in opts.keymap.items(): + for action in actions: + if action.func == name: + yield key opts = p('font_size 11.37', 'clear_all_shortcuts y', 'color23 red') self.ae(opts.font_size, 11.37) @@ -51,8 +52,16 @@ class TestConfParsing(BaseTest): self.ae(opts.pointer_shape_when_grabbed, defaults.pointer_shape_when_grabbed) opts = p('env A=1', 'env B=x$A', 'env C=', 'env D', 'clear_all_shortcuts y', 'kitten_alias a b --moo', 'map f1 kitten a') self.ae(opts.env, {'A': '1', 'B': 'x1', 'C': '', 'D': DELETE_ENV_VAR}) - ka = tuple(opts.keymap.values())[0] + ka = tuple(opts.keymap.values())[0][0] + self.ae(ka.func, 'kitten') self.ae(ka.args, ('b', '--moo')) + opts = p('clear_all_shortcuts y', 'action_alias la launch --moo', 'map f1 la') + ka = tuple(opts.keymap.values())[0][0] + self.ae(ka.func, 'launch') + self.ae(ka.args, ('--moo',)) + opts = p('clear_all_shortcuts y', 'action_alias la launch --moo', 'map f1 combine : new_window : la ') + ka = tuple(opts.keymap.values())[0] + self.ae((ka[0].func, ka[1].func), ('new_window', 'launch')) opts = p('kitty_mod alt') self.ae(opts.kitty_mod, to_modifiers('alt')) self.ae(next(keys_for_func(opts, 'next_layout')).mods, opts.kitty_mod)