Skip to content

fix: use character as insert_text for character-valued ANSI_SEQUENCES entries - #2089

Open
dajiaohuang wants to merge 1 commit into
prompt-toolkit:mainfrom
dajiaohuang:fix-character-valued-ansi-sequence
Open

fix: use character as insert_text for character-valued ANSI_SEQUENCES entries#2089
dajiaohuang wants to merge 1 commit into
prompt-toolkit:mainfrom
dajiaohuang:fix-character-valued-ansi-sequence

Conversation

@dajiaohuang

Copy link
Copy Markdown

Description

When a character-valued entry is added to ANSI_SEQUENCES (e.g., for xterm modifyOtherKeys support), the KeyPress data should be the character, not the raw escape sequence. Otherwise, self-insert bindings will insert the escape sequence instead of the character.

Fixes

Fixes #2086

Changes

  • src/prompt_toolkit/input/vt100_parser.py: In _call_handler, when key is a plain character (not a Keys member), use the character as insert_text instead of the raw escape sequence
  • tests/test_inputstream.py: Add regression test test_character_valued_ansi_sequence

Behavior Change

Before: ANSI_SEQUENCES["\x1b[27;2;78~"] = "N" would cause KeyPress("N", "\x1b[27;2;78~") to be created, and self-insert would insert the raw escape sequence
After: KeyPress("N", "N") is created, and self-insert correctly inserts "N"

… entries

When a character-valued entry is added to ANSI_SEQUENCES (e.g., for
xterm modifyOtherKeys support), the KeyPress data should be the character,
not the raw escape sequence. Otherwise, self-insert bindings will insert
the escape sequence instead of the character.

Fixes prompt-toolkit#2086
@despotak

despotak commented Sep 6, 2026

Copy link
Copy Markdown

Thanks for picking this up — I filed #2086 and had not expected a fix this quickly.

I have verified the patch rather than just read it, because the issue was terse and I owed
it more evidence than I originally gave.

The bug reproduces on a clean install, no downstream patches, prompt_toolkit 3.0.52:

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

ANSI_SEQUENCES["\x1b[27;2;78~"] = "N"        # Shift+N via xterm modifyOtherKeys=2
out = []; Vt100Parser(out.append).feed("\x1b[27;2;78~")
# KeyPress(key='N', data='\x1b[27;2;78~')

self-insert inserts event.data, so the user gets ^[[27;2;78~ in the buffer instead of
N. It is still present on main at 583b341 (the 3.0.53 release commit).

With this PR applied to that commit:

KeyPress(key='N', data='N')

Test suite, run against the cloned source rather than an installed wheel (PYTHONPATH=src,
which matters — pointing pytest at the checkout without it silently tests the installed
copy):

main @ 583b341        156 passed
+ this PR            157 passed        the new regression test, no other change

And the new test is genuinely red on base — stashing only
src/prompt_toolkit/input/vt100_parser.py and keeping the test file fails it, so it pins
the behaviour rather than the implementation.

One data point on the shape of the fix, since if not isinstance(key, Keys): insert_text = key
is broader than the issue strictly requires.
A downstream project has been shipping an
equivalent patch in production since 2026-08-17 with two extra conditions — len(key) == 1
and insert_text.startswith("\x1b"). I expected that narrowing to matter and it does not:
running both against the same inputs on stock 3.0.52 they agree everywhere I could find to
look —

input both produce
character-valued entry ESC[27;2;78~ ('N', 'N')
plain typed a / 7 ('a', 'a') / ('7', '7')
a Keys entry ESC[A (Keys.Up, '\x1b[A')
an Alt chord (tuple) ESC b (Keys.Escape, '\x1b'), ('b', '')
bracketed paste (Keys.BracketedPaste, 'hi')
control char \x03 (Keys.ControlC, '\x03')

The tuple and bracketed-paste branches return before this line, and for a plain typed
character key and insert_text are already equal, so the assignment is a no-op there.
I could not construct a case that distinguishes the two. Reporting that as a
non-objection, not a suggestion.

Why it matters beyond the one-line repro: every project adding xterm modifyOtherKeys or
kitty CSI-u entries to ANSI_SEQUENCES hits this on the first Shift+symbol, because those
protocols report keys whose natural mapping is a plain character rather than a Keys
member. That is what the downstream patch above exists for, and it is why the entry point
is worth fixing in core rather than in each consumer.

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

2 participants