pmacs/builtin
Levi Neuwirth 6c1631eaa8
fix(listview): tear the panel down when a `keys` bind is refused
PR #227 review, P2. `check_key_collisions` compared RAW TOKENS, but the
key parser canonicalizes first: `parse_key_code` (src/key.rs) uppercases
and folds `RET`/`RETURN`/`ENTER`, `SPC`/`SPACE`, `ESC`/`ESCAPE`,
`BS`/`BACKSPACE`, `DEL`/`DELETE` onto one `KeyCode`. So
`keys = { RETURN = ... }` compared unequal to every fixed token, passed
preflight, and was refused by `Keymap::bind` instead --- after the
buffer had been created, made read-only, marked round-trip and given the
fixed keymap, and before the panel was registered.

The old rollback unbound only the newly added keys, so the BUFFER
survived, owned by no `panels` record: unreachable, un-editable, and
findable by name --- which made the next `open` for that name
disambiguate itself to `<2>`. A rejected `keys` table silently renamed
the panel.

The comment directly above the check claimed the opposite guarantee ---
"Reject collisions BEFORE anything is created or bound, so a bad `keys`
table leaves no half-built panel behind" --- and that is corrected here
too, since a comment stating a belief is not code enforcing it.

APPROACH: the second of the two the review offered --- full teardown ---
rather than canonicalizing in preflight. Two reasons, both in the code:

* **A Lua canonicalizer would be a second copy of a Rust rule.** It
  would have to restate `parse_key_code`'s alias table, and the day the
  Rust table gains a name the Lua copy silently stops seeing that alias
  --- reintroducing exactly this bug for it. Deferring to `Keymap::bind`
  cannot go stale, because it IS the thing that decides.
* **There is no Lua-reachable canonicalization to use anyway.**
  `display_sequence` escapes only through `describe.key` and
  `keymap.list`, both of which require the sequence to be BOUND already.
  Reported rather than worked around, and no new binding added.

So the raw-token preflight stays, demoted to what it actually is: a
first pass that buys a better message ("that is the panel's own `g`")
and not safety. The construction block is now all-or-nothing, and the
teardown is `pmacs.buffer.kill`, which through `after_buffer_removed`
prunes the buffer's keymap scope, config locals and folds. `install_keys`
drops its own per-key rollback: two cleanup mechanisms for one failure
is how the weaker one came to be the only one that ran.

Witness: `g6_10c_an_alias_spelling_is_rejected_and_leaves_no_orphan_buffer`
walks every alias the parser folds onto a key the panel owns, asserting
after EACH that the buffer count is unchanged, then that a subsequent
legitimate open gets the plain name rather than `<2>`.

Bite: removing the teardown while keeping the raise fails it at the
buffer count (2 vs 1). Asserting only the error message would have
passed on the broken code --- it raised too; it just left wreckage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
2026-08-09 15:52:17 +02:00
..
api Fix CI and Documentation issues 2026-05-04 10:19:19 -04:00
commands feat(help): the discovery command family — P4 Stage 1 2026-07-31 19:04:48 -04:00
hooks wip(stage2a): the Lua half — hooks, LSP subscribers, applier origin 2026-07-29 18:09:03 -04:00
keymaps feat(find-file): open a file by path with C-x C-f 2026-07-25 10:45:50 -04:00
menus pmacs context menu: core clipboard + menu methods + Lua surface (Q#CM1/Q#CM3/Q#CM6) 2026-06-27 22:03:58 -04:00
packages/repl M7 tail: package system, audit lint, lockfile, resolver 2026-05-07 16:50:37 -04:00
queries/latex feat(latex): bundle LaTeX grammar + reconcile highlights + tests 2026-07-23 12:04:49 -04:00
runtime fix(listview): tear the panel down when a `keys` bind is refused 2026-08-09 15:52:17 +02:00