From a132b8ea43a0a891a99ef3311c869d99639b25a5 Mon Sep 17 00:00:00 2001 From: yange0793-dot Date: Sat, 5 Sep 2026 13:30:38 +0800 Subject: [PATCH] insert the character for character-valued `ANSI_SEQUENCES` entries 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 #2086 Co-Authored-By: Claude Opus 5 (1M context) --- src/prompt_toolkit/input/vt100_parser.py | 8 +++++++- tests/test_inputstream.py | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/prompt_toolkit/input/vt100_parser.py b/src/prompt_toolkit/input/vt100_parser.py index 34ea11057..0f7c4dfe0 100644 --- a/src/prompt_toolkit/input/vt100_parser.py +++ b/src/prompt_toolkit/input/vt100_parser.py @@ -187,8 +187,14 @@ def _call_handler( if key == Keys.BracketedPaste: self._in_bracketed_paste = True self._paste_buffer = "" - else: + elif isinstance(key, Keys): self.feed_key_callback(KeyPress(key, insert_text)) + else: + # An `ANSI_SEQUENCES` entry can map a sequence to a plain + # character instead of a `Keys`. For those, `KeyPress` data is + # the character itself (that's what `self-insert` inserts); + # passing `insert_text` would type the escape sequence. + self.feed_key_callback(KeyPress(key)) def feed(self, data: str) -> None: """ diff --git a/tests/test_inputstream.py b/tests/test_inputstream.py index ab1b03689..b6a4a221a 100644 --- a/tests/test_inputstream.py +++ b/tests/test_inputstream.py @@ -2,6 +2,7 @@ import pytest +from prompt_toolkit.input.ansi_escape_sequences import ANSI_SEQUENCES from prompt_toolkit.input.vt100_parser import Vt100Parser from prompt_toolkit.keys import Keys @@ -125,6 +126,20 @@ def test_invalid(processor, stream): assert processor.keys[2].key == "*" +def test_character_valued_sequence(processor, stream, monkeypatch): + # A sequence can be mapped to a plain character instead of a `Keys`. What + # gets inserted is the character, not the sequence that produced it. + sequence = "\x1b[27;2;78~" + monkeypatch.setitem(ANSI_SEQUENCES, sequence, "N") + + stream.feed(sequence) + stream.flush() + + assert len(processor.keys) == 1 + assert processor.keys[0].key == "N" + assert processor.keys[0].data == "N" + + def test_cpr_response(processor, stream): stream.feed("a\x1b[40;10Rb") assert len(processor.keys) == 3