From 5d24d0d214d05858e344a9e15a63856dc1328eae Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Wed, 15 Jul 2026 00:59:38 +0300 Subject: fix: preserve escape before sgr mouse reports refs #1334 --- src/raw_input.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'src/raw_input.rs') diff --git a/src/raw_input.rs b/src/raw_input.rs index 445b4be3..848df88d 100644 --- a/src/raw_input.rs +++ b/src/raw_input.rs @@ -766,6 +766,15 @@ fn complete_escape_sequence_len(buffer: &[u8]) -> Option { return None; } + if buffer.starts_with(b"\x1b\x1b[<") { + if let Some(mouse_len) = find_csi_final(&buffer[1..], b"Mm") { + let mouse_sequence = std::str::from_utf8(&buffer[1..1 + mouse_len]).ok()?; + if parse_sgr_mouse(mouse_sequence).is_some() { + return Some(1); + } + } + } + if buffer.starts_with(b"\x1b\x1b") { return complete_escape_sequence_len(&buffer[1..]).map(|len| len + 1); } @@ -1572,6 +1581,45 @@ mod tests { )); } + #[test] + fn lone_escape_then_complete_sgr_mouse_report_emits_both_events() { + for report in [b"\x1b[<35;10;20M".as_slice(), b"\x1b[<35;10;20m".as_slice()] { + let mut framer = RawInputFramer::default(); + + assert!(framer.push(b"\x1b").is_empty()); + let events = framer.push(report); + + assert_eq!(events.len(), 2); + let mut events = events.into_iter(); + assert_raw_key(events.next().unwrap(), KeyCode::Esc, KeyModifiers::empty()); + assert!(matches!( + events.next().unwrap(), + RawInputEvent::Mouse(MouseEvent { + kind: MouseEventKind::Moved, + column: 9, + row: 19, + .. + }) + )); + assert!(framer.flush_timeout().is_empty()); + } + } + + #[test] + fn legacy_doubled_escape_alt_arrow_remains_one_event() { + let mut framer = RawInputFramer::default(); + + let events = framer.push(b"\x1b\x1b[A"); + + assert_eq!(events.len(), 1); + assert_raw_key( + events.into_iter().next().unwrap(), + KeyCode::Up, + KeyModifiers::ALT, + ); + assert!(framer.flush_timeout().is_empty()); + } + #[test] fn legacy_reader_extends_only_incomplete_sgr_mouse_timeout() { let mut mouse = RawInputFramer::default(); -- cgit v1.2.3