refactor(help): a chord is recognised by its modifiers, not its spelling (improvise-thpe)

`compact` tested whether a label it had just formatted began with "Ctrl-" or
"Alt-". No false positive today — the reviewer enumerated every
`format_key_label` output — so this is the shape, not the behaviour.

`Hint::keys` is now `(KeyCode, KeyModifiers)` and `compact` takes the pairs, so
the string is not in scope to parse; `Hint::labels` and the help pages format
last through the one spelling in `keymap::key_label`. The regression guard is
the signature, as it is for `Checked`.

`Keymap::labels_for` — the "throw the modifiers away" step the ticket names —
has no caller left and is deleted: a consolidation that keeps its old path keeps
the defect available.

The rule also got wider and total over the bitset: a chord is any modifier
beyond shift. Injected a temporary `KeyModifiers::SUPER` spelled `Super-x` and
ran it — the old filter keeps it, `compact` drops it. Binding dump byte-identical
across the change (4406 lines).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
This commit is contained in:
Edward Langley
2026-09-21 03:57:32 -07:00
parent 8e9842b89f
commit 877d3e193b
6 changed files with 121 additions and 33 deletions
+40
View File
@@ -462,3 +462,43 @@ against an enum that really did have the variant.
Proved discriminating by re-adding the variant with an empty `apply` arm: the
retired spelling parses again and the test fails on `expect_err`.
### a_chord_is_recognised_by_its_modifiers_and_not_by_its_label
`help::compact` dropped a chorded key from a status-bar hint by testing whether
the formatted label started with `"Ctrl-"` or `"Alt-"` — parsing a string it had
just formatted. Finding 3 of the adversarial review of PR #50, filed as
improvise-thpe and deliberately not fixed there: the reviewer enumerated every
`format_key_label` output and none begins with either, so there was **no false
positive**. The defect was the shape.
**The regression guard is the signature**, as it is for `Checked` (gotcha 43):
`compact` now takes `&[(KeyCode, KeyModifiers)]`, so the string it used to parse
is not in scope to parse. `keys_for` returns the pairs and `Hint::keys` carries
them; `help.rs` formats last, through `Hint::labels` and the one spelling in
`keymap::key_label`. `Keymap::labels_for` — the "throw the modifiers away into a
string" step the ticket names — has **no caller left and is deleted**, because a
consolidation that keeps its old path keeps the defect available
(`doctrine(a_consolidation_that_keeps_its_compatibility_test_is_unfinished)`).
The rule also got wider, and that is the part a test can state: a chord is any
modifier **beyond shift**, which is total over the bitset, where the string test
enumerated two prefixes. Shift alone is not a chord — it is how a key is named
rather than how it is held, which `S-Tab` is the case for.
**Injected fault, run rather than argued.** A `KeyModifiers::SUPER` bit and a
`key_label` arm spelling it `Super-x` were added temporarily. The old filter
keeps it — `["T", "Super-x"]`, two survivors where one is wanted — while
`compact` returns `"T"`. That is the ticket's own reproduction, executed, and it
is the only way to see the difference: with `KeyModifiers` as shipped, every
chord's label *does* begin with `Ctrl-` or `Alt-`, so no permanent test can
separate the two rules. Both the bit and the probe were removed afterwards.
One behaviour that had to be preserved deliberately: `keys_for` used to `dedup`
**labels**, and deduping the pairs instead would let two patterns that spell the
same key both reach the status bar. It now dedups by label explicitly.
The binding dump is byte-identical across this change (4406 lines, `diff`
clean), which is what `doctrine(binding_dump_diff)` asks of anything touching
the keymap: the dump's prefix rows go through `Hint`, so they had to be checked
rather than assumed.