-
Notifications
You must be signed in to change notification settings - Fork 829
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
XTerm parsing improvements #570
Changes from 1 commit
bfb962b
763c0d0
0125fbd
30b6a0b
2f2d064
1510739
13925b9
1b8781f
23855f1
f31a9d4
101558b
e1c8598
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,19 @@ | ||
from __future__ import annotations | ||
|
||
|
||
import re | ||
from typing import Any, Callable, Generator, Iterable | ||
|
||
from . import messages | ||
from . import events | ||
from ._types import MessageTarget | ||
from ._parser import Awaitable, Parser, TokenCallback | ||
from . import messages | ||
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 | ||
# escape sequence, at which length should we give up and consider our search | ||
# to be unsuccessful? | ||
from .keys import Keys | ||
|
||
_MAX_SEQUENCE_SEARCH_THRESHOLD = 20 | ||
|
||
_re_mouse_event = re.compile("^" + re.escape("\x1b[") + r"(<?[\d;]+[mM]|M...)\Z") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are two regexes for mouse events that run. This one suggests a mouse event can have any number of parameters, but when we parse the mouse event it assumes there are a fixed number. Is this one definitely required? Could we possibly get away with running just the 1 regex for mouse events rather than 2? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Quite possibly. IIRC there are at least two mouse codes that can be generated. At one point I thought I would have to support both, but everything supports the newer method. That might explain the dual regexes. |
||
|
@@ -24,7 +25,6 @@ | |
|
||
|
||
class XTermParser(Parser[events.Event]): | ||
|
||
_re_sgr_mouse = re.compile(r"\x1b\[<(\d+);(\d+);(\d+)([Mm])") | ||
|
||
def __init__( | ||
|
@@ -144,12 +144,11 @@ def parse(self, on_token: TokenCallback) -> Generator[Awaitable, str, None]: | |
or len(sequence) > _MAX_SEQUENCE_SEARCH_THRESHOLD | ||
): | ||
for character in sequence: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's some very similar code further down, I wonder if it could be hoisted in to a closure? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I considered this, but will do it now that I know I'm not the only one with that thought :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, you said closure... I've factored it out into a separate method |
||
keys = get_key_ansi_sequence(character, None) | ||
if keys is not None: | ||
for key in keys: | ||
on_token(events.Key(self.sender, key=key)) | ||
else: | ||
on_token(events.Key(self.sender, key=character)) | ||
key_events = self._sequence_to_key_events(character) | ||
for event in key_events: | ||
if event.key == "escape": | ||
event = events.Key(event.sender, key="^") | ||
on_token(event) | ||
break | ||
|
||
sequence_character = yield read1() | ||
|
@@ -171,10 +170,10 @@ def parse(self, on_token: TokenCallback) -> Generator[Awaitable, str, None]: | |
|
||
if not bracketed_paste: | ||
# Was it a pressed key event that we received? | ||
keys = get_key_ansi_sequence(sequence, None) | ||
if keys is not None: | ||
for key in keys: | ||
on_token(events.Key(self.sender, key=key)) | ||
key_events = list(self._sequence_to_key_events(sequence)) | ||
for event in key_events: | ||
on_token(event) | ||
if key_events: | ||
break | ||
# Or a mouse event? | ||
mouse_match = _re_mouse_event.match(sequence) | ||
|
@@ -201,9 +200,11 @@ def parse(self, on_token: TokenCallback) -> Generator[Awaitable, str, None]: | |
break | ||
else: | ||
if not bracketed_paste: | ||
keys = get_key_ansi_sequence(character, None) | ||
if keys is not None: | ||
for key in keys: | ||
on_token(events.Key(self.sender, key=key)) | ||
else: | ||
on_token(events.Key(self.sender, key=character)) | ||
for event in self._sequence_to_key_events(character): | ||
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: | ||
yield events.Key(self.sender, key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was 20 chosen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The largest that I think we could expect to receive (that I'm aware of) is length 14 (mouse events in large terminal windows). The existence of the
re_sgr_mouse
regex suggested to me that longer sequences could exist, so I added on some leeway for defensiveness.