fix(cli): make string-valued ANSI mappings insert the mapped character - #94076
Open
salch-cred wants to merge 1 commit into
Open
fix(cli): make string-valued ANSI mappings insert the mapped character#94076salch-cred wants to merge 1 commit into
salch-cred wants to merge 1 commit into
Conversation
Collaborator
Vt100Parser._call_handler always passes the raw matched byte sequence as KeyPress.data. For Keys.* targets that is harmless (bound handlers ignore data), but for mappings whose value is a plain string - Hermes' Shift+ letter table: ESC[27;2;97~ -> "A" - the default self-insert binding inserts event.data, i.e. the literal escape text. Shift+letter therefore still leaked "[27;2;97~" into the prompt buffer on Ghostty even though the sequence itself was decoded correctly (NousResearch#92343): the parser emitted the right key, but the data payload the insert path reads was never the mapped character. Add install_vt100_str_key_data_fix(), which patches _call_handler so a plain-string mapped value becomes its own data payload. Keys members are str subclasses, so they are excluded explicitly - every Keys.* mapping keeps its raw-byte data, and stock prompt_toolkit has zero plain-string mappings (all 180 string values are Keys members), so the blast radius is exactly Hermes' own tables. Wired into the existing startup install block in cli.py; idempotent.
salch-cred
force-pushed
the
fix/shift-letter-csi-self-insert
branch
from
August 24, 2026 19:09
78df404 to
8f5b5cb
Compare
Contributor
Author
|
Rebased on latest main — tests 4/4 passing. Thanks @isimoesg-amzn for the thorough independent verification (the A/B probe and the 384-test regression run are exactly the confidence this needed). The blast-radius argument holding on 3.0.52 is good to have on record. |
Collaborator
Contributor
Author
|
Saw the triage flag about #88097. My PR takes a different approach: instead of expanding the key-alias set, it patches the data payload at the parser level so ANY plain-string mapped value (not just the ones #88097 covers) inserts the mapped character. If #88097 is preferred, happy to consolidate — but the two fixes operate at different layers and could complement each other. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #92343
What & why
After #87511 mapped the Shift+letter CSI sequences in
ANSI_SEQUENCES, the parser emits the right key — butVt100Parser._call_handleralways passes the raw matched byte sequence asKeyPress.data. The default self-insert binding insertsevent.data, so Shift+letter still leaked[27;2;97~into the prompt buffer on Ghostty.Stock prompt_toolkit has zero plain-string mappings (all 180 string values in
ANSI_SEQUENCESareKeysmembers — verified against 3.0.52), so the only plain-string values are Hermes' own Shift+letter table. That gives the fix a perfect blast radius:install_vt100_str_key_data_fix()patches_call_handlerso a plain-string mapped value becomes its owndatapayload.Keysmembers are str subclasses and are excluded explicitly — everyKeys.*mapping keeps its raw-byte data. Wired into the existing startup install block incli.py; idempotent.How to test
Tests drive a real
Vt100Parserwith Hermes' actual mappings installed: without the fix, feedingESC[27;2;97~yieldsKeyPress("A", "\x1b[27;2;97~")(the leak); with it,KeyPress("A", "A"). Also covered:Keys.*data payloads unchanged, install idempotency.Manual repro: Ghostty +
hermes→ type Shift+M. Before:[27;2;109~lands in the buffer. After:M.Platforms tested