Allow defining aliases for more general actions, not just kittens

Fixes #4260
This commit is contained in:
Kovid Goyal 2021-11-22 19:52:43 +05:30
parent 727c69ffdd
commit aa4fa4cc85
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
16 changed files with 370 additions and 281 deletions

View File

@ -210,6 +210,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
--program` option work when using the ``self`` --program` option work when using the ``self``
:option:`kitty +kitten hints --linenum-action` (:iss:`3931`) :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] 0.22.2 [2021-08-02]
---------------------- ----------------------

View File

@ -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. 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 The piping environment
-------------------------- --------------------------

View File

@ -58,6 +58,12 @@ some special variables, documented below:
``FRAGMENT`` ``FRAGMENT``
The fragment (unquoted), if any of the URL or the empty string. 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:
Matching criteria Matching criteria

View File

@ -46,7 +46,7 @@ from .keys import get_shortcut, shortcut_matches
from .layout.base import set_layout_options from .layout.base import set_layout_options
from .notify import notification_activated from .notify import notification_activated
from .options.types import Options 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 .os_window_size import initial_window_size_func
from .rgb import color_from_int from .rgb import color_from_int
from .session import Session, create_sessions, get_os_window_sizing_data 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, remove_socket_file, safe_print, set_primary_selection, single_instance,
startup_notification_handler startup_notification_handler
) )
from .window import MatchPatternType, Window, CommandOutput from .window import CommandOutput, MatchPatternType, Window
class OSWindowDict(TypedDict): class OSWindowDict(TypedDict):
@ -238,7 +238,7 @@ class Boss:
self.current_visual_select: Optional[VisualSelect] = None self.current_visual_select: Optional[VisualSelect] = None
self.startup_cursor_text_color = opts.cursor_text_color self.startup_cursor_text_color = opts.cursor_text_color
self.pending_sequences: Optional[SubSequenceMap] = None 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.cached_values = cached_values
self.os_window_map: Dict[int, TabManager] = {} self.os_window_map: Dict[int, TabManager] = {}
self.os_window_death_actions: Dict[int, Callable[[], None]] = {} self.os_window_death_actions: Dict[int, Callable[[], None]] = {}
@ -259,7 +259,7 @@ class Boss:
) )
set_boss(self) set_boss(self)
self.args = args 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.global_shortcuts = global_shortcuts
self.mouse_handler: Optional[Callable[[WindowSystemMouseEvent], None]] = None self.mouse_handler: Optional[Callable[[WindowSystemMouseEvent], None]] = None
self.update_keymap() self.update_keymap()
@ -895,7 +895,7 @@ class Boss:
t = self.active_tab t = self.active_tab
return None if t is None else t.active_window 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.pending_sequences = sequences
self.default_pending_action = default_pending_action self.default_pending_action = default_pending_action
set_in_sequence_mode(True) set_in_sequence_mode(True)
@ -905,17 +905,18 @@ class Boss:
key_action = get_shortcut(self.keymap, ev) key_action = get_shortcut(self.keymap, ev)
if key_action is None: if key_action is None:
sequences = get_shortcut(get_options().sequence_map, ev) 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) self.set_pending_sequences(sequences)
return True return True
if self.global_shortcuts_map and get_shortcut(self.global_shortcuts_map, ev): if self.global_shortcuts_map and get_shortcut(self.global_shortcuts_map, ev):
return True return True
elif isinstance(key_action, KeyAction): elif isinstance(key_action, tuple):
return self.dispatch_action(key_action) return self.combine(key_action)
return False return False
def clear_pending_sequences(self) -> None: 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) set_in_sequence_mode(False)
def process_sequence(self, ev: KeyEvent) -> None: def process_sequence(self, ev: KeyEvent) -> None:
@ -939,7 +940,7 @@ class Boss:
matched_action = matched_action or self.default_pending_action matched_action = matched_action or self.default_pending_action
self.clear_pending_sequences() self.clear_pending_sequences()
if matched_action is not None: if matched_action is not None:
self.dispatch_action(matched_action) self.combine(matched_action)
def cancel_current_visual_select(self) -> None: def cancel_current_visual_select(self) -> None:
if self.current_visual_select: if self.current_visual_select:
@ -982,11 +983,11 @@ class Boss:
window.screen.set_window_char(ch) window.screen.set_window_char(ch)
self.current_visual_select.window_ids.append(window.id) 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): 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: 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: 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) redirect_mouse_handling(True)
self.mouse_handler = self.visual_window_select_mouse_handler self.mouse_handler = self.visual_window_select_mouse_handler
else: else:
@ -1130,9 +1131,12 @@ class Boss:
map kitty_mod+e combine : new_window : next_layout 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: 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: def on_focus(self, os_window_id: int, focused: bool) -> None:
tm = self.os_window_map.get(os_window_id) tm = self.os_window_map.get(os_window_id)
@ -1363,10 +1367,7 @@ class Boss:
return overlay_window return overlay_window
@ac('misc', 'Run the specified kitten. See :doc:`/kittens/custom` for details') @ac('misc', 'Run the specified kitten. See :doc:`/kittens/custom` for details')
def kitten(self, kitten: str, *args: str) -> None: def kitten(self, kitten: str, *kargs: str) -> None:
import shlex
cmdline = args[0] if args else ''
kargs = shlex.split(cmdline) if cmdline else []
self._run_kitten(kitten, kargs) self._run_kitten(kitten, kargs)
def run_kitten(self, kitten: str, *args: str) -> None: def run_kitten(self, kitten: str, *args: str) -> None:

View File

@ -11,7 +11,8 @@ from .conf.utils import BadLine, load_config as _load_config, parse_config_base
from .constants import cache_dir, defconf from .constants import cache_dir, defconf
from .options.types import Options, defaults, option_names from .options.types import Options, defaults, option_names
from .options.utils import ( from .options.utils import (
KeyDefinition, KeyMap, MouseMap, MouseMapping, SequenceMap ActionAlias, KeyDefinition, KeyMap, MouseMap, MouseMapping, SequenceMap,
build_action_aliases
) )
from .typing import TypedDict from .typing import TypedDict
from .utils import log_error from .utils import log_error
@ -21,9 +22,6 @@ def option_names_for_completion() -> Tuple[str, ...]:
return option_names return option_names
no_op_actions = frozenset({'noop', 'no-op', 'no_op'})
def build_ansi_color_table(opts: Optional[Options] = None) -> int: def build_ansi_color_table(opts: Optional[Options] = None) -> int:
if opts is None: if opts is None:
opts = defaults opts = defaults
@ -93,18 +91,18 @@ def prepare_config_file_for_editing() -> str:
return defconf return defconf
def finalize_keys(opts: Options) -> None: def finalize_keys(opts: Options, alias_map: Dict[str, ActionAlias]) -> None:
defns: List[KeyDefinition] = [] defns: List[KeyDefinition] = []
for d in opts.map: for d in opts.map:
if d is None: # clear_all_shortcuts if d is None: # clear_all_shortcuts
defns = [] # type: ignore defns = [] # type: ignore
else: 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 = {} keymap: KeyMap = {}
sequence_map: SequenceMap = {} sequence_map: SequenceMap = {}
for defn in defns: 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: if defn.is_sequence:
keymap.pop(defn.trigger, None) keymap.pop(defn.trigger, None)
s = sequence_map.setdefault(defn.trigger, {}) s = sequence_map.setdefault(defn.trigger, {})
@ -113,32 +111,32 @@ def finalize_keys(opts: Options) -> None:
if not s: if not s:
del sequence_map[defn.trigger] del sequence_map[defn.trigger]
else: else:
s[defn.rest] = defn.action s[defn.rest] = defn.actions
else: else:
sequence_map.pop(defn.trigger, None) sequence_map.pop(defn.trigger, None)
if is_no_op: if is_no_op:
keymap.pop(defn.trigger, None) keymap.pop(defn.trigger, None)
else: else:
keymap[defn.trigger] = defn.action keymap[defn.trigger] = defn.actions
opts.keymap = keymap opts.keymap = keymap
opts.sequence_map = sequence_map 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] = [] defns: List[MouseMapping] = []
for d in opts.mouse_map: for d in opts.mouse_map:
if d is None: # clear_all_mouse_actions if d is None: # clear_all_mouse_actions
defns = [] # type: ignore defns = [] # type: ignore
else: 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 = {} mousemap: MouseMap = {}
for defn in defns: 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: if is_no_op:
mousemap.pop(defn.trigger, None) mousemap.pop(defn.trigger, None)
else: else:
mousemap[defn.trigger] = defn.action mousemap[defn.trigger] = defn.actions
opts.mousemap = mousemap 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_dict, paths = _load_config(defaults, partial(parse_config, accumulate_bad_lines=accumulate_bad_lines), merge_result_dicts, *paths, overrides=overrides)
opts = Options(opts_dict) opts = Options(opts_dict)
finalize_keys(opts) alias_map = build_action_aliases(opts.kitten_alias, 'kitten')
finalize_mouse_mappings(opts) 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 # delete no longer needed definitions, replacing with empty placeholders
opts.kitten_alias = {} opts.kitten_alias = {}
opts.action_alias = {}
opts.mouse_map = [] opts.mouse_map = []
opts.map = [] opts.map = []
if opts.background_opacity < 1.0 and opts.macos_titlebar_color: if opts.background_opacity < 1.0 and opts.macos_titlebar_color:

View File

@ -27,7 +27,7 @@ from .rgb import color_as_sharp
from .types import MouseEvent, SingleKey from .types import MouseEvent, SingleKey
from .typing import SequenceMap from .typing import SequenceMap
ShortcutMap = Dict[Tuple[SingleKey, ...], KeyAction] ShortcutMap = Dict[Tuple[SingleKey, ...], Tuple[KeyAction, ...]]
def green(x: str) -> str: def green(x: str) -> str:
@ -54,7 +54,7 @@ def mod_to_names(mods: int) -> Generator[str, None, None]:
yield name 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 from .fast_data_types import glfw_get_key_name
keys = [] keys = []
for key_spec in key_sequence: for key_spec in key_sequence:
@ -66,6 +66,7 @@ def print_shortcut(key_sequence: Iterable[SingleKey], action: KeyAction, print:
names.append(kname or f'{key}') names.append(kname or f'{key}')
keys.append('+'.join(names)) keys.append('+'.join(names))
for action in actions:
print('\t' + ' > '.join(keys), action) print('\t' + ' > '.join(keys), action)
@ -87,7 +88,7 @@ def compare_keymaps(final: ShortcutMap, initial: ShortcutMap, print: Callable[..
def flatten_sequence_map(m: SequenceMap) -> ShortcutMap: def flatten_sequence_map(m: SequenceMap) -> ShortcutMap:
ans: Dict[Tuple[SingleKey, ...], KeyAction] = {} ans = {}
for key_spec, rest_map in m.items(): for key_spec, rest_map in m.items():
for r, action in rest_map.items(): for r, action in rest_map.items():
ans[(key_spec,) + (r)] = action ans[(key_spec,) + (r)] = action
@ -99,10 +100,11 @@ def compare_mousemaps(final: MouseMap, initial: MouseMap, print: Callable[..., N
removed = set(initial) - set(final) removed = set(initial) - set(final)
changed = {k for k in set(final) & set(initial) if final[k] != initial[k]} 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}'] 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) when = {-1: 'repeat', 1: 'press', 2: 'doublepress', 3: 'triplepress'}.get(trigger.repeat_count, trigger.repeat_count)
grabbed = 'grabbed' if trigger.grabbed else 'ungrabbed' grabbed = 'grabbed' if trigger.grabbed else 'ungrabbed'
for action in actions:
print('\t', '+'.join(names), when, grabbed, action) print('\t', '+'.join(names), when, grabbed, action)
def print_changes(defns: MouseMap, changes: Set[MouseEvent], text: str) -> None: def print_changes(defns: MouseMap, changes: Set[MouseEvent], text: str) -> None:

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from typing import Optional, Union from typing import Optional, Union, Tuple
from .conf.utils import KeyAction from .conf.utils import KeyAction
from .fast_data_types import ( 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' 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 mods = ev.mods & mod_mask
ans = keymap.get(SingleKey(mods, False, ev.key)) ans = keymap.get(SingleKey(mods, False, ev.key))
if ans is None and ev.shifted_key and mods & GLFW_MOD_SHIFT: if ans is None and ev.shifted_key and mods & GLFW_MOD_SHIFT:

View File

@ -112,7 +112,9 @@ def get_macos_shortcut_for(opts: Options, function: str = 'new_os_window', args:
ans = None ans = None
candidates = [] candidates = []
for k, v in opts.keymap.items(): for k, v in opts.keymap.items():
if v.func == function and v.args == args: if len(v) == 1:
q = v[0]
if q.func == function and q.args == args:
candidates.append(k) candidates.append(k)
if candidates: if candidates:
from .fast_data_types import cocoa_set_global_shortcut from .fast_data_types import cocoa_set_global_shortcut

View File

@ -6,14 +6,16 @@ import os
import posixpath import posixpath
from contextlib import suppress from contextlib import suppress
from typing import ( 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 urllib.parse import ParseResult, unquote, urlparse
from .conf.utils import KeyAction, to_cmdline_implementation from .conf.utils import KeyAction, to_cmdline_implementation
from .constants import config_dir from .constants import config_dir
from .guess_mime_type import guess_type 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 .types import run_once
from .typing import MatchType from .typing import MatchType
from .utils import expandvars, log_error from .utils import expandvars, log_error
@ -29,9 +31,11 @@ class OpenAction(NamedTuple):
actions: Tuple[KeyAction, ...] actions: Tuple[KeyAction, ...]
def parse(lines: Iterable[str]) -> Generator[OpenAction, None, None]: def parse(lines: Iterable[str]) -> Iterator[OpenAction]:
match_criteria: List[MatchCriteria] = [] match_criteria: List[MatchCriteria] = []
actions: List[KeyAction] = [] actions: List[KeyAction] = []
alias_map: Dict[str, ActionAlias] = {}
entries = []
for line in lines: for line in lines:
line = line.strip() line = line.strip()
@ -39,7 +43,7 @@ def parse(lines: Iterable[str]) -> Generator[OpenAction, None, None]:
continue continue
if not line: if not line:
if match_criteria and actions: if match_criteria and actions:
yield OpenAction(tuple(match_criteria), tuple(actions)) entries.append((tuple(match_criteria), tuple(actions)))
match_criteria = [] match_criteria = []
actions = [] actions = []
continue continue
@ -50,18 +54,22 @@ def parse(lines: Iterable[str]) -> Generator[OpenAction, None, None]:
key = key.lower() key = key.lower()
if key == 'action': if key == 'action':
with to_cmdline_implementation.filter_env_vars('URL', 'FILE_PATH', 'FILE', 'FRAGMENT'): with to_cmdline_implementation.filter_env_vars('URL', 'FILE_PATH', 'FILE', 'FRAGMENT'):
x = parse_key_action(rest) for x in parse_key_actions(rest):
if x is not None:
actions.append(x) actions.append(x)
elif key in ('mime', 'ext', 'protocol', 'file', 'path', 'url', 'fragment_matches'): elif key in ('mime', 'ext', 'protocol', 'file', 'path', 'url', 'fragment_matches'):
if key != 'url': if key != 'url':
rest = rest.lower() rest = rest.lower()
match_criteria.append(MatchCriteria(cast(MatchType, key), rest)) 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: else:
log_error(f'Ignoring malformed open actions line: {line}') 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: 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: 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 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: try:
purl = urlparse(url) purl = urlparse(url)
except Exception: except Exception:
@ -184,7 +192,7 @@ def load_open_actions() -> Tuple[OpenAction, ...]:
return tuple(parse(f)) 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: if actions_spec is None:
actions = load_open_actions() actions = load_open_actions()
else: else:

View File

@ -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 <action-launch>`, :ref:`kitten <action-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', opt('+kitten_alias', 'hints hints --hints-offset=0',
option_type='kitten_alias', option_type='kitten_alias',
add_to_default=False, add_to_default=False,
long_text=''' long_text='''Deprecated, use :opt:`action_alias` instead.
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.
''' '''
) )

13
kitty/options/parse.py generated
View File

@ -6,13 +6,13 @@ from kitty.conf.utils import (
unit_float unit_float
) )
from kitty.options.utils import ( 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, 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, 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_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, 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, macos_option_as_alt, macos_titlebar_color, optional_edge_width, parse_map, parse_mouse_map,
parse_mouse_map, resize_draw_strategy, scrollback_lines, scrollback_pager_history_size, symbol_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_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, 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, to_modifiers, url_prefixes, url_style, visual_window_select_characters, watcher,
@ -22,6 +22,10 @@ from kitty.options.utils import (
class Parser: 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: def active_border_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['active_border_color'] = to_color_or_none(val) ans['active_border_color'] = to_color_or_none(val)
@ -1006,7 +1010,7 @@ class Parser:
ans['italic_font'] = str(val) ans['italic_font'] = str(val)
def kitten_alias(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: 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 ans["kitten_alias"][k] = v
def kitty_mod(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: 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]: def create_result_dict() -> typing.Dict[str, typing.Any]:
return { return {
'action_alias': {},
'env': {}, 'env': {},
'font_features': {}, 'font_features': {},
'kitten_alias': {}, 'kitten_alias': {},

289
kitty/options/types.py generated
View File

@ -43,6 +43,7 @@ else:
choices_for_tab_switch_strategy = str choices_for_tab_switch_strategy = str
option_names = ( # {{{ option_names = ( # {{{
'action_alias',
'active_border_color', 'active_border_color',
'active_tab_background', 'active_tab_background',
'active_tab_font_style', 'active_tab_font_style',
@ -583,6 +584,7 @@ class Options:
window_padding_width: FloatEdges = FloatEdges(left=0, top=0, right=0, bottom=0) window_padding_width: FloatEdges = FloatEdges(left=0, top=0, right=0, bottom=0)
window_resize_step_cells: int = 2 window_resize_step_cells: int = 2
window_resize_step_lines: int = 2 window_resize_step_lines: int = 2
action_alias: typing.Dict[str, typing.List[str]] = {}
env: typing.Dict[str, str] = {} env: typing.Dict[str, str] = {}
font_features: typing.Dict[str, typing.Tuple[kitty.fonts.FontFeature, ...]] = {} font_features: typing.Dict[str, typing.Tuple[kitty.fonts.FontFeature, ...]] = {}
kitten_alias: typing.Dict[str, typing.List[str]] = {} kitten_alias: typing.Dict[str, typing.List[str]] = {}
@ -698,6 +700,7 @@ class Options:
defaults = Options() defaults = Options()
defaults.action_alias = {}
defaults.env = {} defaults.env = {}
defaults.font_features = {} defaults.font_features = {}
defaults.kitten_alias = {} defaults.kitten_alias = {}
@ -705,252 +708,252 @@ defaults.symbol_map = {}
defaults.watcher = {} defaults.watcher = {}
defaults.map = [ defaults.map = [
# copy_to_clipboard # copy_to_clipboard
KeyDefinition(False, KeyAction('copy_to_clipboard'), 1024, False, 99, ()), KeyDefinition(False, (KeyAction('copy_to_clipboard'),), 1024, False, 99, ()),
# paste_from_clipboard # paste_from_clipboard
KeyDefinition(False, KeyAction('paste_from_clipboard'), 1024, False, 118, ()), KeyDefinition(False, (KeyAction('paste_from_clipboard'),), 1024, False, 118, ()),
# paste_from_selection # paste_from_selection
KeyDefinition(False, KeyAction('paste_from_selection'), 1024, False, 115, ()), KeyDefinition(False, (KeyAction('paste_from_selection'),), 1024, False, 115, ()),
# paste_from_selection # 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 # 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 # scroll_line_up
KeyDefinition(False, KeyAction('scroll_line_up'), 1024, False, 57352, ()), KeyDefinition(False, (KeyAction('scroll_line_up'),), 1024, False, 57352, ()),
# scroll_line_up # scroll_line_up
KeyDefinition(False, KeyAction('scroll_line_up'), 1024, False, 107, ()), KeyDefinition(False, (KeyAction('scroll_line_up'),), 1024, False, 107, ()),
# scroll_line_down # scroll_line_down
KeyDefinition(False, KeyAction('scroll_line_down'), 1024, False, 57353, ()), KeyDefinition(False, (KeyAction('scroll_line_down'),), 1024, False, 57353, ()),
# scroll_line_down # scroll_line_down
KeyDefinition(False, KeyAction('scroll_line_down'), 1024, False, 106, ()), KeyDefinition(False, (KeyAction('scroll_line_down'),), 1024, False, 106, ()),
# scroll_page_up # scroll_page_up
KeyDefinition(False, KeyAction('scroll_page_up'), 1024, False, 57354, ()), KeyDefinition(False, (KeyAction('scroll_page_up'),), 1024, False, 57354, ()),
# scroll_page_down # scroll_page_down
KeyDefinition(False, KeyAction('scroll_page_down'), 1024, False, 57355, ()), KeyDefinition(False, (KeyAction('scroll_page_down'),), 1024, False, 57355, ()),
# scroll_home # scroll_home
KeyDefinition(False, KeyAction('scroll_home'), 1024, False, 57356, ()), KeyDefinition(False, (KeyAction('scroll_home'),), 1024, False, 57356, ()),
# scroll_end # scroll_end
KeyDefinition(False, KeyAction('scroll_end'), 1024, False, 57357, ()), KeyDefinition(False, (KeyAction('scroll_end'),), 1024, False, 57357, ()),
# scroll_to_previous_prompt # 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 # 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 # show_scrollback
KeyDefinition(False, KeyAction('show_scrollback'), 1024, False, 104, ()), KeyDefinition(False, (KeyAction('show_scrollback'),), 1024, False, 104, ()),
# show_last_command_output # 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 # new_window
KeyDefinition(False, KeyAction('new_window'), 1024, False, 57345, ()), KeyDefinition(False, (KeyAction('new_window'),), 1024, False, 57345, ()),
# new_os_window # new_os_window
KeyDefinition(False, KeyAction('new_os_window'), 1024, False, 110, ()), KeyDefinition(False, (KeyAction('new_os_window'),), 1024, False, 110, ()),
# close_window # close_window
KeyDefinition(False, KeyAction('close_window'), 1024, False, 119, ()), KeyDefinition(False, (KeyAction('close_window'),), 1024, False, 119, ()),
# next_window # next_window
KeyDefinition(False, KeyAction('next_window'), 1024, False, 93, ()), KeyDefinition(False, (KeyAction('next_window'),), 1024, False, 93, ()),
# previous_window # previous_window
KeyDefinition(False, KeyAction('previous_window'), 1024, False, 91, ()), KeyDefinition(False, (KeyAction('previous_window'),), 1024, False, 91, ()),
# move_window_forward # move_window_forward
KeyDefinition(False, KeyAction('move_window_forward'), 1024, False, 102, ()), KeyDefinition(False, (KeyAction('move_window_forward'),), 1024, False, 102, ()),
# move_window_backward # 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 # 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 # start_resizing_window
KeyDefinition(False, KeyAction('start_resizing_window'), 1024, False, 114, ()), KeyDefinition(False, (KeyAction('start_resizing_window'),), 1024, False, 114, ()),
# first_window # first_window
KeyDefinition(False, KeyAction('first_window'), 1024, False, 49, ()), KeyDefinition(False, (KeyAction('first_window'),), 1024, False, 49, ()),
# second_window # second_window
KeyDefinition(False, KeyAction('second_window'), 1024, False, 50, ()), KeyDefinition(False, (KeyAction('second_window'),), 1024, False, 50, ()),
# third_window # third_window
KeyDefinition(False, KeyAction('third_window'), 1024, False, 51, ()), KeyDefinition(False, (KeyAction('third_window'),), 1024, False, 51, ()),
# fourth_window # fourth_window
KeyDefinition(False, KeyAction('fourth_window'), 1024, False, 52, ()), KeyDefinition(False, (KeyAction('fourth_window'),), 1024, False, 52, ()),
# fifth_window # fifth_window
KeyDefinition(False, KeyAction('fifth_window'), 1024, False, 53, ()), KeyDefinition(False, (KeyAction('fifth_window'),), 1024, False, 53, ()),
# sixth_window # sixth_window
KeyDefinition(False, KeyAction('sixth_window'), 1024, False, 54, ()), KeyDefinition(False, (KeyAction('sixth_window'),), 1024, False, 54, ()),
# seventh_window # seventh_window
KeyDefinition(False, KeyAction('seventh_window'), 1024, False, 55, ()), KeyDefinition(False, (KeyAction('seventh_window'),), 1024, False, 55, ()),
# eighth_window # eighth_window
KeyDefinition(False, KeyAction('eighth_window'), 1024, False, 56, ()), KeyDefinition(False, (KeyAction('eighth_window'),), 1024, False, 56, ()),
# ninth_window # ninth_window
KeyDefinition(False, KeyAction('ninth_window'), 1024, False, 57, ()), KeyDefinition(False, (KeyAction('ninth_window'),), 1024, False, 57, ()),
# tenth_window # tenth_window
KeyDefinition(False, KeyAction('tenth_window'), 1024, False, 48, ()), KeyDefinition(False, (KeyAction('tenth_window'),), 1024, False, 48, ()),
# focus_visible_window # focus_visible_window
KeyDefinition(False, KeyAction('focus_visible_window'), 1024, False, 57370, ()), KeyDefinition(False, (KeyAction('focus_visible_window'),), 1024, False, 57370, ()),
# swap_with_window # swap_with_window
KeyDefinition(False, KeyAction('swap_with_window'), 1024, False, 57371, ()), KeyDefinition(False, (KeyAction('swap_with_window'),), 1024, False, 57371, ()),
# next_tab # next_tab
KeyDefinition(False, KeyAction('next_tab'), 1024, False, 57351, ()), KeyDefinition(False, (KeyAction('next_tab'),), 1024, False, 57351, ()),
# next_tab # next_tab
KeyDefinition(False, KeyAction('next_tab'), 4, False, 57346, ()), KeyDefinition(False, (KeyAction('next_tab'),), 4, False, 57346, ()),
# previous_tab # previous_tab
KeyDefinition(False, KeyAction('previous_tab'), 1024, False, 57350, ()), KeyDefinition(False, (KeyAction('previous_tab'),), 1024, False, 57350, ()),
# previous_tab # previous_tab
KeyDefinition(False, KeyAction('previous_tab'), 5, False, 57346, ()), KeyDefinition(False, (KeyAction('previous_tab'),), 5, False, 57346, ()),
# new_tab # new_tab
KeyDefinition(False, KeyAction('new_tab'), 1024, False, 116, ()), KeyDefinition(False, (KeyAction('new_tab'),), 1024, False, 116, ()),
# close_tab # close_tab
KeyDefinition(False, KeyAction('close_tab'), 1024, False, 113, ()), KeyDefinition(False, (KeyAction('close_tab'),), 1024, False, 113, ()),
# move_tab_forward # move_tab_forward
KeyDefinition(False, KeyAction('move_tab_forward'), 1024, False, 46, ()), KeyDefinition(False, (KeyAction('move_tab_forward'),), 1024, False, 46, ()),
# move_tab_backward # move_tab_backward
KeyDefinition(False, KeyAction('move_tab_backward'), 1024, False, 44, ()), KeyDefinition(False, (KeyAction('move_tab_backward'),), 1024, False, 44, ()),
# set_tab_title # set_tab_title
KeyDefinition(False, KeyAction('set_tab_title'), 1026, False, 116, ()), KeyDefinition(False, (KeyAction('set_tab_title'),), 1026, False, 116, ()),
# next_layout # next_layout
KeyDefinition(False, KeyAction('next_layout'), 1024, False, 108, ()), KeyDefinition(False, (KeyAction('next_layout'),), 1024, False, 108, ()),
# increase_font_size # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # toggle_fullscreen
KeyDefinition(False, KeyAction('toggle_fullscreen'), 1024, False, 57374, ()), KeyDefinition(False, (KeyAction('toggle_fullscreen'),), 1024, False, 57374, ()),
# toggle_maximized # toggle_maximized
KeyDefinition(False, KeyAction('toggle_maximized'), 1024, False, 57373, ()), KeyDefinition(False, (KeyAction('toggle_maximized'),), 1024, False, 57373, ()),
# input_unicode_character # input_unicode_character
KeyDefinition(False, KeyAction('kitten', ('unicode_input',)), 1024, False, 117, ()), KeyDefinition(False, (KeyAction('kitten', ('unicode_input',)),), 1024, False, 117, ()),
# edit_config_file # edit_config_file
KeyDefinition(False, KeyAction('edit_config_file'), 1024, False, 57365, ()), KeyDefinition(False, (KeyAction('edit_config_file'),), 1024, False, 57365, ()),
# kitty_shell # kitty_shell
KeyDefinition(False, KeyAction('kitty_shell', ('window',)), 1024, False, 57344, ()), KeyDefinition(False, (KeyAction('kitty_shell', ('window',)),), 1024, False, 57344, ()),
# increase_background_opacity # 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 # 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 # 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 # 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 # 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 # reload_config_file
KeyDefinition(False, KeyAction('load_config_file'), 1024, False, 57368, ()), KeyDefinition(False, (KeyAction('load_config_file'),), 1024, False, 57368, ()),
# debug_config # debug_config
KeyDefinition(False, KeyAction('debug_config'), 1024, False, 57369, ()), KeyDefinition(False, (KeyAction('debug_config'),), 1024, False, 57369, ()),
] ]
if is_macos: if is_macos:
defaults.map.append(KeyDefinition(False, KeyAction('copy_to_clipboard'), 8, False, 99, ())) 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('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'),), 10, False, 57354, ()))
defaults.map.append(KeyDefinition(False, KeyAction('scroll_line_up'), 8, False, 57352, ())) 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'),), 10, False, 57355, ()))
defaults.map.append(KeyDefinition(False, KeyAction('scroll_line_down'), 8, False, 57353, ())) 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_up'),), 8, False, 57354, ()))
defaults.map.append(KeyDefinition(False, KeyAction('scroll_page_down'), 8, False, 57355, ())) 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_home'),), 8, False, 57356, ()))
defaults.map.append(KeyDefinition(False, KeyAction('scroll_end'), 8, False, 57357, ())) 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_window'),), 8, False, 57345, ()))
defaults.map.append(KeyDefinition(False, KeyAction('new_os_window'), 8, False, 110, ())) 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('close_window'),), 9, False, 100, ()))
defaults.map.append(KeyDefinition(False, KeyAction('start_resizing_window'), 8, False, 114, ())) 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('first_window'),), 8, False, 49, ()))
defaults.map.append(KeyDefinition(False, KeyAction('second_window'), 8, False, 50, ())) 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('third_window'),), 8, False, 51, ()))
defaults.map.append(KeyDefinition(False, KeyAction('fourth_window'), 8, False, 52, ())) 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('fifth_window'),), 8, False, 53, ()))
defaults.map.append(KeyDefinition(False, KeyAction('sixth_window'), 8, False, 54, ())) 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('seventh_window'),), 8, False, 55, ()))
defaults.map.append(KeyDefinition(False, KeyAction('eighth_window'), 8, False, 56, ())) 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('ninth_window'),), 8, False, 57, ()))
defaults.map.append(KeyDefinition(False, KeyAction('next_tab'), 9, False, 93, ())) 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('previous_tab'),), 9, False, 91, ()))
defaults.map.append(KeyDefinition(False, KeyAction('new_tab'), 8, False, 116, ())) 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_tab'),), 8, False, 119, ()))
defaults.map.append(KeyDefinition(False, KeyAction('close_os_window'), 9, 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('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, 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)),), 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)),), 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)),), 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, '-', 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('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('kitten', ('unicode_input',)),), 12, False, 32, ()))
defaults.map.append(KeyDefinition(False, KeyAction('edit_config_file'), 8, False, 44, ())) 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('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('load_config_file'),), 12, False, 44, ()))
defaults.map.append(KeyDefinition(False, KeyAction('debug_config'), 10, False, 44, ())) defaults.map.append(KeyDefinition(False, (KeyAction('debug_config'),), 10, False, 44, ()))
defaults.mouse_map = [ defaults.mouse_map = [
# click_url_or_select # 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 # 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 # 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 # click_url
MouseMapping(0, 5, -1, True, KeyAction('mouse_handle_click', ('link',))), MouseMapping(0, 5, -1, True, (KeyAction('mouse_handle_click', ('link',)),)),
# click_url # 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 # click_url_discard
MouseMapping(0, 5, 1, True, KeyAction('discard_event')), MouseMapping(0, 5, 1, True, (KeyAction('discard_event'),)),
# paste_selection # paste_selection
MouseMapping(2, 0, -1, False, KeyAction('paste_from_selection')), MouseMapping(2, 0, -1, False, (KeyAction('paste_from_selection'),)),
# start_simple_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 # start_rectangle_selection
MouseMapping(0, 6, 1, False, KeyAction('mouse_selection', (2,))), MouseMapping(0, 6, 1, False, (KeyAction('mouse_selection', (2,)),)),
# select_word # select_word
MouseMapping(0, 0, 2, False, KeyAction('mouse_selection', (3,))), MouseMapping(0, 0, 2, False, (KeyAction('mouse_selection', (3,)),)),
# select_line # select_line
MouseMapping(0, 0, 3, False, KeyAction('mouse_selection', (4,))), MouseMapping(0, 0, 3, False, (KeyAction('mouse_selection', (4,)),)),
# select_line_from_point # select_line_from_point
MouseMapping(0, 6, 3, False, KeyAction('mouse_selection', (5,))), MouseMapping(0, 6, 3, False, (KeyAction('mouse_selection', (5,)),)),
# extend_selection # extend_selection
MouseMapping(1, 0, 1, False, KeyAction('mouse_selection', (1,))), MouseMapping(1, 0, 1, False, (KeyAction('mouse_selection', (1,)),)),
# paste_selection_grabbed # paste_selection_grabbed
MouseMapping(2, 1, -1, True, KeyAction('paste_selection')), MouseMapping(2, 1, -1, True, (KeyAction('paste_selection'),)),
# paste_selection_grabbed # paste_selection_grabbed
MouseMapping(2, 1, -1, False, KeyAction('paste_selection')), MouseMapping(2, 1, -1, False, (KeyAction('paste_selection'),)),
# paste_selection_grabbed # paste_selection_grabbed
MouseMapping(2, 1, 1, True, KeyAction('discard_event')), MouseMapping(2, 1, 1, True, (KeyAction('discard_event'),)),
# start_simple_selection_grabbed # 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 # 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 # 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 # 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 # select_word_grabbed
MouseMapping(0, 1, 2, True, KeyAction('mouse_selection', (3,))), MouseMapping(0, 1, 2, True, (KeyAction('mouse_selection', (3,)),)),
# select_word_grabbed # select_word_grabbed
MouseMapping(0, 1, 2, False, KeyAction('mouse_selection', (3,))), MouseMapping(0, 1, 2, False, (KeyAction('mouse_selection', (3,)),)),
# select_line_grabbed # select_line_grabbed
MouseMapping(0, 1, 3, True, KeyAction('mouse_selection', (4,))), MouseMapping(0, 1, 3, True, (KeyAction('mouse_selection', (4,)),)),
# select_line_grabbed # 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 # 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 # 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 # extend_selection_grabbed
MouseMapping(1, 1, 1, True, KeyAction('mouse_selection', (1,))), MouseMapping(1, 1, 1, True, (KeyAction('mouse_selection', (1,)),)),
# extend_selection_grabbed # 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 # 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'),)),
] ]

View File

@ -6,17 +6,19 @@ import os
import re import re
import sys import sys
from typing import ( from typing import (
Any, Callable, Container, Dict, Iterable, List, NamedTuple, Optional, Any, Callable, Container, Dict, Iterable, Iterator, List, NamedTuple,
Sequence, Tuple, Union Optional, Sequence, Tuple, Union
) )
import kitty.fast_data_types as defines import kitty.fast_data_types as defines
from kitty.conf.utils import ( from kitty.conf.utils import (
KeyAction, KeyFuncWrapper, positive_float, positive_int, KeyAction, KeyFuncWrapper, positive_float, positive_int, python_string,
python_string, to_bool, to_cmdline, to_color, uniq, unit_float to_bool, to_cmdline, to_color, uniq, unit_float
) )
from kitty.constants import config_dir, is_macos 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.fonts import FontFeature
from kitty.key_names import ( from kitty.key_names import (
character_key_name_aliases, functional_key_name_aliases, 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.types import FloatEdges, MouseEvent, SingleKey
from kitty.utils import expandvars, log_error from kitty.utils import expandvars, log_error
KeyMap = Dict[SingleKey, KeyAction] KeyMap = Dict[SingleKey, Tuple[KeyAction, ...]]
MouseMap = Dict[MouseEvent, KeyAction] MouseMap = Dict[MouseEvent, Tuple[KeyAction, ...]]
KeySequence = Tuple[SingleKey, ...] KeySequence = Tuple[SingleKey, ...]
SubSequenceMap = Dict[KeySequence, KeyAction] SubSequenceMap = Dict[KeySequence, Tuple[KeyAction, ...]]
SequenceMap = Dict[SingleKey, SubSequenceMap] SequenceMap = Dict[SingleKey, SubSequenceMap]
MINIMUM_FONT_SIZE = 4 MINIMUM_FONT_SIZE = 4
default_tab_separator = '' default_tab_separator = ''
@ -58,14 +60,6 @@ def shlex_parse(func: str, rest: str) -> FuncArgsType:
return func, to_cmdline(rest) 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: def parse_send_text_bytes(text: str) -> bytes:
return python_string(text).encode('utf-8') return python_string(text).encode('utf-8')
@ -91,7 +85,7 @@ def kitten_parse(func: str, rest: str) -> FuncArgsType:
else: else:
args = rest.split(maxsplit=2)[1:] args = rest.split(maxsplit=2)[1:]
func = 'kitten' func = 'kitten'
return func, args return func, [args[0]] + (to_cmdline(args[1]) if len(args) > 1 else [])
@func_with_args('goto_tab') @func_with_args('goto_tab')
@ -772,11 +766,14 @@ def watcher(val: str, current_val: Container[str]) -> Iterable[Tuple[str, str]]:
yield val, val yield val, val
def kitten_alias(val: str) -> Iterable[Tuple[str, List[str]]]: def action_alias(val: str) -> Iterable[Tuple[str, List[str]]]:
parts = val.split(maxsplit=2) parts = val.split(maxsplit=1)
if len(parts) >= 2: if len(parts) > 1:
name = parts.pop(0) alias_name, rest = parts
yield name, parts yield alias_name, to_cmdline(rest)
kitten_alias = action_alias
def symbol_map(val: str) -> Iterable[Tuple[Tuple[int, int], str]]: def symbol_map(val: str) -> Iterable[Tuple[Tuple[int, int], str]]:
@ -806,12 +803,24 @@ def symbol_map(val: str) -> Iterable[Tuple[Tuple[int, int], str]]:
yield (a, b), family 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) parts = action.strip().split(maxsplit=1)
func = parts[0] func = parts[0]
if len(parts) == 1: if len(parts) == 1:
return KeyAction(func, ()) yield KeyAction(func, ())
return
rest = parts[1] rest = parts[1]
if func == 'combine':
yield from parse_combine(rest, action_type)
else:
parser = func_with_args.get(func) parser = func_with_args.get(func)
if parser is not None: if parser is not None:
try: try:
@ -819,44 +828,64 @@ def parse_key_action(action: str, action_type: str = 'map') -> Optional[KeyActio
except Exception as err: except Exception as err:
log_error(f'Ignoring invalid {action_type} action: {action} with err: {err}') log_error(f'Ignoring invalid {action_type} action: {action} with err: {err}')
else: else:
return KeyAction(func, tuple(args)) yield KeyAction(func, tuple(args))
else: else:
log_error(f'Ignoring unknown {action_type} action: {action}') log_error(f'Ignoring unknown {action_type} action: {action}')
return None
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: 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: @property
if not self.action.args or not aliases: def is_no_op(self) -> bool:
return self.action return len(self.actions) == 1 and self.actions[0].func in self.no_op_actions
kitten = self.action.args[0]
rest = str(self.action.args[1] if len(self.action.args) > 1 else '') def resolve_aliases(self, aliases: Dict[str, List[ActionAlias]]) -> Tuple[KeyAction, ...]:
changed = False self.actions = tuple(resolve_aliases_in_action(a, aliases) for a in self.actions)
for key, expanded in aliases.items(): return self.actions
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
class MouseMapping(BaseDefinition): 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.button = button
self.mods = mods self.mods = mods
self.repeat_count = repeat_count self.repeat_count = repeat_count
self.grabbed = grabbed self.grabbed = grabbed
self.action = action self.actions = actions
def __repr__(self) -> str: 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': 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_kitten_aliases(aliases)) return MouseMapping(self.button, defines.resolve_key_mods(kitty_mod, self.mods), self.repeat_count, self.grabbed, self.resolve_aliases(aliases))
@property @property
def trigger(self) -> MouseEvent: def trigger(self) -> MouseEvent:
@ -865,21 +894,21 @@ class MouseMapping(BaseDefinition):
class KeyDefinition(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.is_sequence = is_sequence
self.action = action self.actions = actions
self.trigger = SingleKey(mods, is_native, key) self.trigger = SingleKey(mods, is_native, key)
self.rest = rest self.rest = rest
def __repr__(self) -> str: 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: def r(k: SingleKey) -> SingleKey:
mods = defines.resolve_key_mods(kitty_mod, k.mods) mods = defines.resolve_key_mods(kitty_mod, k.mods)
return k._replace(mods=mods) return k._replace(mods=mods)
return KeyDefinition( 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), defines.resolve_key_mods(kitty_mod, self.trigger.mods),
self.trigger.is_native, self.trigger.key, tuple(map(r, self.rest))) 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') log_error(f'Shortcut: {sc} has unknown key, ignoring')
return return
try: try:
paction = parse_key_action(action) paction = tuple(parse_key_actions(action))
except Exception: except Exception:
log_error('Invalid shortcut action: {}. Ignoring.'.format( log_error(f'Invalid shortcut action: {action}. Ignoring.')
action))
else: else:
if paction is not None: if paction:
if is_sequence: if is_sequence:
if trigger is not None: if trigger is not None:
yield KeyDefinition(True, paction, trigger[0], trigger[1], trigger[2], rest) 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') log_error(f'Mouse modes: {modes} not recognized, ignoring')
return return
try: try:
paction = parse_key_action(action, 'mouse_map') paction = tuple(parse_key_actions(action, 'mouse_map'))
except Exception: except Exception:
log_error(f'Invalid mouse action: {action}. Ignoring.') log_error(f'Invalid mouse action: {action}. Ignoring.')
return return
if paction is None: if not paction:
return return
for mode in sorted(specified_modes): for mode in sorted(specified_modes):
yield MouseMapping(button, mods, count, mode == 'grabbed', paction) yield MouseMapping(button, mods, count, mode == 'grabbed', paction)

View File

@ -646,7 +646,7 @@ class Window:
action = get_options().mousemap.get(ev) action = get_options().mousemap.get(ev)
if action is None: if action is None:
return False 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: def open_url(self, url: str, hyperlink_id: int, cwd: Optional[str] = None) -> None:
opts = get_options() opts = get_options()

View File

@ -69,10 +69,11 @@ class Callbacks:
def on_mouse_event(self, event): def on_mouse_event(self, event):
ev = MouseEvent(**event) ev = MouseEvent(**event)
action = self.opts.mousemap.get(ev) actions = self.opts.mousemap.get(ev)
if action is None: if not actions:
return False return False
self.current_mouse_button = ev.button self.current_mouse_button = ev.button
for action in actions:
getattr(self, action.func)(*action.args) getattr(self, action.func)(*action.args)
self.current_mouse_button = 0 self.current_mouse_button = 0
return True return True
@ -115,8 +116,8 @@ class BaseTest(TestCase):
if options: if options:
final_options.update(options) final_options.update(options)
options = Options(merge_result_dicts(defaults._asdict(), final_options)) options = Options(merge_result_dicts(defaults._asdict(), final_options))
finalize_keys(options) finalize_keys(options, {})
finalize_mouse_mappings(options) finalize_mouse_mappings(options, {})
set_options(options) set_options(options)
return options return options

View File

@ -31,7 +31,8 @@ class TestConfParsing(BaseTest):
return ans return ans
def keys_for_func(opts, name): def keys_for_func(opts, name):
for key, action in opts.keymap.items(): for key, actions in opts.keymap.items():
for action in actions:
if action.func == name: if action.func == name:
yield key yield key
@ -51,8 +52,16 @@ class TestConfParsing(BaseTest):
self.ae(opts.pointer_shape_when_grabbed, defaults.pointer_shape_when_grabbed) 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') 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}) 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')) 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') opts = p('kitty_mod alt')
self.ae(opts.kitty_mod, to_modifiers('alt')) self.ae(opts.kitty_mod, to_modifiers('alt'))
self.ae(next(keys_for_func(opts, 'next_layout')).mods, opts.kitty_mod) self.ae(next(keys_for_func(opts, 'next_layout')).mods, opts.kitty_mod)