diff --git a/kitty/debug_config.py b/kitty/debug_config.py index 8c3ce0d8f..255a90179 100644 --- a/kitty/debug_config.py +++ b/kitty/debug_config.py @@ -22,7 +22,7 @@ from .fast_data_types import Color, num_users from .options.types import Options as KittyOpts, defaults from .options.utils import SequenceMap from .rgb import color_as_sharp -from .types import MouseEvent, Shortcut +from .types import MouseEvent, Shortcut, mod_to_names AnyEvent = TypeVar('AnyEvent', MouseEvent, Shortcut) Print = Callable[..., None] @@ -41,30 +41,40 @@ def title(x: str) -> str: return colored(x, 'blue', intense=True) -def print_event(ev: AnyEvent, defn: str, print: Print) -> None: - print(f'\t{ev.human_repr} → {defn}') +def print_event(ev: str, defn: str, print: Print) -> None: + print(f'\t{ev} → {defn}') -def print_mapping_changes(defns: Dict[AnyEvent, str], changes: Set[AnyEvent], text: str, print: Print) -> None: +def print_mapping_changes(defns: Dict[str, str], changes: Set[str], text: str, print: Print) -> None: if changes: print(title(text)) for k in sorted(changes): print_event(k, defns[k], print) -def compare_maps(final: Dict[AnyEvent, str], initial: Dict[AnyEvent, str], print: Print) -> None: - is_mouse = False - for k in initial: - if isinstance(k, MouseEvent): - is_mouse = True - break - added = set(final) - set(initial) - removed = set(initial) - set(final) - changed = {k for k in set(final) & set(initial) if final[k] != initial[k]} - which = 'mouse actions' if is_mouse else 'shortcuts' - print_mapping_changes(final, added, f'Added {which}:', print) - print_mapping_changes(initial, removed, f'Removed {which}:', print) - print_mapping_changes(final, changed, f'Changed {which}:', print) +def compare_shortcut_maps(final: Dict[Shortcut, str], final_kitty_mod: int, initial: Dict[Shortcut, str], initial_kitty_mod: int, print: Print) -> None: + # previous_tab uses a definition of ctrl+shift+tab not kitty_mod+tab, but + # we cant distinguish these as the information about the original + # definition is discarded. + ei = {k._replace(kitty_mod=0 if v == 'previous_tab' and k.keys[0].key == 57346 else initial_kitty_mod).human_repr: v for k, v in initial.items()} + ef = {k._replace(kitty_mod=final_kitty_mod).human_repr: v for k, v in final.items()} + added = set(ef) - set(ei) + removed = set(ei) - set(ef) + changed = {k for k in set(ef) & set(ei) if ef[k] != ei[k]} + print_mapping_changes(ef, added, 'Added shortcuts:', print) + print_mapping_changes(ei, removed, 'Removed shortcuts:', print) + print_mapping_changes(ef, changed, 'Changed shortcuts:', print) + + +def compare_mouse_maps(final: Dict[MouseEvent, str], final_kitty_mod: int, initial: Dict[MouseEvent, str], initial_kitty_mod: int, print: Print) -> None: + ei = {k.human_repr: v for k, v in initial.items()} + ef = {k.human_repr: v for k, v in final.items()} + added = set(ef) - set(ei) + removed = set(ei) - set(ef) + changed = {k for k in set(ef) & set(ei) if ef[k] != ei[k]} + print_mapping_changes(ef, added, 'Added mouse actions:', print) + print_mapping_changes(ei, removed, 'Removed mouse actions:', print) + print_mapping_changes(ef, changed, 'Changed mouse actions:', print) def flatten_sequence_map(m: SequenceMap) -> ShortcutMap: @@ -105,16 +115,19 @@ def compare_opts(opts: KittyOpts, print: Print) -> None: if isinstance(val, Color): colors.append(fmt.format(f) + ' ' + color_as_sharp(val) + ' ' + styled(' ', bg=val)) else: - print(fmt.format(f), str(getattr(opts, f))) + if f == 'kitty_mod': + print(fmt.format(f), '+'.join(mod_to_names(getattr(opts, f)))) + else: + print(fmt.format(f), str(getattr(opts, f))) - compare_maps(opts.mousemap, default_opts.mousemap, print) + compare_mouse_maps(opts.mousemap, opts.kitty_mod, default_opts.mousemap, default_opts.kitty_mod, print) final_, initial_ = opts.keymap, default_opts.keymap final: ShortcutMap = {Shortcut((k,)): v for k, v in final_.items()} initial: ShortcutMap = {Shortcut((k,)): v for k, v in initial_.items()} final_s, initial_s = map(flatten_sequence_map, (opts.sequence_map, default_opts.sequence_map)) final.update(final_s) initial.update(initial_s) - compare_maps(final, initial, print) + compare_shortcut_maps(final, opts.kitty_mod, initial, default_opts.kitty_mod, print) if colors: print(f'{title("Colors")}:', end='\n\t') print('\n\t'.join(sorted(colors))) diff --git a/kitty/types.py b/kitty/types.py index 9c450f2b6..bb73a0f60 100644 --- a/kitty/types.py +++ b/kitty/types.py @@ -3,7 +3,7 @@ from functools import update_wrapper from typing import ( - TYPE_CHECKING, Any, Callable, Generic, NamedTuple, Tuple, TypeVar, Union, Iterator + TYPE_CHECKING, Any, Callable, Generic, NamedTuple, Tuple, TypeVar, Union, Iterator, Dict ) _T = TypeVar('_T') @@ -49,16 +49,12 @@ class SignalInfo(NamedTuple): sival_ptr: int -def mod_to_names(mods: int) -> Iterator[str]: - from .fast_data_types import ( - GLFW_MOD_ALT, GLFW_MOD_CAPS_LOCK, GLFW_MOD_CONTROL, GLFW_MOD_HYPER, - GLFW_MOD_META, GLFW_MOD_NUM_LOCK, GLFW_MOD_SHIFT, GLFW_MOD_SUPER - ) - from .constants import is_macos - modmap = {'ctrl': GLFW_MOD_CONTROL, 'shift': GLFW_MOD_SHIFT, ('opt' if is_macos else 'alt'): GLFW_MOD_ALT, - ('cmd' if is_macos else 'super'): GLFW_MOD_SUPER, 'hyper': GLFW_MOD_HYPER, 'meta': GLFW_MOD_META, - 'caps_lock': GLFW_MOD_CAPS_LOCK, 'num_lock': GLFW_MOD_NUM_LOCK} - for name, val in modmap.items(): +def mod_to_names(mods: int, kitty_mod: int = 0) -> Iterator[str]: + has_kitty_mod = kitty_mod and (mods & kitty_mod) == kitty_mod + if has_kitty_mod: + mods &= ~kitty_mod + yield 'kitty_mod' + for name, val in modmap().items(): if mods & val: yield name @@ -76,24 +72,28 @@ class SingleKey(NamedTuple): kwds.append(f'{f}={val!r}') return 'SingleKey(' + ', '.join(kwds) + ')' - @property - def human_repr(self) -> str: + def human_repr_with_kitty_mod(self, kitty_mod: int = 0) -> str: from .fast_data_types import glfw_get_key_name names = [] - names = list(mod_to_names(self.mods)) + names = list(mod_to_names(self.mods, kitty_mod)) if self.key > 0: kname = (glfw_get_key_name(0, self.key) if self.is_native else glfw_get_key_name(self.key, 0)) or f'{self.key}' kname = {' ': 'space'}.get(kname, kname) names.append(kname) return '+'.join(names) + @property + def human_repr(self) -> str: + return self.human_repr_with_kitty_mod() + class Shortcut(NamedTuple): keys: Tuple[SingleKey, ...] + kitty_mod: int = 0 @property def human_repr(self) -> str: - return ' > '.join(k.human_repr for k in self.keys) + return ' > '.join(k.human_repr_with_kitty_mod(self.kitty_mod) for k in self.keys) class MouseEvent(NamedTuple): @@ -177,6 +177,19 @@ def run_once(f: Callable[[], _T]) -> 'RunOnce[_T]': return RunOnce(f) +@run_once +def modmap() -> Dict[str, int]: + from .fast_data_types import ( + GLFW_MOD_ALT, GLFW_MOD_CAPS_LOCK, GLFW_MOD_CONTROL, GLFW_MOD_HYPER, + GLFW_MOD_META, GLFW_MOD_NUM_LOCK, GLFW_MOD_SHIFT, GLFW_MOD_SUPER + ) + from .constants import is_macos + + return {'ctrl': GLFW_MOD_CONTROL, 'shift': GLFW_MOD_SHIFT, ('opt' if is_macos else 'alt'): GLFW_MOD_ALT, + ('cmd' if is_macos else 'super'): GLFW_MOD_SUPER, 'hyper': GLFW_MOD_HYPER, 'meta': GLFW_MOD_META, + 'caps_lock': GLFW_MOD_CAPS_LOCK, 'num_lock': GLFW_MOD_NUM_LOCK} + + if TYPE_CHECKING: from typing import Literal ActionGroup = Literal['cp', 'sc', 'win', 'tab', 'mouse', 'mk', 'lay', 'misc', 'debug']