diff --git a/pm3py/cli/rawcli/entry.py b/pm3py/cli/rawcli/entry.py index b26a902..a86b811 100644 --- a/pm3py/cli/rawcli/entry.py +++ b/pm3py/cli/rawcli/entry.py @@ -28,18 +28,32 @@ def byte_space(text: str, mode: str = HEX) -> str: return " ".join(compact[i:i + n] for i in range(0, len(compact), n)) -def install_key_bindings(kb, session) -> None: - """Wire Ctrl-/ (toggle entry mode) and live auto-byte-spacing of digit input onto ``kb``. +#: keys that toggle entry mode. Ctrl-/ is emitted as Ctrl-_ (0x1F) by most terminals; c-t is an +#: always-available fallback that power users can rely on regardless of terminal. +TOGGLE_KEYS = ("c-_", "c-t") - ``session`` is the :class:`~pm3py.cli.rawcli.session.RawSession` whose ``entry_mode`` is - toggled and read.""" + +def _bind(kb, key, handler) -> None: + """Add a binding, ignoring key names a given prompt_toolkit build doesn't accept.""" + try: + kb.add(key)(handler) + except ValueError: + pass + + +def install_key_bindings(kb, session) -> None: + """Wire the entry-mode toggle (Ctrl-/ , via Ctrl-_ , plus Ctrl-t) and live auto-byte-spacing + of digit input onto ``kb``. ``session`` is the :class:`RawSession` whose ``entry_mode`` is + toggled and read. Never raises on an unknown key name.""" from prompt_toolkit.document import Document - @kb.add("c-/") def _toggle(event): session.toggle_entry_mode() event.app.invalidate() # redraw the breadcrumb + for key in TOGGLE_KEYS: + _bind(kb, key, _toggle) + def _regroup_after(event): buf = event.current_buffer buf.insert_text(event.data) @@ -49,4 +63,4 @@ def install_key_bindings(kb, session) -> None: buf.document = Document(grouped, len(grouped)) for ch in sorted(_DIGITS[HEX] | _DIGITS[BIN]): - kb.add(ch)(_regroup_after) + _bind(kb, ch, _regroup_after) diff --git a/tests/test_rawcli.py b/tests/test_rawcli.py index efb112b..3dbdf02 100644 --- a/tests/test_rawcli.py +++ b/tests/test_rawcli.py @@ -326,3 +326,13 @@ class TestCompleter: s.protocol, s.transponder = "hf14a", "NTAG215" cs = self._complete(s, "help GET") assert any(c.text == "GET_VERSION" for c in cs) + + +class TestKeyBindings: + def test_install_does_not_raise(self): + # regression: "c-/" is not a valid prompt_toolkit key and used to crash launch + from prompt_toolkit.key_binding import KeyBindings + from pm3py.cli.rawcli.entry import install_key_bindings, TOGGLE_KEYS + kb = KeyBindings() + install_key_bindings(kb, RawSession()) + assert len(kb.bindings) >= len(TOGGLE_KEYS)