Translate "escape" to "^" when XTermParser has to backtrack

This commit is contained in:
Darren Burns
2022-06-11 13:38:59 +01:00
parent 2f2d064d34
commit 1510739227
2 changed files with 25 additions and 24 deletions

View File

@@ -1,18 +1,19 @@
from __future__ import annotations from __future__ import annotations
import re import re
from typing import Any, Callable, Generator, Iterable from typing import Any, Callable, Generator, Iterable
from . import messages
from . import events from . import events
from ._types import MessageTarget from . import messages
from ._parser import Awaitable, Parser, TokenCallback
from ._ansi_sequences import ANSI_SEQUENCES_KEYS from ._ansi_sequences import ANSI_SEQUENCES_KEYS
from ._parser import Awaitable, Parser, TokenCallback
from ._types import MessageTarget
# When trying to determine whether the current sequence is a supported/valid # When trying to determine whether the current sequence is a supported/valid
# escape sequence, at which length should we give up and consider our search # escape sequence, at which length should we give up and consider our search
# to be unsuccessful? # to be unsuccessful?
from .keys import Keys
_MAX_SEQUENCE_SEARCH_THRESHOLD = 20 _MAX_SEQUENCE_SEARCH_THRESHOLD = 20
_re_mouse_event = re.compile("^" + re.escape("\x1b[") + r"(<?[\d;]+[mM]|M...)\Z") _re_mouse_event = re.compile("^" + re.escape("\x1b[") + r"(<?[\d;]+[mM]|M...)\Z")
@@ -24,7 +25,6 @@ _re_bracketed_paste_end = re.compile(r"^\x1b\[201~$")
class XTermParser(Parser[events.Event]): class XTermParser(Parser[events.Event]):
_re_sgr_mouse = re.compile(r"\x1b\[<(\d+);(\d+);(\d+)([Mm])") _re_sgr_mouse = re.compile(r"\x1b\[<(\d+);(\d+);(\d+)([Mm])")
def __init__( def __init__(
@@ -144,12 +144,11 @@ class XTermParser(Parser[events.Event]):
or len(sequence) > _MAX_SEQUENCE_SEARCH_THRESHOLD or len(sequence) > _MAX_SEQUENCE_SEARCH_THRESHOLD
): ):
for character in sequence: for character in sequence:
keys = get_key_ansi_sequence(character, None) key_events = self._sequence_to_key_events(character)
if keys is not None: for event in key_events:
for key in keys: if event.key == "escape":
on_token(events.Key(self.sender, key=key)) event = events.Key(event.sender, key="^")
else: on_token(event)
on_token(events.Key(self.sender, key=character))
break break
sequence_character = yield read1() sequence_character = yield read1()
@@ -171,10 +170,10 @@ class XTermParser(Parser[events.Event]):
if not bracketed_paste: if not bracketed_paste:
# Was it a pressed key event that we received? # Was it a pressed key event that we received?
keys = get_key_ansi_sequence(sequence, None) key_events = list(self._sequence_to_key_events(sequence))
if keys is not None: for event in key_events:
for key in keys: on_token(event)
on_token(events.Key(self.sender, key=key)) if key_events:
break break
# Or a mouse event? # Or a mouse event?
mouse_match = _re_mouse_event.match(sequence) mouse_match = _re_mouse_event.match(sequence)
@@ -201,9 +200,11 @@ class XTermParser(Parser[events.Event]):
break break
else: else:
if not bracketed_paste: if not bracketed_paste:
keys = get_key_ansi_sequence(character, None) for event in self._sequence_to_key_events(character):
if keys is not None: on_token(event)
def _sequence_to_key_events(self, sequence: str) -> Iterable[events.Key]:
default = (sequence,) if len(sequence) == 1 else ()
keys = ANSI_SEQUENCES_KEYS.get(sequence, default)
for key in keys: for key in keys:
on_token(events.Key(self.sender, key=key)) yield events.Key(self.sender, key)
else:
on_token(events.Key(self.sender, key=character))

View File

@@ -67,8 +67,8 @@ def test_cant_match_escape_sequence_too_long(parser):
assert len(events) == len(sequence) assert len(events) == len(sequence)
assert all(isinstance(event, Key) for event in events) assert all(isinstance(event, Key) for event in events)
# '\x1b' is translated to 'escape' # When we backtrack '\x1b' is translated to '^'
assert events[0].key == "escape" assert events[0].key == "^"
# The rest of the characters correspond to the expected key presses # The rest of the characters correspond to the expected key presses
events = events[1:] events = events[1:]
@@ -87,7 +87,7 @@ def test_unknown_sequence_followed_by_known_sequence(parser):
sequence = unknown_sequence + known_sequence sequence = unknown_sequence + known_sequence
events = parser.feed(sequence) events = parser.feed(sequence)
assert next(events).key == "escape" assert next(events).key == "^"
assert next(events).key == "[" assert next(events).key == "["
assert next(events).key == "?" assert next(events).key == "?"
assert next(events).key == "end" assert next(events).key == "end"