aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-08-29 00:06:44 +0300
committerGitHub <noreply@github.com>2026-08-29 00:06:44 +0300
commitc2637dc182ddc5425108824d5ed15d24ce38c4e3 (patch)
tree70d2a45ed633ccca75495d040d9237df2b4f4157 /src
parent82e6a80eb3ae39fb3d3ebd4d1fed19389767e605 (diff)
fix: handle associated text on special keys (#3350)
refs #3328
Diffstat (limited to 'src')
-rw-r--r--src/input/parse.rs33
1 files changed, 31 insertions, 2 deletions
diff --git a/src/input/parse.rs b/src/input/parse.rs
index ce87fd2f..15e0c6ad 100644
--- a/src/input/parse.rs
+++ b/src/input/parse.rs
@@ -203,7 +203,7 @@ fn parse_xterm_modified_special_sequence(data: &str) -> Option<TerminalKey> {
let suffix_char = body.chars().last()?;
if suffix_char.is_ascii_alphabetic() {
let modifier_and_event = body.strip_suffix(suffix_char)?;
- let (modifier_text, event_type) = split_modifier_and_event(modifier_and_event);
+ let (modifier_text, event_type) = split_xterm_modifier_and_event(modifier_and_event)?;
let mod_value = modifier_text.parse::<u8>().ok()?.checked_sub(1)?;
let code = match suffix_char {
'A' => KeyCode::Up,
@@ -227,7 +227,7 @@ fn parse_xterm_modified_special_sequence(data: &str) -> Option<TerminalKey> {
let tilde_body = body.strip_suffix('~')?;
let (code_part, modifier_part) = tilde_body.split_once(';')?;
- let (modifier_text, event_type) = split_modifier_and_event(modifier_part);
+ let (modifier_text, event_type) = split_xterm_modifier_and_event(modifier_part)?;
let mod_value = modifier_text.parse::<u8>().ok()?.checked_sub(1)?;
let code = match code_part {
"2" => KeyCode::Insert,
@@ -250,6 +250,19 @@ fn parse_xterm_modified_special_sequence(data: &str) -> Option<TerminalKey> {
)
}
+fn split_xterm_modifier_and_event(input: &str) -> Option<(&str, Option<&str>)> {
+ let modifier_and_event =
+ if let Some((modifier_and_event, associated_text)) = input.split_once(';') {
+ // Alacritty on macOS reports Cocoa function-key markers as associated
+ // text. The sequence final already identifies the key, so ignore them.
+ parse_kitty_associated_text(associated_text)?;
+ modifier_and_event
+ } else {
+ input
+ };
+ Some(split_modifier_and_event(modifier_and_event))
+}
+
fn split_modifier_and_event(input: &str) -> (&str, Option<&str>) {
match input.split_once(':') {
Some((modifier, event)) if !modifier.is_empty() => (modifier, Some(event)),
@@ -806,6 +819,22 @@ mod tests {
}
#[test]
+ fn parse_xterm_special_sequences_with_associated_text() {
+ for (sequence, expected_code) in [
+ ("\x1b[1;1;63233B", KeyCode::Down),
+ ("\x1b[5;1;63276~", KeyCode::PageUp),
+ ] {
+ let key = parse_terminal_key_sequence(sequence).unwrap();
+ assert_eq!(key.code, expected_code);
+ assert_eq!(key.modifiers, KeyModifiers::empty());
+ assert_eq!(key.kind, crossterm::event::KeyEventKind::Press);
+ assert_eq!(key.generated_text, None);
+ }
+
+ assert_eq!(parse_terminal_key_sequence("\x1b[1;1;63233;63234B"), None);
+ }
+
+ #[test]
fn parse_kitty_functional_up_arrow_sequence() {
let key = parse_terminal_key_sequence("\x1b[57419;1u").unwrap();
assert_eq!(key.code, KeyCode::Up);