Skip to content

Commit 5cd5aa9

Browse files
authored
Improve the method via which unsupported sequences are ignored (#3800)
* Make ignoring keys a silent operation But when we do so, ensure that we log what sequence was ignored if the key driver logging is enabled. The core requirement for #3742. * Start a proper ignore section in the dictionary of sequences And by "proper ignore section" I simply mean: have the sequences we want to ignore all gathered together in a really obvious location, and with some sort of explanation. * Simplify opt+§ in WezTerm on macOS * Improve the wezterm opt mappings This brings them more in line with other terminals I've tested. * Tidy up the explanation for the WezTerm mappings * Ignore various ctrl-cmd- sequences from kitty * Fix a couple of incorrectly-copied sequences * Transform Ctrl+§ into 0 under kitty Most (all?) other terminals on macOS do this anyway. * Tweak some wording * Add Shift-F11 and Shift-F12 for rxvt * Switch to using a special value for ignored sequences Rather than use the pre-existing convention of a tuple that contains Keys.Ignore, which *could* imply that a sequence maps to a set of keys that happens to include Key.Ignore, but isn't exclusive to just that, here we swap over to using a single special value for an ignored sequence. * Swap to using a value as a flag, not a type #3800 (review)
1 parent baee05a commit 5cd5aa9

File tree

2 files changed

+92
-11
lines changed

2 files changed

+92
-11
lines changed

src/textual/_ansi_sequences.py

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,21 @@
22

33
from typing import Mapping, Tuple
44

5+
from typing_extensions import Final
6+
57
from .keys import Keys
68

9+
10+
class IgnoredSequence:
11+
"""Class used to mark that a sequence should be ignored."""
12+
13+
14+
IGNORE_SEQUENCE: Final[IgnoredSequence] = IgnoredSequence()
15+
"""Constant to indicate that a sequence should be ignored."""
16+
17+
718
# Mapping of vt100 escape codes to Keys.
8-
ANSI_SEQUENCES_KEYS: Mapping[str, Tuple[Keys, ...] | str] = {
19+
ANSI_SEQUENCES_KEYS: Mapping[str, Tuple[Keys, ...] | str | IgnoredSequence] = {
920
# Control keys.
1021
" ": (Keys.Space,),
1122
"\r": (Keys.Enter,),
@@ -112,6 +123,8 @@
112123
"\x1b[21;2~": (Keys.F22,),
113124
"\x1b[23;2~": (Keys.F23,),
114125
"\x1b[24;2~": (Keys.F24,),
126+
"\x1b[23$": (Keys.F23,), # rxvt
127+
"\x1b[24$": (Keys.F24,), # rxvt
115128
# --
116129
# Control + function keys.
117130
"\x1b[1;5P": (Keys.ControlF1,),
@@ -170,12 +183,6 @@
170183
# Tmux (Win32 subsystem) sends the following scroll events.
171184
"\x1b[62~": (Keys.ScrollUp,),
172185
"\x1b[63~": (Keys.ScrollDown,),
173-
# --
174-
# Sequences generated by numpad 5. Not sure what it means. (It doesn't
175-
# appear in 'infocmp'. Just ignore.
176-
"\x1b[E": (Keys.Ignore,), # Xterm.
177-
"\x1b[G": (Keys.Ignore,), # Linux console.
178-
# --
179186
# Meta/control/escape + pageup/pagedown/insert/delete.
180187
"\x1b[3;2~": (Keys.ShiftDelete,), # xterm, gnome-terminal.
181188
"\x1b[3$": (Keys.ShiftDelete,), # rxvt
@@ -370,6 +377,59 @@
370377
"\x1bOx": "8",
371378
"\x1bOy": "9",
372379
"\x1bOM": (Keys.Enter,),
380+
# WezTerm on macOS emits sequences for Opt and keys on the top numeric
381+
# row; whereas other terminals provide various characters. The following
382+
# swallow up those sequences and turns them into characters the same as
383+
# the other terminals.
384+
"\x1b§": "§",
385+
"\x1b1": "¡",
386+
"\x1b2": "™",
387+
"\x1b3": "£",
388+
"\x1b4": "¢",
389+
"\x1b5": "∞",
390+
"\x1b6": "§",
391+
"\x1b7": "¶",
392+
"\x1b8": "•",
393+
"\x1b9": "ª",
394+
"\x1b0": "º",
395+
"\x1b-": "–",
396+
"\x1b=": "≠",
397+
# Ctrl+§ on kitty is different from most other terminals on macOS.
398+
"\x1b[167;5u": "0",
399+
############################################################################
400+
# The ignore section. Only add sequences here if they are going to be
401+
# ignored. Also, when adding a sequence here, please include a note as
402+
# to why it is being ignored; ideally citing sources if possible.
403+
############################################################################
404+
# The following 2 are inherited from prompt toolkit. They relate to a
405+
# press of 5 on the numeric keypad, when *not* in number mode.
406+
"\x1b[E": IGNORE_SEQUENCE, # Xterm.
407+
"\x1b[G": IGNORE_SEQUENCE, # Linux console.
408+
# Various ctrl+cmd+ keys under Kitty on macOS.
409+
"\x1b[3;13~": IGNORE_SEQUENCE, # ctrl-cmd-del
410+
"\x1b[1;13H": IGNORE_SEQUENCE, # ctrl-cmd-home
411+
"\x1b[1;13F": IGNORE_SEQUENCE, # ctrl-cmd-end
412+
"\x1b[5;13~": IGNORE_SEQUENCE, # ctrl-cmd-pgup
413+
"\x1b[6;13~": IGNORE_SEQUENCE, # ctrl-cmd-pgdn
414+
"\x1b[49;13u": IGNORE_SEQUENCE, # ctrl-cmd-1
415+
"\x1b[50;13u": IGNORE_SEQUENCE, # ctrl-cmd-2
416+
"\x1b[51;13u": IGNORE_SEQUENCE, # ctrl-cmd-3
417+
"\x1b[52;13u": IGNORE_SEQUENCE, # ctrl-cmd-4
418+
"\x1b[53;13u": IGNORE_SEQUENCE, # ctrl-cmd-5
419+
"\x1b[54;13u": IGNORE_SEQUENCE, # ctrl-cmd-6
420+
"\x1b[55;13u": IGNORE_SEQUENCE, # ctrl-cmd-7
421+
"\x1b[56;13u": IGNORE_SEQUENCE, # ctrl-cmd-8
422+
"\x1b[57;13u": IGNORE_SEQUENCE, # ctrl-cmd-9
423+
"\x1b[48;13u": IGNORE_SEQUENCE, # ctrl-cmd-0
424+
"\x1b[45;13u": IGNORE_SEQUENCE, # ctrl-cmd--
425+
"\x1b[61;13u": IGNORE_SEQUENCE, # ctrl-cmd-+
426+
"\x1b[91;13u": IGNORE_SEQUENCE, # ctrl-cmd-[
427+
"\x1b[93;13u": IGNORE_SEQUENCE, # ctrl-cmd-]
428+
"\x1b[92;13u": IGNORE_SEQUENCE, # ctrl-cmd-\
429+
"\x1b[39;13u": IGNORE_SEQUENCE, # ctrl-cmd-'
430+
"\x1b[59;13u": IGNORE_SEQUENCE, # ctrl-cmd-;
431+
"\x1b[47;13u": IGNORE_SEQUENCE, # ctrl-cmd-/
432+
"\x1b[46;13u": IGNORE_SEQUENCE, # ctrl-cmd-.
373433
}
374434

375435
# https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036

src/textual/_xterm_parser.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
from typing import Any, Callable, Generator, Iterable
66

77
from . import events, messages
8-
from ._ansi_sequences import ANSI_SEQUENCES_KEYS
8+
from ._ansi_sequences import ANSI_SEQUENCES_KEYS, IGNORE_SEQUENCE
99
from ._parser import Awaitable, Parser, TokenCallback
10-
from .keys import KEY_NAME_REPLACEMENTS, _character_to_key
10+
from .keys import KEY_NAME_REPLACEMENTS, Keys, _character_to_key
1111

1212
# When trying to determine whether the current sequence is a supported/valid
1313
# escape sequence, at which length should we give up and consider our search
@@ -100,6 +100,20 @@ def parse(self, on_token: TokenCallback) -> Generator[Awaitable, str, None]:
100100
bracketed_paste = False
101101
use_prior_escape = False
102102

103+
def on_key_token(event: events.Key) -> None:
104+
"""Token callback wrapper for handling keys.
105+
106+
Args:
107+
event: The key event to send to the callback.
108+
109+
This wrapper looks for keys that should be ignored, and filters
110+
them out, logging the ignored sequence when it does.
111+
"""
112+
if event.key == Keys.Ignore:
113+
self.debug_log(f"ignored={event.character!r}")
114+
else:
115+
on_token(event)
116+
103117
def reissue_sequence_as_keys(reissue_sequence: str) -> None:
104118
if self._reissued_sequence_debug_book is not None:
105119
self._reissued_sequence_debug_book(reissue_sequence)
@@ -204,7 +218,7 @@ def reissue_sequence_as_keys(reissue_sequence: str) -> None:
204218
# Was it a pressed key event that we received?
205219
key_events = list(sequence_to_key_events(sequence))
206220
for key_event in key_events:
207-
on_token(key_event)
221+
on_key_token(key_event)
208222
if key_events:
209223
break
210224
# Or a mouse event?
@@ -229,7 +243,7 @@ def reissue_sequence_as_keys(reissue_sequence: str) -> None:
229243
else:
230244
if not bracketed_paste:
231245
for event in sequence_to_key_events(character):
232-
on_token(event)
246+
on_key_token(event)
233247

234248
def _sequence_to_key_events(
235249
self, sequence: str, _unicode_name=unicodedata.name
@@ -243,6 +257,13 @@ def _sequence_to_key_events(
243257
Keys
244258
"""
245259
keys = ANSI_SEQUENCES_KEYS.get(sequence)
260+
# If we're being asked to ignore the key...
261+
if keys is IGNORE_SEQUENCE:
262+
# ...build a special ignore key event, which has the ignore
263+
# name as the key (that is, the key this sequence is bound
264+
# to is the ignore key) and the sequence that was ignored as
265+
# the character.
266+
yield events.Key(Keys.Ignore, sequence)
246267
if isinstance(keys, tuple):
247268
# If the sequence mapped to a tuple, then it's values from the
248269
# `Keys` enum. Raise key events from what we find in the tuple.

0 commit comments

Comments
 (0)