From 29fd06747b31e8ec7fbf32af662263040783b6c7 Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Fri, 22 May 2026 15:18:08 +0300 Subject: fix: accept unicode keybindings refs #247 --- src/raw_input.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) (limited to 'src/raw_input.rs') diff --git a/src/raw_input.rs b/src/raw_input.rs index fa496b95..d75c6c49 100644 --- a/src/raw_input.rs +++ b/src/raw_input.rs @@ -231,6 +231,11 @@ pub(crate) fn flush_incomplete_input_bytes(buffer: &mut Vec) -> Option Option { return (buffer.len() >= 3).then_some(3); } - Some(2) + let escaped_char_width = utf8_char_width(buffer[1])?; + if buffer.len() < 1 + escaped_char_width { + return None; + } + std::str::from_utf8(&buffer[1..1 + escaped_char_width]).ok()?; + Some(1 + escaped_char_width) } fn find_osc_terminator(buffer: &[u8]) -> Option { @@ -1045,6 +1055,41 @@ mod tests { assert!(buffer.is_empty()); } + #[test] + fn alt_utf8_char_drains_as_one_event_before_following_input() { + let events = parse_raw_input_bytes_sync("\x1béx".as_bytes()); + assert_eq!(events.len(), 2); + assert_raw_key( + events.into_iter().next().unwrap(), + KeyCode::Char('é'), + KeyModifiers::ALT, + ); + } + + #[test] + fn chunked_alt_utf8_waits_for_continuation_byte_after_escape() { + let (tx, mut rx) = mpsc::channel(8); + let mut buffer = Vec::new(); + let bytes = "\x1bé".as_bytes(); + + drain_chunk(&mut buffer, &tx, &bytes[..2]); + assert_eq!(buffer, bytes[..2]); + assert!(collect_events(&mut rx).is_empty()); + flush_incomplete_buffer(&mut buffer, &tx); + assert_eq!(buffer, bytes[..2]); + assert!(collect_events(&mut rx).is_empty()); + + drain_chunk(&mut buffer, &tx, &bytes[2..]); + assert!(buffer.is_empty()); + let events = collect_events(&mut rx); + assert_eq!(events.len(), 1); + assert_raw_key( + events.into_iter().next().unwrap(), + KeyCode::Char('é'), + KeyModifiers::ALT, + ); + } + #[test] fn chunked_utf8_waits_for_continuation_byte() { let (tx, mut rx) = mpsc::channel(8); -- cgit v1.2.3