From 5f03930aa209578e9e678d5df5ce119ba7587d29 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 4 Dec 2023 11:46:47 +0000 Subject: [PATCH 01/12] 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. --- src/textual/_xterm_parser.py | 38 +++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/textual/_xterm_parser.py b/src/textual/_xterm_parser.py index 1b2b47a64f..1dfde3cf92 100644 --- a/src/textual/_xterm_parser.py +++ b/src/textual/_xterm_parser.py @@ -7,7 +7,7 @@ from . import events, messages from ._ansi_sequences import ANSI_SEQUENCES_KEYS 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 @@ -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) @@ -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? @@ -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 @@ -244,10 +258,20 @@ def _sequence_to_key_events( """ keys = ANSI_SEQUENCES_KEYS.get(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. - for key in keys: - yield events.Key(key.value, sequence if len(sequence) == 1 else None) + # If we're being asked to ignore the key... + if keys == (Keys.Ignore,): + # ...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.value, sequence) + else: + # 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. + for key in keys: + yield events.Key( + key.value, sequence if len(sequence) == 1 else None + ) return # If keys is a string, the intention is that it's a mapping to a # character, which should really be treated as the sequence for the From 6d41f1445cd5a48c659a1aedcb02054a15a08d3f Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 4 Dec 2023 13:36:41 +0000 Subject: [PATCH 02/12] 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. --- src/textual/_ansi_sequences.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/textual/_ansi_sequences.py b/src/textual/_ansi_sequences.py index a504ea718d..cfa4cc8328 100644 --- a/src/textual/_ansi_sequences.py +++ b/src/textual/_ansi_sequences.py @@ -170,12 +170,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 @@ -370,6 +364,15 @@ "\x1bOx": "8", "\x1bOy": "9", "\x1bOM": (Keys.Enter,), + ############################################################################ + # 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": (Keys.Ignore,), # Xterm. + "\x1b[G": (Keys.Ignore,), # Linux console. } # https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036 From 45cddead9fee1f899d5bcc07be441308e5f7a2ea Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 4 Dec 2023 13:47:59 +0000 Subject: [PATCH 03/12] =?UTF-8?q?Simplify=20opt+=C2=A7=20in=20WezTerm=20on?= =?UTF-8?q?=20macOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/textual/_ansi_sequences.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/textual/_ansi_sequences.py b/src/textual/_ansi_sequences.py index cfa4cc8328..1e2385138f 100644 --- a/src/textual/_ansi_sequences.py +++ b/src/textual/_ansi_sequences.py @@ -364,6 +364,7 @@ "\x1bOx": "8", "\x1bOy": "9", "\x1bOM": (Keys.Enter,), + "\x1b§": "§", # Opt+secton sign (WezTerm (20230712-072601-f4abf8fd)-Darwin) ############################################################################ # 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 From 82682cc8f0584ee953f35eb77ff91723540ce86a Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 4 Dec 2023 14:04:18 +0000 Subject: [PATCH 04/12] Improve the wezterm opt mappings This brings them more in line with other terminals I've tested. --- src/textual/_ansi_sequences.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/textual/_ansi_sequences.py b/src/textual/_ansi_sequences.py index 1e2385138f..ecd724cfcf 100644 --- a/src/textual/_ansi_sequences.py +++ b/src/textual/_ansi_sequences.py @@ -364,7 +364,19 @@ "\x1bOx": "8", "\x1bOy": "9", "\x1bOM": (Keys.Enter,), - "\x1b§": "§", # Opt+secton sign (WezTerm (20230712-072601-f4abf8fd)-Darwin) + "\x1b§": "§", # Opt+secton sign (WezTerm Darwin) + "\x1b1": "¡", # Opt+1 (WezTerm Darwin) + "\x1b2": "™", # Opt+2 (WezTerm Darwin) + "\x1b3": "£", # Opt+3 (WezTerm Darwin) + "\x1b4": "¢", # Opt+4 (WezTerm Darwin) + "\x1b5": "∞", # Opt+5 (WezTerm Darwin) + "\x1b6": "§", # Opt+6 (WezTerm Darwin) + "\x1b7": "¶", # Opt+7 (WezTerm Darwin) + "\x1b8": "•", # Opt+8 (WezTerm Darwin) + "\x1b9": "ª", # Opt+9 (WezTerm Darwin) + "\x1b0": "º", # Opt+0 (WezTerm Darwin) + "\x1b-": "–", # Opt+minus (WezTerm Darwin) + "\x1b=": "≠", # Opt+= (WezTerm Darwin) ############################################################################ # 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 From c88818623f7f59390d674ea3188605202d95370d Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 4 Dec 2023 14:11:27 +0000 Subject: [PATCH 05/12] Tidy up the explanation for the WezTerm mappings --- src/textual/_ansi_sequences.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/textual/_ansi_sequences.py b/src/textual/_ansi_sequences.py index ecd724cfcf..021026e618 100644 --- a/src/textual/_ansi_sequences.py +++ b/src/textual/_ansi_sequences.py @@ -364,19 +364,23 @@ "\x1bOx": "8", "\x1bOy": "9", "\x1bOM": (Keys.Enter,), - "\x1b§": "§", # Opt+secton sign (WezTerm Darwin) - "\x1b1": "¡", # Opt+1 (WezTerm Darwin) - "\x1b2": "™", # Opt+2 (WezTerm Darwin) - "\x1b3": "£", # Opt+3 (WezTerm Darwin) - "\x1b4": "¢", # Opt+4 (WezTerm Darwin) - "\x1b5": "∞", # Opt+5 (WezTerm Darwin) - "\x1b6": "§", # Opt+6 (WezTerm Darwin) - "\x1b7": "¶", # Opt+7 (WezTerm Darwin) - "\x1b8": "•", # Opt+8 (WezTerm Darwin) - "\x1b9": "ª", # Opt+9 (WezTerm Darwin) - "\x1b0": "º", # Opt+0 (WezTerm Darwin) - "\x1b-": "–", # Opt+minus (WezTerm Darwin) - "\x1b=": "≠", # Opt+= (WezTerm Darwin) + # WezTerm on macOS emits sequences for Opt and keys on the top numeric + # row; whereas other terminals provide various characters. The follow + # swallows 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=": "≠", ############################################################################ # 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 From 8e584a991f19202a35acc0c7b4b6c31df7df9ac6 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 4 Dec 2023 15:06:18 +0000 Subject: [PATCH 06/12] Ignore various ctrl-cmd- sequences from kitty --- src/textual/_ansi_sequences.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/textual/_ansi_sequences.py b/src/textual/_ansi_sequences.py index 021026e618..f548eea21e 100644 --- a/src/textual/_ansi_sequences.py +++ b/src/textual/_ansi_sequences.py @@ -390,6 +390,31 @@ # press of 5 on the numeric keypad, when *not* in number mode. "\x1b[E": (Keys.Ignore,), # Xterm. "\x1b[G": (Keys.Ignore,), # Linux console. + # Various ctrl+cmd+ keys under Kitty on macOS. + "\x1b[3;13": (Keys.Ignore,), # ctrl-cmd-del + "\x1b[1;13H": (Keys.Ignore,), # ctrl-cmd-home + "\x1b[1;13F": (Keys.Ignore,), # ctrl-cmd-end + "\x1b[5;13": (Keys.Ignore,), # ctrl-cmd-pgup + "\x1b[6;13": (Keys.Ignore,), # ctrl-cmd-pgdn + "\x1b[49;13u": (Keys.Ignore,), # ctrl-cmd-1 + "\x1b[50;13u": (Keys.Ignore,), # ctrl-cmd-2 + "\x1b[51;13u": (Keys.Ignore,), # ctrl-cmd-3 + "\x1b[52;13u": (Keys.Ignore,), # ctrl-cmd-4 + "\x1b[53;13u": (Keys.Ignore,), # ctrl-cmd-5 + "\x1b[54;13u": (Keys.Ignore,), # ctrl-cmd-6 + "\x1b[55;13u": (Keys.Ignore,), # ctrl-cmd-7 + "\x1b[56;13u": (Keys.Ignore,), # ctrl-cmd-8 + "\x1b[57;13u": (Keys.Ignore,), # ctrl-cmd-9 + "\x1b[48;13u": (Keys.Ignore,), # ctrl-cmd-0 + "\x1b[45;13u": (Keys.Ignore,), # ctrl-cmd-- + "\x1b[61;13u": (Keys.Ignore,), # ctrl-cmd-+ + "\x1b[91;13u": (Keys.Ignore,), # ctrl-cmd-[ + "\x1b[93;13u": (Keys.Ignore,), # ctrl-cmd-] + "\x1b[92;13u": (Keys.Ignore,), # ctrl-cmd-\ + "\x1b[39;13u": (Keys.Ignore,), # ctrl-cmd-' + "\x1b[59;13u": (Keys.Ignore,), # ctrl-cmd-; + "\x1b[47;13u": (Keys.Ignore,), # ctrl-cmd-/ + "\x1b[46;13u": (Keys.Ignore,), # ctrl-cmd-. } # https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036 From 257cb20294ab38ed27e68f6fb9e56a392e3256c8 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 4 Dec 2023 15:16:15 +0000 Subject: [PATCH 07/12] Fix a couple of incorrectly-copied sequences --- src/textual/_ansi_sequences.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/textual/_ansi_sequences.py b/src/textual/_ansi_sequences.py index f548eea21e..a33f46d095 100644 --- a/src/textual/_ansi_sequences.py +++ b/src/textual/_ansi_sequences.py @@ -391,11 +391,11 @@ "\x1b[E": (Keys.Ignore,), # Xterm. "\x1b[G": (Keys.Ignore,), # Linux console. # Various ctrl+cmd+ keys under Kitty on macOS. - "\x1b[3;13": (Keys.Ignore,), # ctrl-cmd-del + "\x1b[3;13~": (Keys.Ignore,), # ctrl-cmd-del "\x1b[1;13H": (Keys.Ignore,), # ctrl-cmd-home "\x1b[1;13F": (Keys.Ignore,), # ctrl-cmd-end - "\x1b[5;13": (Keys.Ignore,), # ctrl-cmd-pgup - "\x1b[6;13": (Keys.Ignore,), # ctrl-cmd-pgdn + "\x1b[5;13~": (Keys.Ignore,), # ctrl-cmd-pgup + "\x1b[6;13~": (Keys.Ignore,), # ctrl-cmd-pgdn "\x1b[49;13u": (Keys.Ignore,), # ctrl-cmd-1 "\x1b[50;13u": (Keys.Ignore,), # ctrl-cmd-2 "\x1b[51;13u": (Keys.Ignore,), # ctrl-cmd-3 From c0ddd76ffac86f001cf3cc23e5dcaca22ab31ac3 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 4 Dec 2023 15:35:21 +0000 Subject: [PATCH 08/12] =?UTF-8?q?Transform=20Ctrl+=C2=A7=20into=200=20unde?= =?UTF-8?q?r=20kitty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most (all?) other terminals on macOS do this anyway. --- src/textual/_ansi_sequences.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/textual/_ansi_sequences.py b/src/textual/_ansi_sequences.py index a33f46d095..7f9723a0b6 100644 --- a/src/textual/_ansi_sequences.py +++ b/src/textual/_ansi_sequences.py @@ -381,6 +381,8 @@ "\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 From 4967368382fa55d12b2fbe468e40bad7caf3acd0 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 4 Dec 2023 15:59:46 +0000 Subject: [PATCH 09/12] Tweak some wording --- src/textual/_ansi_sequences.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/textual/_ansi_sequences.py b/src/textual/_ansi_sequences.py index 7f9723a0b6..bcb1fda57f 100644 --- a/src/textual/_ansi_sequences.py +++ b/src/textual/_ansi_sequences.py @@ -365,8 +365,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 follow - # swallows up those sequences and turns them into characters the same as + # 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": "¡", From b9c0c0a134b7d9198e700da53385071405b39350 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Tue, 5 Dec 2023 09:39:21 +0000 Subject: [PATCH 10/12] Add Shift-F11 and Shift-F12 for rxvt --- src/textual/_ansi_sequences.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/textual/_ansi_sequences.py b/src/textual/_ansi_sequences.py index bcb1fda57f..39d01b52cd 100644 --- a/src/textual/_ansi_sequences.py +++ b/src/textual/_ansi_sequences.py @@ -112,6 +112,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,), From 8df1807585ec30710d62b857ec24b4a8936b2eb1 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Tue, 5 Dec 2023 13:00:50 +0000 Subject: [PATCH 11/12] 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. --- src/textual/_ansi_sequences.py | 61 ++++++++++++++++++---------------- src/textual/_xterm_parser.py | 27 +++++++-------- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/textual/_ansi_sequences.py b/src/textual/_ansi_sequences.py index 39d01b52cd..2c10a11a1f 100644 --- a/src/textual/_ansi_sequences.py +++ b/src/textual/_ansi_sequences.py @@ -1,11 +1,16 @@ from __future__ import annotations -from typing import Mapping, Tuple +from typing import Mapping, Tuple, Type from .keys import Keys + +class IgnoredSequence: + """Class used to mark 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 | Type[IgnoredSequence]] = { # Control keys. " ": (Keys.Space,), "\r": (Keys.Enter,), @@ -392,33 +397,33 @@ ############################################################################ # 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": (Keys.Ignore,), # Xterm. - "\x1b[G": (Keys.Ignore,), # Linux console. + "\x1b[E": IgnoredSequence, # Xterm. + "\x1b[G": IgnoredSequence, # Linux console. # Various ctrl+cmd+ keys under Kitty on macOS. - "\x1b[3;13~": (Keys.Ignore,), # ctrl-cmd-del - "\x1b[1;13H": (Keys.Ignore,), # ctrl-cmd-home - "\x1b[1;13F": (Keys.Ignore,), # ctrl-cmd-end - "\x1b[5;13~": (Keys.Ignore,), # ctrl-cmd-pgup - "\x1b[6;13~": (Keys.Ignore,), # ctrl-cmd-pgdn - "\x1b[49;13u": (Keys.Ignore,), # ctrl-cmd-1 - "\x1b[50;13u": (Keys.Ignore,), # ctrl-cmd-2 - "\x1b[51;13u": (Keys.Ignore,), # ctrl-cmd-3 - "\x1b[52;13u": (Keys.Ignore,), # ctrl-cmd-4 - "\x1b[53;13u": (Keys.Ignore,), # ctrl-cmd-5 - "\x1b[54;13u": (Keys.Ignore,), # ctrl-cmd-6 - "\x1b[55;13u": (Keys.Ignore,), # ctrl-cmd-7 - "\x1b[56;13u": (Keys.Ignore,), # ctrl-cmd-8 - "\x1b[57;13u": (Keys.Ignore,), # ctrl-cmd-9 - "\x1b[48;13u": (Keys.Ignore,), # ctrl-cmd-0 - "\x1b[45;13u": (Keys.Ignore,), # ctrl-cmd-- - "\x1b[61;13u": (Keys.Ignore,), # ctrl-cmd-+ - "\x1b[91;13u": (Keys.Ignore,), # ctrl-cmd-[ - "\x1b[93;13u": (Keys.Ignore,), # ctrl-cmd-] - "\x1b[92;13u": (Keys.Ignore,), # ctrl-cmd-\ - "\x1b[39;13u": (Keys.Ignore,), # ctrl-cmd-' - "\x1b[59;13u": (Keys.Ignore,), # ctrl-cmd-; - "\x1b[47;13u": (Keys.Ignore,), # ctrl-cmd-/ - "\x1b[46;13u": (Keys.Ignore,), # ctrl-cmd-. + "\x1b[3;13~": IgnoredSequence, # ctrl-cmd-del + "\x1b[1;13H": IgnoredSequence, # ctrl-cmd-home + "\x1b[1;13F": IgnoredSequence, # ctrl-cmd-end + "\x1b[5;13~": IgnoredSequence, # ctrl-cmd-pgup + "\x1b[6;13~": IgnoredSequence, # ctrl-cmd-pgdn + "\x1b[49;13u": IgnoredSequence, # ctrl-cmd-1 + "\x1b[50;13u": IgnoredSequence, # ctrl-cmd-2 + "\x1b[51;13u": IgnoredSequence, # ctrl-cmd-3 + "\x1b[52;13u": IgnoredSequence, # ctrl-cmd-4 + "\x1b[53;13u": IgnoredSequence, # ctrl-cmd-5 + "\x1b[54;13u": IgnoredSequence, # ctrl-cmd-6 + "\x1b[55;13u": IgnoredSequence, # ctrl-cmd-7 + "\x1b[56;13u": IgnoredSequence, # ctrl-cmd-8 + "\x1b[57;13u": IgnoredSequence, # ctrl-cmd-9 + "\x1b[48;13u": IgnoredSequence, # ctrl-cmd-0 + "\x1b[45;13u": IgnoredSequence, # ctrl-cmd-- + "\x1b[61;13u": IgnoredSequence, # ctrl-cmd-+ + "\x1b[91;13u": IgnoredSequence, # ctrl-cmd-[ + "\x1b[93;13u": IgnoredSequence, # ctrl-cmd-] + "\x1b[92;13u": IgnoredSequence, # ctrl-cmd-\ + "\x1b[39;13u": IgnoredSequence, # ctrl-cmd-' + "\x1b[59;13u": IgnoredSequence, # ctrl-cmd-; + "\x1b[47;13u": IgnoredSequence, # ctrl-cmd-/ + "\x1b[46;13u": IgnoredSequence, # ctrl-cmd-. } # https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036 diff --git a/src/textual/_xterm_parser.py b/src/textual/_xterm_parser.py index 1dfde3cf92..3490cc6367 100644 --- a/src/textual/_xterm_parser.py +++ b/src/textual/_xterm_parser.py @@ -5,7 +5,7 @@ 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, IgnoredSequence from ._parser import Awaitable, Parser, TokenCallback from .keys import KEY_NAME_REPLACEMENTS, Keys, _character_to_key @@ -257,21 +257,18 @@ def _sequence_to_key_events( Keys """ keys = ANSI_SEQUENCES_KEYS.get(sequence) + # If we're being asked to ignore the key... + if keys is IgnoredSequence: + # ...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 we're being asked to ignore the key... - if keys == (Keys.Ignore,): - # ...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.value, sequence) - else: - # 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. - for key in keys: - yield events.Key( - key.value, sequence if len(sequence) == 1 else None - ) + # 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. + for key in keys: + yield events.Key(key.value, sequence if len(sequence) == 1 else None) return # If keys is a string, the intention is that it's a mapping to a # character, which should really be treated as the sequence for the From a322b1eb7f4ec6b2f826c22c6975f3201624e8d0 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Tue, 2 Jan 2024 09:30:36 +0000 Subject: [PATCH 12/12] Swap to using a value as a flag, not a type https://github.com/Textualize/textual/pull/3800#pullrequestreview-1770853775 --- src/textual/_ansi_sequences.py | 62 +++++++++++++++++++--------------- src/textual/_xterm_parser.py | 4 +-- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/textual/_ansi_sequences.py b/src/textual/_ansi_sequences.py index 2c10a11a1f..f993b4ddab 100644 --- a/src/textual/_ansi_sequences.py +++ b/src/textual/_ansi_sequences.py @@ -1,6 +1,8 @@ from __future__ import annotations -from typing import Mapping, Tuple, Type +from typing import Mapping, Tuple + +from typing_extensions import Final from .keys import Keys @@ -9,8 +11,12 @@ 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 | Type[IgnoredSequence]] = { +ANSI_SEQUENCES_KEYS: Mapping[str, Tuple[Keys, ...] | str | IgnoredSequence] = { # Control keys. " ": (Keys.Space,), "\r": (Keys.Enter,), @@ -397,33 +403,33 @@ class IgnoredSequence: ############################################################################ # 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": IgnoredSequence, # Xterm. - "\x1b[G": IgnoredSequence, # Linux console. + "\x1b[E": IGNORE_SEQUENCE, # Xterm. + "\x1b[G": IGNORE_SEQUENCE, # Linux console. # Various ctrl+cmd+ keys under Kitty on macOS. - "\x1b[3;13~": IgnoredSequence, # ctrl-cmd-del - "\x1b[1;13H": IgnoredSequence, # ctrl-cmd-home - "\x1b[1;13F": IgnoredSequence, # ctrl-cmd-end - "\x1b[5;13~": IgnoredSequence, # ctrl-cmd-pgup - "\x1b[6;13~": IgnoredSequence, # ctrl-cmd-pgdn - "\x1b[49;13u": IgnoredSequence, # ctrl-cmd-1 - "\x1b[50;13u": IgnoredSequence, # ctrl-cmd-2 - "\x1b[51;13u": IgnoredSequence, # ctrl-cmd-3 - "\x1b[52;13u": IgnoredSequence, # ctrl-cmd-4 - "\x1b[53;13u": IgnoredSequence, # ctrl-cmd-5 - "\x1b[54;13u": IgnoredSequence, # ctrl-cmd-6 - "\x1b[55;13u": IgnoredSequence, # ctrl-cmd-7 - "\x1b[56;13u": IgnoredSequence, # ctrl-cmd-8 - "\x1b[57;13u": IgnoredSequence, # ctrl-cmd-9 - "\x1b[48;13u": IgnoredSequence, # ctrl-cmd-0 - "\x1b[45;13u": IgnoredSequence, # ctrl-cmd-- - "\x1b[61;13u": IgnoredSequence, # ctrl-cmd-+ - "\x1b[91;13u": IgnoredSequence, # ctrl-cmd-[ - "\x1b[93;13u": IgnoredSequence, # ctrl-cmd-] - "\x1b[92;13u": IgnoredSequence, # ctrl-cmd-\ - "\x1b[39;13u": IgnoredSequence, # ctrl-cmd-' - "\x1b[59;13u": IgnoredSequence, # ctrl-cmd-; - "\x1b[47;13u": IgnoredSequence, # ctrl-cmd-/ - "\x1b[46;13u": IgnoredSequence, # ctrl-cmd-. + "\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 diff --git a/src/textual/_xterm_parser.py b/src/textual/_xterm_parser.py index 3490cc6367..0a2ba5b045 100644 --- a/src/textual/_xterm_parser.py +++ b/src/textual/_xterm_parser.py @@ -5,7 +5,7 @@ from typing import Any, Callable, Generator, Iterable from . import events, messages -from ._ansi_sequences import ANSI_SEQUENCES_KEYS, IgnoredSequence +from ._ansi_sequences import ANSI_SEQUENCES_KEYS, IGNORE_SEQUENCE from ._parser import Awaitable, Parser, TokenCallback from .keys import KEY_NAME_REPLACEMENTS, Keys, _character_to_key @@ -258,7 +258,7 @@ def _sequence_to_key_events( """ keys = ANSI_SEQUENCES_KEYS.get(sequence) # If we're being asked to ignore the key... - if keys is IgnoredSequence: + 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