Skip to content

Commit

Permalink
Improve the method via which unsupported sequences are ignored (#3800)
Browse files Browse the repository at this point in the history
* 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)
  • Loading branch information
davep authored and willmcgugan committed Jan 11, 2024
1 parent 550ba85 commit 35ab613
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 11 deletions.
74 changes: 67 additions & 7 deletions src/textual/_ansi_sequences.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@

from typing import Mapping, Tuple

from typing_extensions import Final

from .keys import Keys


class IgnoredSequence:
"""Class used to mark that a sequence should be ignored."""


IGNORE_SEQUENCE: Final[IgnoredSequence] = IgnoredSequence()
"""Constant to indicate that a sequence should be ignored."""


# Mapping of vt100 escape codes to Keys.
ANSI_SEQUENCES_KEYS: Mapping[str, Tuple[Keys, ...] | str] = {
ANSI_SEQUENCES_KEYS: Mapping[str, Tuple[Keys, ...] | str | IgnoredSequence] = {
# Control keys.
" ": (Keys.Space,),
"\r": (Keys.Enter,),
Expand Down Expand Up @@ -112,6 +123,8 @@
"\x1b[21;2~": (Keys.F22,),
"\x1b[23;2~": (Keys.F23,),
"\x1b[24;2~": (Keys.F24,),
"\x1b[23$": (Keys.F23,), # rxvt
"\x1b[24$": (Keys.F24,), # rxvt
# --
# Control + function keys.
"\x1b[1;5P": (Keys.ControlF1,),
Expand Down Expand Up @@ -170,12 +183,6 @@
# Tmux (Win32 subsystem) sends the following scroll events.
"\x1b[62~": (Keys.ScrollUp,),
"\x1b[63~": (Keys.ScrollDown,),
# --
# Sequences generated by numpad 5. Not sure what it means. (It doesn't
# appear in 'infocmp'. Just ignore.
"\x1b[E": (Keys.Ignore,), # Xterm.
"\x1b[G": (Keys.Ignore,), # Linux console.
# --
# Meta/control/escape + pageup/pagedown/insert/delete.
"\x1b[3;2~": (Keys.ShiftDelete,), # xterm, gnome-terminal.
"\x1b[3$": (Keys.ShiftDelete,), # rxvt
Expand Down Expand Up @@ -370,6 +377,59 @@
"\x1bOx": "8",
"\x1bOy": "9",
"\x1bOM": (Keys.Enter,),
# WezTerm on macOS emits sequences for Opt and keys on the top numeric
# row; whereas other terminals provide various characters. The following
# swallow up those sequences and turns them into characters the same as
# the other terminals.
"\x1b§": "§",
"\x1b1": "¡",
"\x1b2": "™",
"\x1b3": "£",
"\x1b4": "¢",
"\x1b5": "∞",
"\x1b6": "§",
"\x1b7": "¶",
"\x1b8": "•",
"\x1b9": "ª",
"\x1b0": "º",
"\x1b-": "–",
"\x1b=": "≠",
# Ctrl+§ on kitty is different from most other terminals on macOS.
"\x1b[167;5u": "0",
############################################################################
# The ignore section. Only add sequences here if they are going to be
# ignored. Also, when adding a sequence here, please include a note as
# to why it is being ignored; ideally citing sources if possible.
############################################################################
# The following 2 are inherited from prompt toolkit. They relate to a
# press of 5 on the numeric keypad, when *not* in number mode.
"\x1b[E": IGNORE_SEQUENCE, # Xterm.
"\x1b[G": IGNORE_SEQUENCE, # Linux console.
# Various ctrl+cmd+ keys under Kitty on macOS.
"\x1b[3;13~": IGNORE_SEQUENCE, # ctrl-cmd-del
"\x1b[1;13H": IGNORE_SEQUENCE, # ctrl-cmd-home
"\x1b[1;13F": IGNORE_SEQUENCE, # ctrl-cmd-end
"\x1b[5;13~": IGNORE_SEQUENCE, # ctrl-cmd-pgup
"\x1b[6;13~": IGNORE_SEQUENCE, # ctrl-cmd-pgdn
"\x1b[49;13u": IGNORE_SEQUENCE, # ctrl-cmd-1
"\x1b[50;13u": IGNORE_SEQUENCE, # ctrl-cmd-2
"\x1b[51;13u": IGNORE_SEQUENCE, # ctrl-cmd-3
"\x1b[52;13u": IGNORE_SEQUENCE, # ctrl-cmd-4
"\x1b[53;13u": IGNORE_SEQUENCE, # ctrl-cmd-5
"\x1b[54;13u": IGNORE_SEQUENCE, # ctrl-cmd-6
"\x1b[55;13u": IGNORE_SEQUENCE, # ctrl-cmd-7
"\x1b[56;13u": IGNORE_SEQUENCE, # ctrl-cmd-8
"\x1b[57;13u": IGNORE_SEQUENCE, # ctrl-cmd-9
"\x1b[48;13u": IGNORE_SEQUENCE, # ctrl-cmd-0
"\x1b[45;13u": IGNORE_SEQUENCE, # ctrl-cmd--
"\x1b[61;13u": IGNORE_SEQUENCE, # ctrl-cmd-+
"\x1b[91;13u": IGNORE_SEQUENCE, # ctrl-cmd-[
"\x1b[93;13u": IGNORE_SEQUENCE, # ctrl-cmd-]
"\x1b[92;13u": IGNORE_SEQUENCE, # ctrl-cmd-\
"\x1b[39;13u": IGNORE_SEQUENCE, # ctrl-cmd-'
"\x1b[59;13u": IGNORE_SEQUENCE, # ctrl-cmd-;
"\x1b[47;13u": IGNORE_SEQUENCE, # ctrl-cmd-/
"\x1b[46;13u": IGNORE_SEQUENCE, # ctrl-cmd-.
}

# https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036
Expand Down
29 changes: 25 additions & 4 deletions src/textual/_xterm_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
from typing import Any, Callable, Generator, Iterable

from . import events, messages
from ._ansi_sequences import ANSI_SEQUENCES_KEYS
from ._ansi_sequences import ANSI_SEQUENCES_KEYS, IGNORE_SEQUENCE
from ._parser import Awaitable, Parser, TokenCallback
from .keys import KEY_NAME_REPLACEMENTS, _character_to_key
from .keys import KEY_NAME_REPLACEMENTS, Keys, _character_to_key

# 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
Expand Down Expand Up @@ -100,6 +100,20 @@ def parse(self, on_token: TokenCallback) -> Generator[Awaitable, str, None]:
bracketed_paste = False
use_prior_escape = False

def on_key_token(event: events.Key) -> None:
"""Token callback wrapper for handling keys.
Args:
event: The key event to send to the callback.
This wrapper looks for keys that should be ignored, and filters
them out, logging the ignored sequence when it does.
"""
if event.key == Keys.Ignore:
self.debug_log(f"ignored={event.character!r}")
else:
on_token(event)

def reissue_sequence_as_keys(reissue_sequence: str) -> None:
if self._reissued_sequence_debug_book is not None:
self._reissued_sequence_debug_book(reissue_sequence)
Expand Down Expand Up @@ -204,7 +218,7 @@ def reissue_sequence_as_keys(reissue_sequence: str) -> None:
# Was it a pressed key event that we received?
key_events = list(sequence_to_key_events(sequence))
for key_event in key_events:
on_token(key_event)
on_key_token(key_event)
if key_events:
break
# Or a mouse event?
Expand All @@ -229,7 +243,7 @@ def reissue_sequence_as_keys(reissue_sequence: str) -> None:
else:
if not bracketed_paste:
for event in sequence_to_key_events(character):
on_token(event)
on_key_token(event)

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

0 comments on commit 35ab613

Please sign in to comment.