diff options
| author | akbash <akbash@herdr.dev> | 2026-07-14 22:46:33 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-14 22:46:33 +0300 |
| commit | 30232287361f753d071ecd6bca7b9dda276ec0f9 (patch) | |
| tree | 7e90d02be6993bfbc1de9b9e2a63774d2ee74149 | |
| parent | d482836a87e6fd5f884b595021578c0a39f16cf5 (diff) | |
fix: preserve associated emoji input (#1410)
refs #1404
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
| -rw-r--r-- | docs/next/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | src/input/parse.rs | 44 |
2 files changed, 37 insertions, 8 deletions
diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md index f6c060bf..51407fed 100644 --- a/docs/next/CHANGELOG.md +++ b/docs/next/CHANGELOG.md @@ -11,6 +11,7 @@ - Expanded Space and Agent sidebar entries now use a packed layout by default; set the corresponding `row_gap` to `1` to restore the previous spacing. (#873) ### Fixed +- Single-codepoint emoji chosen from the Windows emoji picker now reach panes when WezTerm's kitty keyboard support sends them as CSI-u events with associated text. (#1404) - Outer-terminal focus gained and lost reports now reach the focused pane when its application enables focus reporting, restoring Neovim file autoreload and other focus-aware terminal behavior. (#1337) - Native Windows servers now detach from the terminal console that launched them, so closing WezTerm, Windows Terminal, or another host terminal no longer stops persistent pane processes. (#1329) - Windows API clients now remain connected while waiting for initial named-pipe request bytes, so `status server`, `api snapshot`, and other socket commands no longer intermittently fail with BrokenPipe. (#1279) 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<TerminalKey> { fn parse_kitty_key_sequence(data: &str) -> Option<TerminalKey> { 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::<u8>().ok()?.checked_sub(1)?; + let (modifier_text, event_type) = split_modifier_and_event(modifier_part); + let modifier = modifier_text.parse::<u8>().ok()?.checked_sub(1)?; let mut key_fields = key_part.split(':'); let codepoint = key_fields.next()?.parse::<u32>().ok()?; @@ -30,6 +31,12 @@ fn parse_kitty_key_sequence(data: &str) -> Option<TerminalKey> { .filter(|field| !field.is_empty()) .and_then(|field| field.parse::<u32>().ok()); + if let Some(text) = associated_text { + if text.parse::<u32>().ok()? != codepoint { + return None; + } + } + let code = kitty_codepoint_to_keycode(codepoint)?; let kind = parse_kitty_event_type(event_type)?; @@ -593,6 +600,27 @@ mod tests { } #[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(); assert_eq!(key.code, KeyCode::Backspace); |
