From 30232287361f753d071ecd6bca7b9dda276ec0f9 Mon Sep 17 00:00:00 2001 From: akbash Date: Tue, 14 Jul 2026 22:46:33 +0300 Subject: fix: preserve associated emoji input (#1410) refs #1404 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> --- src/input/parse.rs | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) (limited to 'src/input') diff --git a/src/input/parse.rs b/src/input/parse.rs index 77c630ba..0071596b 100644 --- a/src/input/parse.rs +++ b/src/input/parse.rs @@ -13,15 +13,16 @@ pub fn parse_terminal_key_sequence(data: &str) -> Option { fn parse_kitty_key_sequence(data: &str) -> Option { let body = data.strip_prefix("\x1b[")?.strip_suffix('u')?; - let (main, event_type) = match body.rsplit_once(':') { - Some((head, tail)) if tail.chars().all(|ch| ch.is_ascii_digit()) && head.contains(';') => { - (head, Some(tail)) - } - _ => (body, None), - }; + let mut fields = body.split(';'); + let key_part = fields.next()?; + let modifier_part = fields.next().unwrap_or("1"); + let associated_text = fields.next(); + if fields.next().is_some() { + return None; + } - let (key_part, modifier_part) = main.rsplit_once(';').unwrap_or((main, "1")); - let modifier = modifier_part.parse::().ok()?.checked_sub(1)?; + let (modifier_text, event_type) = split_modifier_and_event(modifier_part); + let modifier = modifier_text.parse::().ok()?.checked_sub(1)?; let mut key_fields = key_part.split(':'); let codepoint = key_fields.next()?.parse::().ok()?; @@ -30,6 +31,12 @@ fn parse_kitty_key_sequence(data: &str) -> Option { .filter(|field| !field.is_empty()) .and_then(|field| field.parse::().ok()); + if let Some(text) = associated_text { + if text.parse::().ok()? != codepoint { + return None; + } + } + let code = kitty_codepoint_to_keycode(codepoint)?; let kind = parse_kitty_event_type(event_type)?; @@ -592,6 +599,27 @@ mod tests { assert_eq!(key.shifted_codepoint, Some('L' as u32)); } + #[test] + fn parse_kitty_sequence_with_associated_emoji_text() { + let key = parse_terminal_key_sequence("\x1b[128512;1;128512u").unwrap(); + assert_terminal_key_eq( + key, + KeyCode::Char('😀'), + KeyModifiers::empty(), + crossterm::event::KeyEventKind::Press, + None, + ); + } + + #[test] + fn reject_unmodeled_kitty_associated_text() { + assert_eq!(parse_terminal_key_sequence("\x1b[128512;1;128513u"), None); + assert_eq!( + parse_terminal_key_sequence("\x1b[128512;1;128512:65039u"), + None + ); + } + #[test] fn parse_kitty_alt_backspace_sequence() { let key = parse_terminal_key_sequence("\x1b[127;3u").unwrap(); -- cgit v1.2.3