feat(rawcli): ipython-style completion (Phase 6)

- completer.py: RawCompleter completes the leading command token —
  control verbs + the identified transponder's catalog commands — with
  each command's help as the completion meta (tooltip). `help <cmd>`
  completes catalog command names.
- app: wire the completer with complete_while_typing (live menu) +
  AutoSuggestFromHistory (inline hints) for the ipython feel.

Completes the 6-phase rawcli plan. 4 new tests (verb/catalog completion,
tooltips, help-arg completion).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
michael
2026-07-14 12:19:42 -07:00
parent 4e7181d289
commit f76663f798
3 changed files with 93 additions and 1 deletions

View File

@@ -13,6 +13,7 @@ from pm3py.cli.rawcli.entry import byte_space
from pm3py.cli.rawcli.identify import identify, clear, name_14a
from pm3py.cli.rawcli.catalog import TYPE2, ISO15, catalog_for, catalog_for as _cf
from pm3py.cli.rawcli.tlv import parse_tlv, format_tlv
from pm3py.cli.rawcli.completer import RawCompleter
from pm3py.cli.rawcli.app import dispatch
_ANSI = re.compile(r"\x1b\[[0-9;]*m")
@@ -296,3 +297,32 @@ class TestTLV:
def test_tlv_command(self, capsys):
dispatch(RawSession(), "tlv " + self.BLOCK.hex())
assert "NDEF MESSAGE" in capsys.readouterr().out
class TestCompleter:
def _complete(self, session, text):
from prompt_toolkit.document import Document
return list(RawCompleter(session).get_completions(Document(text), None))
def test_completes_control_verbs(self):
cs = self._complete(RawSession(), "ide")
assert any(c.text == "identify" for c in cs)
def test_completes_catalog_commands_with_meta(self):
s = RawSession()
s.protocol, s.transponder = "hf14a", "NTAG215" # -> Type 2 catalog
cs = self._complete(s, "REA")
texts = [c.text for c in cs]
assert "READ(" in texts
read = next(c for c in cs if c.text == "READ(")
assert "0x30" in read.display_meta_text # tooltip = help
def test_no_catalog_before_identify(self):
cs = self._complete(RawSession(), "REA")
assert all(c.text != "READ(" for c in cs) # no tag => no catalog commands
def test_help_argument_completes_command_names(self):
s = RawSession()
s.protocol, s.transponder = "hf14a", "NTAG215"
cs = self._complete(s, "help GET")
assert any(c.text == "GET_VERSION" for c in cs)