Skip to content

Insert the character for character-valued ANSI_SEQUENCES entries - #2091

Open
yange0793-dot wants to merge 1 commit into
prompt-toolkit:mainfrom
yange0793-dot:character-valued-ansi-sequences
Open

Insert the character for character-valued ANSI_SEQUENCES entries#2091
yange0793-dot wants to merge 1 commit into
prompt-toolkit:mainfrom
yange0793-dot:character-valued-ansi-sequences

Conversation

@yange0793-dot

Copy link
Copy Markdown

Closes #2086.

Problem

ANSI_SEQUENCES maps an escape sequence to a key, and that key can be a plain character
rather than a Keys member — KeyPress explicitly supports both (":param key: A Keys
instance or text (one character)"
), and when constructed with only a key it sets
data = key for the character case.

Vt100Parser._call_handler always built KeyPress(key, insert_text), where insert_text
is the raw matched sequence. self-insert inserts event.data, so a character-valued
entry typed its own escape sequence:

from prompt_toolkit.input.ansi_escape_sequences import ANSI_SEQUENCES
from prompt_toolkit.input.vt100_parser import Vt100Parser

SEQ = "\x1b[27;2;78~"          # xterm with modifyOtherKeys=2 sends this for shift-N
ANSI_SEQUENCES[SEQ] = "N"

presses = []
p = Vt100Parser(presses.append)
p.feed(SEQ)
p.flush()
print(presses[0].key, repr(presses[0].data))
# before: N '\x1b[27;2;78~'   ->  a prompt inserts the sequence
# after:  N 'N'

This is invisible for every sequence prompt_toolkit ships, because those all resolve to
Keys members whose bindings never read data.

Fix

Pass the escape sequence as data only for Keys, and let KeyPress derive the data for
a character key. Ordinary typed characters are unchanged — _call_handler(prefix[0], prefix[0]) already had key and data equal — and Keys.BracketedPaste handling is
untouched.

Test

tests/test_inputstream.py::test_character_valued_sequence registers such an entry with
monkeypatch.setitem and asserts both key and data. On main it fails on the data
assertion.

Verification

Ran locally against 583b341, Python 3.14.3, darwin:

  • pytest tests/157 passed
  • ruff check . and ruff format --check . → clean
  • mypy --strict src/ on --platform win32/linux/darwin → only the pre-existing
    contrib/ssh/server.py:127 error (asyncssh not installed locally); no new errors

One open question

ANSI_SEQUENCES is still annotated dict[str, Keys | tuple[Keys, ...]], which does not
admit the character values this now handles. I left the annotation alone to keep the diff
small — widening it also means guarding _get_reverse_ansi_sequences, whose result is
dict[Keys, str]. Happy to do that here or in a follow-up if you'd like the type to
document the behaviour.

AI disclosure

Written with Claude Code (Claude Opus 5), credited in the commit trailer. The output
quoted above and the check results come from running the code locally, not from the model.

An `ANSI_SEQUENCES` entry can map a sequence to a plain character instead of
a `Keys` member, and `KeyPress` documents that for such a key the data is the
character itself. The parser always passed the matched escape sequence as
`data`, so `self-insert` typed that sequence into the buffer rather than the
character. Ordinary typed characters are unaffected: they already arrive with
key and data equal.

Closes prompt-toolkit#2086

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Character-valued ANSI_SEQUENCES entries insert their escape sequence instead of the character

1 participant