Files
pm3py/pm3py/cli/rawcli/entry.py
michael 54e52f029f feat(rawcli): flexible entry + parser (Phase 2)
- parser.py: classify a line into control / function-call / raw payload;
  parse loose hex/binary with intermixed 0x/0b tokens (whitespace-
  insensitive, defaulting to the session entry mode)
- entry.py: byte_space() byte-group formatting + key bindings — Ctrl-/
  toggles hex/binary entry mode (breadcrumb updates), digits auto-space
  into byte groups as you type
- app.py: dispatch() drives the loop via parse_line (testable without a
  live prompt); identify/call are stubbed for Phases 3/4

14 new tests (parser tokens/intermix/errors, byte_space, dispatch).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 12:07:21 -07:00

53 lines
1.9 KiB
Python

"""Numeric entry helpers: byte-group spacing for hex/binary input and the prompt_toolkit key
bindings that toggle entry mode (Ctrl-/) and auto-space digits as you type.
The pure ``byte_space`` / ``group_size`` functions carry the logic and are unit-tested; the key
bindings are thin wiring exercised in the live REPL.
"""
from __future__ import annotations
HEX = "hex"
BIN = "bin"
_DIGITS = {HEX: set("0123456789abcdefABCDEF"), BIN: set("01")}
def group_size(mode: str) -> int:
"""Characters per byte group: 2 for hex, 8 for binary."""
return 2 if mode == HEX else 8
def byte_space(text: str, mode: str = HEX) -> str:
"""Re-group a run of hex/binary digits into space-separated byte groups.
Leaves a token that carries an explicit ``0x``/``0b`` prefix untouched (so intermixed input
isn't mangled); only a bare digit run is regrouped."""
if "x" in text.lower() or "b" in text.lower():
return text
n = group_size(mode)
compact = text.replace(" ", "")
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``.
``session`` is the :class:`~pm3py.cli.rawcli.session.RawSession` whose ``entry_mode`` is
toggled and read."""
from prompt_toolkit.document import Document
@kb.add("c-/")
def _toggle(event):
session.toggle_entry_mode()
event.app.invalidate() # redraw the breadcrumb
def _regroup_after(event):
buf = event.current_buffer
buf.insert_text(event.data)
# only auto-space a bare digit run (no prefix), matching byte_space's guard
grouped = byte_space(buf.text, session.entry_mode)
if grouped != buf.text:
buf.document = Document(grouped, len(grouped))
for ch in sorted(_DIGITS[HEX] | _DIGITS[BIN]):
kb.add(ch)(_regroup_after)