aboutsummaryrefslogtreecommitdiff
path: root/src/input
diff options
context:
space:
mode:
authorakbash <akbash@herdr.dev>2026-07-14 22:46:33 +0300
committerGitHub <noreply@github.com>2026-07-14 22:46:33 +0300
commit30232287361f753d071ecd6bca7b9dda276ec0f9 (patch)
tree7e90d02be6993bfbc1de9b9e2a63774d2ee74149 /src/input
parentd482836a87e6fd5f884b595021578c0a39f16cf5 (diff)
fix: preserve associated emoji input (#1410)
refs #1404 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Diffstat (limited to 'src/input')
-rw-r--r--src/input/parse.rs44
1 files changed, 36 insertions, 8 deletions
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);