From 284a9e64c2ea480529d461009ef55ef88c883716 Mon Sep 17 00:00:00 2001 From: rootiest Date: Fri, 23 Aug 2024 10:12:32 -0400 Subject: [PATCH] feat: add keyboard mapping display functionality for different modes --- keymap.py | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++ keymaps.conf | 3 ++ 2 files changed, 118 insertions(+) create mode 100644 keymap.py diff --git a/keymap.py b/keymap.py new file mode 100644 index 0000000..d916eff --- /dev/null +++ b/keymap.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +"""Kitten to print the current list of keyboard shortcuts (consists of BOTH single keys and key +sequences). +""" + +import string +import re +from collections import defaultdict +from typing import Union, Final, Dict, List + +from kittens.tui.handler import result_handler +from kitty import fast_data_types +from kitty.boss import Boss +from kitty.options.utils import KeyMap +from kitty.tab_bar import Formatter as fmt +from kitty.types import Shortcut, mod_to_names + +# List of categories and regular expressions to match actions on +categories: Final = { + "Scrolling": r"(^scroll_|show_scrollback|show_last_command_output)", + "Tab Management": r"(^|_)tab(_|$)", + "Window Management": r"(^|_)windows?(_|$)", + "Layout Management": r"(^|_)layout(_|\b)", + "Other Shortcuts": r".", +} + +ShortcutRepr = str +ActionMap = dict[str, list[ShortcutRepr]] + + +def main(args: list[str]) -> Union[str, None]: + pass + + +@result_handler(no_ui=True) +def handle_result(args: list[str], answer: str, target_window_id: int, boss: Boss): + opts = fast_data_types.get_options() + modes_categorized: Dict[str, Dict[str, ActionMap]] = {} + for mode_name, mode_data in opts.keyboard_modes.items(): + # set up keymaps (single keystrokes) + # keymap: KeyMap = ( + # boss.keymap + # mode_data.keymap + # ) # same as `opts.keymap`, except with global keymaps removed + # keymap: dict[Shortcut, str] = { + # Shortcut((key,)): action for key, action in keymap.items() + # } + # set up key sequences (combinations of keystrokes, separated by '>') + # seq_keymap: dict[Shortcut, str] = { + # Shortcut((inner_key.trigger, *(inner_key.rest or []))): inner_key.definition + # for key, subseq in keymap.items() + # for inner_key in subseq + # for subseq_keys, action in subseq.items() + # } + + seq_keymap = {} + + for key, subseq in mode_data.keymap.items(): + # debug_file.write(f"{key}, {subseq}\n") + # debug_file.write(f"{type(key)}, {type(subseq)}\n") + + for inner_key in subseq: + trigger = inner_key.trigger + rest = inner_key.rest or [] + shortcut = Shortcut((trigger, *rest)) + definition = inner_key.definition + seq_keymap[shortcut] = definition + # keymap.update(seq_keymap) + + # categorize shortcuts + # because each action can have multiple shortcuts associated with it, we also attempt to + # group shortcuts with the same actions together. + output_categorized: dict[str, ActionMap] = defaultdict( + lambda: defaultdict(list) + ) + for key, action in seq_keymap.items(): + key_repr: ShortcutRepr = key.human_repr(kitty_mod=opts.kitty_mod) + key_repr = f"{key_repr:<15} {fmt.fg.red}→{fmt.fg.default} {action}" + + for subheader, re_pat in categories.items(): + if re.search(re_pat, action): + action_map: ActionMap = output_categorized[subheader] + action_map[action].append(key_repr) + break + else: + output_categorized["Other Shortcuts"][action].append(key_repr) + # emsg = f"No valid subheader found for keymap {key_repr!r}." + # raise ValueError(emsg) + modes_categorized[mode_name] = output_categorized + # print out shortcuts + output = [ + "Kitty keyboard mappings", + "=======================", + "", + "My kitty_mod is {}.".format("+".join(mod_to_names(opts.kitty_mod))), + "", + ] + for mode_name, output_categorized in modes_categorized.items(): + hr_output_name = mode_name or "Standard" + output.append("=" * 40) + output.append(f"{hr_output_name} Mode") + output.append("=" * 40) + for category in categories: + if category not in output_categorized: + continue + output.extend([category, "=" * len(category), ""]) + output.extend(sum(output_categorized[category].values(), [])) + output.append("") + + boss.display_scrollback( + boss.active_window, + "\n".join(output), + title="Kitty keyboard mappings", + report_cursor=False, + ) diff --git a/keymaps.conf b/keymaps.conf index 60add5f..22146b2 100644 --- a/keymaps.conf +++ b/keymaps.conf @@ -55,6 +55,9 @@ map kitty_mod+right next_tab map ctrl+tab next_tab map kitty_mod+q close_tab +# Kitty which-key +map kitty_mod+F1 kitten keymap.py + # Create a new window splitting the space used by the existing one so that # the two windows are placed one above the other map f5 launch --location=hsplit