Skip to main content

fresh/server/
input_parser.rs

1//! Server-side input parsing
2//!
3//! Parses raw bytes from the client into crossterm events.
4//! This allows the server to handle all input parsing, keeping the client ultra-light.
5
6use std::time::Instant;
7
8use crossterm::event::{
9    Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind,
10};
11
12/// How long to wait for more bytes after receiving ESC before treating it as standalone Escape.
13const ESC_TIMEOUT_MS: u128 = 50;
14
15/// Parser state for incremental input parsing
16#[derive(Debug)]
17pub struct InputParser {
18    /// Buffer for incomplete escape sequences
19    buffer: Vec<u8>,
20    /// Maximum buffer size before we give up on an escape sequence
21    max_buffer_size: usize,
22    /// When the buffer last received a byte (for ESC timeout)
23    last_byte_time: Option<Instant>,
24    /// Buffer for bracketed paste content (between \x1b[200~ and \x1b[201~)
25    paste_buffer: Option<Vec<u8>>,
26}
27
28impl Default for InputParser {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl InputParser {
35    pub fn new() -> Self {
36        Self {
37            buffer: Vec::with_capacity(32),
38            max_buffer_size: 256,
39            last_byte_time: None,
40            paste_buffer: None,
41        }
42    }
43
44    /// Parse input bytes and return any complete events
45    pub fn parse(&mut self, bytes: &[u8]) -> Vec<Event> {
46        let mut events = Vec::new();
47
48        for &byte in bytes {
49            // If we're inside a bracketed paste, buffer bytes until end marker
50            if let Some(ref mut paste_buf) = self.paste_buffer {
51                paste_buf.push(byte);
52                // Check for end marker: \x1b[201~
53                if paste_buf.len() >= 6 && paste_buf.ends_with(b"\x1b[201~") {
54                    // Remove the end marker from the paste content
55                    let content_len = paste_buf.len() - 6;
56                    let text = String::from_utf8_lossy(&paste_buf[..content_len]).into_owned();
57                    self.paste_buffer = None;
58                    events.push(Event::Paste(text));
59                }
60                continue;
61            }
62
63            self.buffer.push(byte);
64            self.last_byte_time = Some(Instant::now());
65
66            // Try to parse the buffer
67            match self.try_parse() {
68                ParseResult::Complete(event) => {
69                    events.push(event);
70                    self.buffer.clear();
71                    self.last_byte_time = None;
72                }
73                ParseResult::PasteStart => {
74                    // Enter bracketed paste mode
75                    self.paste_buffer = Some(Vec::new());
76                    self.buffer.clear();
77                    self.last_byte_time = None;
78                }
79                ParseResult::Incomplete => {
80                    // Need more bytes
81                    if self.buffer.len() > self.max_buffer_size {
82                        // Buffer too large, discard and treat as raw bytes
83                        for &b in &self.buffer {
84                            if let Some(event) = self.byte_to_event(b) {
85                                events.push(event);
86                            }
87                        }
88                        self.buffer.clear();
89                        self.last_byte_time = None;
90                    }
91                }
92                ParseResult::Invalid => {
93                    // Invalid sequence, treat first byte as raw and retry rest
94                    if !self.buffer.is_empty() {
95                        let first = self.buffer[0];
96                        if let Some(event) = self.byte_to_event(first) {
97                            events.push(event);
98                        }
99                        let rest: Vec<u8> = self.buffer[1..].to_vec();
100                        self.buffer.clear();
101                        self.last_byte_time = None;
102                        // Re-parse the rest
103                        events.extend(self.parse(&rest));
104                    }
105                }
106            }
107        }
108
109        events
110    }
111
112    /// Flush any pending escape sequence that has timed out.
113    ///
114    /// Call this periodically (e.g., every server tick) to ensure standalone
115    /// ESC keystrokes are emitted promptly instead of waiting indefinitely
116    /// for a follow-up byte that may never arrive.
117    pub fn flush_timeout(&mut self) -> Vec<Event> {
118        if self.buffer.is_empty() {
119            return Vec::new();
120        }
121
122        let timed_out = self
123            .last_byte_time
124            .map(|t| t.elapsed().as_millis() >= ESC_TIMEOUT_MS)
125            .unwrap_or(false);
126
127        if !timed_out {
128            return Vec::new();
129        }
130
131        // Buffer has been sitting too long - flush as individual bytes
132        let mut events = Vec::new();
133        let buf = std::mem::take(&mut self.buffer);
134        for &b in &buf {
135            if let Some(event) = self.byte_to_event(b) {
136                events.push(event);
137            }
138        }
139        self.last_byte_time = None;
140        events
141    }
142
143    /// Try to parse the current buffer
144    fn try_parse(&self) -> ParseResult {
145        if self.buffer.is_empty() {
146            return ParseResult::Incomplete;
147        }
148
149        let bytes = &self.buffer;
150
151        // Check for escape sequences
152        if bytes[0] == 0x1b {
153            return self.parse_escape_sequence();
154        }
155
156        // Single byte - convert directly
157        if let Some(event) = self.byte_to_event(bytes[0]) {
158            return ParseResult::Complete(event);
159        }
160
161        ParseResult::Invalid
162    }
163
164    /// Parse an escape sequence
165    fn parse_escape_sequence(&self) -> ParseResult {
166        let bytes = &self.buffer;
167
168        if bytes.len() < 2 {
169            return ParseResult::Incomplete;
170        }
171
172        match bytes[1] {
173            // CSI sequences: ESC [
174            b'[' => self.parse_csi_sequence(),
175            // SS3 sequences: ESC O (function keys on some terminals)
176            b'O' => self.parse_ss3_sequence(),
177            // ESC followed by another ESC: the first is standalone Escape,
178            // the second starts a new escape sequence. Return Invalid so the
179            // first byte is emitted as Escape and the second \x1b is re-parsed.
180            0x1b => ParseResult::Invalid,
181            // Alt + key: ESC + key
182            _ => {
183                let key = bytes[1];
184                let event = Event::Key(KeyEvent::new(byte_to_keycode(key), KeyModifiers::ALT));
185                ParseResult::Complete(event)
186            }
187        }
188    }
189
190    /// Parse CSI (Control Sequence Introducer) sequence: ESC [ ...
191    fn parse_csi_sequence(&self) -> ParseResult {
192        let bytes = &self.buffer;
193
194        if bytes.len() < 3 {
195            return ParseResult::Incomplete;
196        }
197
198        // Find the final byte (0x40-0x7E)
199        let final_idx = bytes[2..].iter().position(|&b| (0x40..=0x7E).contains(&b));
200
201        match final_idx {
202            None => {
203                // Check if we have parameter bytes (0x30-0x3F) or intermediate bytes (0x20-0x2F)
204                let all_valid = bytes[2..].iter().all(|&b| (0x20..=0x3F).contains(&b));
205                if all_valid {
206                    ParseResult::Incomplete
207                } else {
208                    ParseResult::Invalid
209                }
210            }
211            Some(idx) => {
212                let final_byte = bytes[2 + idx];
213                let params = &bytes[2..2 + idx];
214
215                self.parse_csi_final(params, final_byte)
216            }
217        }
218    }
219
220    /// Parse CSI sequence with final byte
221    fn parse_csi_final(&self, params: &[u8], final_byte: u8) -> ParseResult {
222        match final_byte {
223            // Cursor keys
224            b'A' => ParseResult::Complete(Event::Key(KeyEvent::new(
225                KeyCode::Up,
226                self.parse_modifiers(params),
227            ))),
228            b'B' => ParseResult::Complete(Event::Key(KeyEvent::new(
229                KeyCode::Down,
230                self.parse_modifiers(params),
231            ))),
232            b'C' => ParseResult::Complete(Event::Key(KeyEvent::new(
233                KeyCode::Right,
234                self.parse_modifiers(params),
235            ))),
236            b'D' => ParseResult::Complete(Event::Key(KeyEvent::new(
237                KeyCode::Left,
238                self.parse_modifiers(params),
239            ))),
240            b'H' => ParseResult::Complete(Event::Key(KeyEvent::new(
241                KeyCode::Home,
242                self.parse_modifiers(params),
243            ))),
244            b'F' => ParseResult::Complete(Event::Key(KeyEvent::new(
245                KeyCode::End,
246                self.parse_modifiers(params),
247            ))),
248
249            // Special keys with tilde
250            b'~' => self.parse_tilde_sequence(params),
251
252            // Mouse events (SGR format): CSI < Cb ; Cx ; Cy M/m
253            b'M' | b'm' => {
254                if !params.is_empty() && params[0] == b'<' {
255                    self.parse_sgr_mouse(params, final_byte == b'M')
256                } else {
257                    // X10 mouse format
258                    self.parse_x10_mouse()
259                }
260            }
261
262            // Shift+Tab (Back Tab): CSI Z
263            b'Z' => ParseResult::Complete(Event::Key(KeyEvent::new(
264                KeyCode::BackTab,
265                KeyModifiers::SHIFT,
266            ))),
267
268            // Focus events
269            b'I' => ParseResult::Complete(Event::FocusGained),
270            b'O' => ParseResult::Complete(Event::FocusLost),
271
272            _ => ParseResult::Invalid,
273        }
274    }
275
276    /// Parse tilde sequences: CSI number ~
277    fn parse_tilde_sequence(&self, params: &[u8]) -> ParseResult {
278        let (num, modifiers) = self.parse_num_and_modifiers(params);
279
280        // Bracketed paste start: CSI 200 ~
281        if num == 200 {
282            return ParseResult::PasteStart;
283        }
284
285        // Bracketed paste end: CSI 201 ~ (shouldn't appear outside paste mode,
286        // but handle gracefully by ignoring)
287        if num == 201 {
288            return ParseResult::Complete(Event::Key(KeyEvent::new(
289                KeyCode::Null,
290                KeyModifiers::empty(),
291            )));
292        }
293
294        let keycode = match num {
295            1 => KeyCode::Home,
296            2 => KeyCode::Insert,
297            3 => KeyCode::Delete,
298            4 => KeyCode::End,
299            5 => KeyCode::PageUp,
300            6 => KeyCode::PageDown,
301            7 => KeyCode::Home,
302            8 => KeyCode::End,
303            11 => KeyCode::F(1),
304            12 => KeyCode::F(2),
305            13 => KeyCode::F(3),
306            14 => KeyCode::F(4),
307            15 => KeyCode::F(5),
308            17 => KeyCode::F(6),
309            18 => KeyCode::F(7),
310            19 => KeyCode::F(8),
311            20 => KeyCode::F(9),
312            21 => KeyCode::F(10),
313            23 => KeyCode::F(11),
314            24 => KeyCode::F(12),
315            _ => return ParseResult::Invalid,
316        };
317
318        ParseResult::Complete(Event::Key(KeyEvent::new(keycode, modifiers)))
319    }
320
321    /// Parse SGR mouse format: CSI < Cb ; Cx ; Cy M/m
322    fn parse_sgr_mouse(&self, params: &[u8], pressed: bool) -> ParseResult {
323        // Skip the '<'
324        let params_str = std::str::from_utf8(&params[1..]).unwrap_or("");
325        let parts: Vec<&str> = params_str.split(';').collect();
326
327        if parts.len() != 3 {
328            return ParseResult::Invalid;
329        }
330
331        let cb: u16 = parts[0].parse().unwrap_or(0);
332        let cx: u16 = parts[1].parse().unwrap_or(1);
333        let cy: u16 = parts[2].parse().unwrap_or(1);
334
335        let button_bits = cb & 0b11;
336        let button = match button_bits {
337            0 => MouseButton::Left,
338            1 => MouseButton::Middle,
339            2 => MouseButton::Right,
340            _ => MouseButton::Left, // 3 = no button (for motion)
341        };
342
343        let modifiers = KeyModifiers::from_bits_truncate(
344            if cb & 4 != 0 {
345                KeyModifiers::SHIFT.bits()
346            } else {
347                0
348            } | if cb & 8 != 0 {
349                KeyModifiers::ALT.bits()
350            } else {
351                0
352            } | if cb & 16 != 0 {
353                KeyModifiers::CONTROL.bits()
354            } else {
355                0
356            },
357        );
358
359        let kind = if cb & 32 != 0 {
360            // Motion event
361            if cb & 64 != 0 {
362                // Scroll while moving (unusual)
363                if cb & 1 != 0 {
364                    MouseEventKind::ScrollDown
365                } else {
366                    MouseEventKind::ScrollUp
367                }
368            } else if button_bits == 3 {
369                // Motion with no button pressed (hover)
370                MouseEventKind::Moved
371            } else {
372                // Motion with button pressed (drag)
373                MouseEventKind::Drag(button)
374            }
375        } else if cb & 64 != 0 {
376            // Scroll
377            if cb & 1 != 0 {
378                MouseEventKind::ScrollDown
379            } else {
380                MouseEventKind::ScrollUp
381            }
382        } else if pressed {
383            MouseEventKind::Down(button)
384        } else {
385            MouseEventKind::Up(button)
386        };
387
388        ParseResult::Complete(Event::Mouse(MouseEvent {
389            kind,
390            column: cx.saturating_sub(1),
391            row: cy.saturating_sub(1),
392            modifiers,
393        }))
394    }
395
396    /// Parse X10 mouse format (legacy)
397    fn parse_x10_mouse(&self) -> ParseResult {
398        let bytes = &self.buffer;
399
400        if bytes.len() < 6 {
401            return ParseResult::Incomplete;
402        }
403
404        let cb = bytes[3].wrapping_sub(32);
405        let cx = bytes[4].wrapping_sub(32);
406        let cy = bytes[5].wrapping_sub(32);
407
408        let button = match cb & 0b11 {
409            0 => MouseButton::Left,
410            1 => MouseButton::Middle,
411            2 => MouseButton::Right,
412            3 => {
413                // Release
414                return ParseResult::Complete(Event::Mouse(MouseEvent {
415                    kind: MouseEventKind::Up(MouseButton::Left),
416                    column: cx as u16,
417                    row: cy as u16,
418                    modifiers: KeyModifiers::empty(),
419                }));
420            }
421            _ => MouseButton::Left,
422        };
423
424        ParseResult::Complete(Event::Mouse(MouseEvent {
425            kind: MouseEventKind::Down(button),
426            column: cx as u16,
427            row: cy as u16,
428            modifiers: KeyModifiers::empty(),
429        }))
430    }
431
432    /// Parse SS3 sequence: ESC O ...
433    fn parse_ss3_sequence(&self) -> ParseResult {
434        let bytes = &self.buffer;
435
436        if bytes.len() < 3 {
437            return ParseResult::Incomplete;
438        }
439
440        let keycode = match bytes[2] {
441            b'P' => KeyCode::F(1),
442            b'Q' => KeyCode::F(2),
443            b'R' => KeyCode::F(3),
444            b'S' => KeyCode::F(4),
445            b'A' => KeyCode::Up,
446            b'B' => KeyCode::Down,
447            b'C' => KeyCode::Right,
448            b'D' => KeyCode::Left,
449            b'H' => KeyCode::Home,
450            b'F' => KeyCode::End,
451            _ => return ParseResult::Invalid,
452        };
453
454        ParseResult::Complete(Event::Key(KeyEvent::new(keycode, KeyModifiers::empty())))
455    }
456
457    /// Parse modifiers from CSI parameters
458    fn parse_modifiers(&self, params: &[u8]) -> KeyModifiers {
459        // Format: [num;modifiers] where modifiers = 1 + (shift) + 2*(alt) + 4*(ctrl)
460        let params_str = std::str::from_utf8(params).unwrap_or("");
461        if let Some(idx) = params_str.find(';') {
462            if let Ok(mods) = params_str[idx + 1..].parse::<u8>() {
463                return modifiers_from_param(mods);
464            }
465        }
466        KeyModifiers::empty()
467    }
468
469    /// Parse number and modifiers from CSI parameters
470    fn parse_num_and_modifiers(&self, params: &[u8]) -> (u8, KeyModifiers) {
471        let params_str = std::str::from_utf8(params).unwrap_or("");
472        let parts: Vec<&str> = params_str.split(';').collect();
473
474        let num = parts.first().and_then(|s| s.parse().ok()).unwrap_or(0);
475        let mods = parts.get(1).and_then(|s| s.parse().ok()).unwrap_or(1);
476
477        (num, modifiers_from_param(mods))
478    }
479
480    /// Convert a single byte to an event
481    fn byte_to_event(&self, byte: u8) -> Option<Event> {
482        let keycode = byte_to_keycode(byte);
483        let modifiers = if byte < 32 && byte != 9 && byte != 10 && byte != 13 && byte != 27 {
484            // Control character (but not Tab, LF, CR, or Esc)
485            KeyModifiers::CONTROL
486        } else {
487            KeyModifiers::empty()
488        };
489
490        Some(Event::Key(KeyEvent::new(keycode, modifiers)))
491    }
492}
493
494/// Result of trying to parse the buffer
495enum ParseResult {
496    /// Successfully parsed a complete event
497    Complete(Event),
498    /// Bracketed paste start marker detected (\x1b[200~)
499    PasteStart,
500    /// Need more bytes to complete the sequence
501    Incomplete,
502    /// Invalid sequence
503    Invalid,
504}
505
506/// Convert a byte to a KeyCode
507fn byte_to_keycode(byte: u8) -> KeyCode {
508    match byte {
509        0 => KeyCode::Char('@'), // Ctrl+@
510        9 => KeyCode::Tab,
511        10 | 13 => KeyCode::Enter,                          // LF or CR
512        1..=26 => KeyCode::Char((b'a' + byte - 1) as char), // Ctrl+A through Ctrl+Z
513        27 => KeyCode::Esc,
514        28..=31 => KeyCode::Char((b'\\' + byte - 28) as char),
515        32 => KeyCode::Char(' '),
516        127 => KeyCode::Backspace,
517        b if (32..127).contains(&b) => KeyCode::Char(b as char),
518        _ => KeyCode::Null,
519    }
520}
521
522/// Convert modifier parameter to KeyModifiers
523fn modifiers_from_param(param: u8) -> KeyModifiers {
524    let param = param.saturating_sub(1);
525    KeyModifiers::from_bits_truncate(
526        if param & 1 != 0 {
527            KeyModifiers::SHIFT.bits()
528        } else {
529            0
530        } | if param & 2 != 0 {
531            KeyModifiers::ALT.bits()
532        } else {
533            0
534        } | if param & 4 != 0 {
535            KeyModifiers::CONTROL.bits()
536        } else {
537            0
538        },
539    )
540}
541
542#[cfg(test)]
543mod tests {
544    use super::*;
545
546    #[test]
547    fn test_simple_characters() {
548        let mut parser = InputParser::new();
549        let events = parser.parse(b"abc");
550        assert_eq!(events.len(), 3);
551        match &events[0] {
552            Event::Key(ke) => assert_eq!(ke.code, KeyCode::Char('a')),
553            _ => panic!("Expected key event"),
554        }
555    }
556
557    #[test]
558    fn test_control_characters_have_ctrl_modifier() {
559        let mut parser = InputParser::new();
560        // Ctrl+C = 0x03
561        let events = parser.parse(&[0x03]);
562        match &events[0] {
563            Event::Key(ke) => {
564                assert_eq!(ke.code, KeyCode::Char('c'));
565                assert!(ke.modifiers.contains(KeyModifiers::CONTROL));
566            }
567            _ => panic!("Expected key event"),
568        }
569    }
570
571    #[test]
572    fn test_escape_buffers_until_complete() {
573        let mut parser = InputParser::new();
574        // ESC alone should buffer
575        assert!(parser.parse(&[0x1b]).is_empty());
576        // Adding more should still buffer
577        assert!(parser.parse(b"[").is_empty());
578        // Final byte completes the sequence
579        let events = parser.parse(b"A");
580        assert_eq!(events.len(), 1);
581        match &events[0] {
582            Event::Key(ke) => assert_eq!(ke.code, KeyCode::Up),
583            _ => panic!("Expected Up key"),
584        }
585    }
586
587    #[test]
588    fn test_csi_sequences_parse_arrow_keys() {
589        let mut parser = InputParser::new();
590        // CSI format: ESC [ <final>
591        let events = parser.parse(b"\x1b[A");
592        match &events[0] {
593            Event::Key(ke) => assert_eq!(ke.code, KeyCode::Up),
594            _ => panic!("Expected key event"),
595        }
596    }
597
598    #[test]
599    fn test_ss3_sequences_parse_function_keys() {
600        let mut parser = InputParser::new();
601        // SS3 format: ESC O <letter>
602        let events = parser.parse(b"\x1bOP");
603        match &events[0] {
604            Event::Key(ke) => assert_eq!(ke.code, KeyCode::F(1)),
605            _ => panic!("Expected key event"),
606        }
607    }
608
609    #[test]
610    fn test_alt_key_via_esc_prefix() {
611        let mut parser = InputParser::new();
612        // Alt+a: ESC a (ESC followed by non-sequence char)
613        let events = parser.parse(b"\x1ba");
614        match &events[0] {
615            Event::Key(ke) => {
616                assert_eq!(ke.code, KeyCode::Char('a'));
617                assert!(ke.modifiers.contains(KeyModifiers::ALT));
618            }
619            _ => panic!("Expected key event"),
620        }
621    }
622
623    #[test]
624    fn test_csi_modifiers_parsed_correctly() {
625        let mut parser = InputParser::new();
626        // Shift+Up: ESC [ 1 ; 2 A (2 = shift)
627        let events = parser.parse(b"\x1b[1;2A");
628        match &events[0] {
629            Event::Key(ke) => {
630                assert_eq!(ke.code, KeyCode::Up);
631                assert!(ke.modifiers.contains(KeyModifiers::SHIFT));
632            }
633            _ => panic!("Expected Shift+Up"),
634        }
635    }
636
637    #[test]
638    fn test_sgr_mouse_events_are_1_indexed() {
639        let mut parser = InputParser::new();
640        // SGR mouse: CSI < button ; x ; y M
641        // Terminal sends 1-indexed, we convert to 0-indexed
642        let events = parser.parse(b"\x1b[<0;10;5M");
643        match &events[0] {
644            Event::Mouse(me) => {
645                assert_eq!(me.column, 9); // 10-1
646                assert_eq!(me.row, 4); // 5-1
647            }
648            _ => panic!("Expected mouse event"),
649        }
650    }
651
652    #[test]
653    fn test_focus_events() {
654        let mut parser = InputParser::new();
655        let events = parser.parse(b"\x1b[I");
656        assert!(matches!(&events[0], Event::FocusGained));
657
658        let events = parser.parse(b"\x1b[O");
659        assert!(matches!(&events[0], Event::FocusLost));
660    }
661
662    #[test]
663    fn test_mixed_input_preserves_order() {
664        let mut parser = InputParser::new();
665        let events = parser.parse(b"a\x1b[Ab");
666        assert_eq!(events.len(), 3);
667        // Order: 'a', Up, 'b'
668        assert!(matches!(&events[0], Event::Key(ke) if ke.code == KeyCode::Char('a')));
669        assert!(matches!(&events[1], Event::Key(ke) if ke.code == KeyCode::Up));
670        assert!(matches!(&events[2], Event::Key(ke) if ke.code == KeyCode::Char('b')));
671    }
672
673    #[test]
674    fn test_enter_key() {
675        let mut parser = InputParser::new();
676        // CR (carriage return) = 0x0D = 13
677        let events = parser.parse(&[0x0D]);
678        match &events[0] {
679            Event::Key(ke) => {
680                assert_eq!(ke.code, KeyCode::Enter);
681                assert!(ke.modifiers.is_empty());
682            }
683            _ => panic!("Expected Enter key event"),
684        }
685
686        // LF (line feed) = 0x0A = 10
687        let events = parser.parse(&[0x0A]);
688        match &events[0] {
689            Event::Key(ke) => {
690                assert_eq!(ke.code, KeyCode::Enter);
691                assert!(ke.modifiers.is_empty());
692            }
693            _ => panic!("Expected Enter key event"),
694        }
695    }
696
697    #[test]
698    fn test_tab_key() {
699        let mut parser = InputParser::new();
700        // Tab = 0x09 = 9
701        let events = parser.parse(&[0x09]);
702        match &events[0] {
703            Event::Key(ke) => {
704                assert_eq!(ke.code, KeyCode::Tab);
705                assert!(ke.modifiers.is_empty());
706            }
707            _ => panic!("Expected Tab key event"),
708        }
709    }
710
711    #[test]
712    fn test_mouse_motion_without_button() {
713        let mut parser = InputParser::new();
714        // SGR mouse motion with no button: CSI < 35 ; x ; y M
715        // 35 = 32 (motion) + 3 (no button)
716        let events = parser.parse(b"\x1b[<35;10;5M");
717        match &events[0] {
718            Event::Mouse(me) => {
719                assert!(matches!(me.kind, MouseEventKind::Moved));
720                assert_eq!(me.column, 9); // 10 - 1 (0-indexed)
721                assert_eq!(me.row, 4); // 5 - 1 (0-indexed)
722            }
723            _ => panic!("Expected mouse motion event"),
724        }
725    }
726
727    // ---- Regression tests for issue #1089 ----
728
729    #[test]
730    fn test_shift_tab_csi_z() {
731        let mut parser = InputParser::new();
732        // Shift+Tab sends CSI Z = ESC [ Z
733        let events = parser.parse(b"\x1b[Z");
734        assert_eq!(events.len(), 1);
735        match &events[0] {
736            Event::Key(ke) => {
737                assert_eq!(ke.code, KeyCode::BackTab);
738                assert!(ke.modifiers.contains(KeyModifiers::SHIFT));
739            }
740            _ => panic!("Expected BackTab key event, got {:?}", events[0]),
741        }
742    }
743
744    #[test]
745    fn test_esc_then_mouse_event_same_chunk() {
746        let mut parser = InputParser::new();
747        // User presses Escape, then moves mouse. Both arrive in one chunk:
748        // ESC (0x1b) followed by mouse event ESC [ < 35 ; 67 ; 18 M
749        let events = parser.parse(b"\x1b\x1b[<35;67;18M");
750        assert_eq!(
751            events.len(),
752            2,
753            "Expected Escape + mouse event, got: {:?}",
754            events
755        );
756
757        // First event: standalone Escape
758        match &events[0] {
759            Event::Key(ke) => {
760                assert_eq!(ke.code, KeyCode::Esc);
761                assert!(ke.modifiers.is_empty());
762            }
763            _ => panic!("Expected Esc key event, got {:?}", events[0]),
764        }
765
766        // Second event: mouse motion
767        match &events[1] {
768            Event::Mouse(me) => {
769                assert!(matches!(me.kind, MouseEventKind::Moved));
770                assert_eq!(me.column, 66); // 67 - 1
771                assert_eq!(me.row, 17); // 18 - 1
772            }
773            _ => panic!("Expected mouse motion event, got {:?}", events[1]),
774        }
775    }
776
777    #[test]
778    fn test_esc_then_mouse_event_separate_chunks() {
779        let mut parser = InputParser::new();
780
781        // First chunk: standalone ESC (buffered, waiting for more bytes)
782        let events = parser.parse(&[0x1b]);
783        assert!(events.is_empty(), "ESC should be buffered");
784
785        // Second chunk: mouse event arrives later
786        let events = parser.parse(b"\x1b[<35;67;18M");
787        assert_eq!(
788            events.len(),
789            2,
790            "Expected Escape + mouse event, got: {:?}",
791            events
792        );
793
794        // First event: standalone Escape (disambiguated by seeing another ESC)
795        match &events[0] {
796            Event::Key(ke) => {
797                assert_eq!(ke.code, KeyCode::Esc);
798                assert!(ke.modifiers.is_empty());
799            }
800            _ => panic!("Expected Esc key event, got {:?}", events[0]),
801        }
802
803        // Second event: mouse motion
804        match &events[1] {
805            Event::Mouse(me) => {
806                assert!(matches!(me.kind, MouseEventKind::Moved));
807            }
808            _ => panic!("Expected mouse motion event, got {:?}", events[1]),
809        }
810    }
811
812    #[test]
813    fn test_esc_then_csi_arrow_separate_chunks() {
814        let mut parser = InputParser::new();
815
816        // ESC buffered
817        let events = parser.parse(&[0x1b]);
818        assert!(events.is_empty());
819
820        // Arrow key sequence arrives (starts with another ESC)
821        let events = parser.parse(b"\x1b[A");
822        assert_eq!(events.len(), 2, "Expected Escape + Up, got: {:?}", events);
823
824        match &events[0] {
825            Event::Key(ke) => assert_eq!(ke.code, KeyCode::Esc),
826            _ => panic!("Expected Esc"),
827        }
828        match &events[1] {
829            Event::Key(ke) => assert_eq!(ke.code, KeyCode::Up),
830            _ => panic!("Expected Up"),
831        }
832    }
833
834    #[test]
835    fn test_standalone_esc_flush_timeout() {
836        let mut parser = InputParser::new();
837
838        // ESC buffered
839        let events = parser.parse(&[0x1b]);
840        assert!(events.is_empty());
841
842        // Simulate time passing (replace last_byte_time with a past timestamp)
843        parser.last_byte_time =
844            Some(Instant::now() - std::time::Duration::from_millis(ESC_TIMEOUT_MS as u64 + 10));
845
846        // Flush should emit the standalone Escape
847        let events = parser.flush_timeout();
848        assert_eq!(events.len(), 1);
849        match &events[0] {
850            Event::Key(ke) => {
851                assert_eq!(ke.code, KeyCode::Esc);
852                assert!(ke.modifiers.is_empty());
853            }
854            _ => panic!("Expected Esc key event"),
855        }
856
857        // Buffer should be empty now
858        assert!(parser.buffer.is_empty());
859    }
860
861    #[test]
862    fn test_flush_timeout_does_nothing_when_recent() {
863        let mut parser = InputParser::new();
864
865        // ESC buffered just now
866        let events = parser.parse(&[0x1b]);
867        assert!(events.is_empty());
868
869        // Flush should NOT emit anything (too recent)
870        let events = parser.flush_timeout();
871        assert!(events.is_empty());
872
873        // Buffer still has the ESC
874        assert_eq!(parser.buffer.len(), 1);
875    }
876
877    #[test]
878    fn test_esc_then_mouse_click() {
879        let mut parser = InputParser::new();
880        // ESC followed by mouse button press: ESC [ < 0 ; 10 ; 5 M
881        let events = parser.parse(b"\x1b\x1b[<0;10;5M");
882        assert_eq!(
883            events.len(),
884            2,
885            "Expected Escape + mouse click, got: {:?}",
886            events
887        );
888
889        match &events[0] {
890            Event::Key(ke) => assert_eq!(ke.code, KeyCode::Esc),
891            _ => panic!("Expected Esc"),
892        }
893        match &events[1] {
894            Event::Mouse(me) => {
895                assert!(matches!(me.kind, MouseEventKind::Down(MouseButton::Left)));
896            }
897            _ => panic!("Expected mouse down event, got {:?}", events[1]),
898        }
899    }
900
901    // ---- Bracketed paste tests ----
902
903    #[test]
904    fn test_bracketed_paste_simple() {
905        let mut parser = InputParser::new();
906        // Bracketed paste: \x1b[200~ ... \x1b[201~
907        let events = parser.parse(b"\x1b[200~Hello, world!\x1b[201~");
908        assert_eq!(events.len(), 1, "Expected 1 paste event, got: {:?}", events);
909        match &events[0] {
910            Event::Paste(text) => assert_eq!(text, "Hello, world!"),
911            _ => panic!("Expected Paste event, got {:?}", events[0]),
912        }
913    }
914
915    #[test]
916    fn test_bracketed_paste_with_newlines() {
917        let mut parser = InputParser::new();
918        let events = parser.parse(b"\x1b[200~line1\nline2\nline3\x1b[201~");
919        assert_eq!(events.len(), 1);
920        match &events[0] {
921            Event::Paste(text) => assert_eq!(text, "line1\nline2\nline3"),
922            _ => panic!("Expected Paste event"),
923        }
924    }
925
926    #[test]
927    fn test_bracketed_paste_split_across_chunks() {
928        let mut parser = InputParser::new();
929
930        // Start marker arrives
931        let events = parser.parse(b"\x1b[200~Hello");
932        assert!(events.is_empty(), "Paste not complete yet");
933
934        // More content
935        let events = parser.parse(b", world!");
936        assert!(events.is_empty(), "Paste not complete yet");
937
938        // End marker arrives
939        let events = parser.parse(b"\x1b[201~");
940        assert_eq!(events.len(), 1);
941        match &events[0] {
942            Event::Paste(text) => assert_eq!(text, "Hello, world!"),
943            _ => panic!("Expected Paste event"),
944        }
945    }
946
947    #[test]
948    fn test_bracketed_paste_followed_by_keypress() {
949        let mut parser = InputParser::new();
950        // Paste followed by a regular keypress
951        let events = parser.parse(b"\x1b[200~pasted\x1b[201~a");
952        assert_eq!(
953            events.len(),
954            2,
955            "Expected paste + key event, got: {:?}",
956            events
957        );
958        match &events[0] {
959            Event::Paste(text) => assert_eq!(text, "pasted"),
960            _ => panic!("Expected Paste event"),
961        }
962        match &events[1] {
963            Event::Key(ke) => assert_eq!(ke.code, KeyCode::Char('a')),
964            _ => panic!("Expected key event"),
965        }
966    }
967
968    #[test]
969    fn test_bracketed_paste_empty() {
970        let mut parser = InputParser::new();
971        let events = parser.parse(b"\x1b[200~\x1b[201~");
972        assert_eq!(events.len(), 1);
973        match &events[0] {
974            Event::Paste(text) => assert_eq!(text, ""),
975            _ => panic!("Expected empty Paste event"),
976        }
977    }
978
979    #[test]
980    fn test_bracketed_paste_with_escape_sequences_inside() {
981        let mut parser = InputParser::new();
982        // Pasted text might contain escape sequences (e.g., colored text from another terminal)
983        let events = parser.parse(b"\x1b[200~\x1b[31mred text\x1b[0m\x1b[201~");
984        assert_eq!(events.len(), 1);
985        match &events[0] {
986            Event::Paste(text) => assert_eq!(text, "\x1b[31mred text\x1b[0m"),
987            _ => panic!("Expected Paste event with escape sequences"),
988        }
989    }
990
991    #[test]
992    fn test_keypress_then_bracketed_paste() {
993        let mut parser = InputParser::new();
994        let events = parser.parse(b"x\x1b[200~pasted\x1b[201~");
995        assert_eq!(events.len(), 2);
996        match &events[0] {
997            Event::Key(ke) => assert_eq!(ke.code, KeyCode::Char('x')),
998            _ => panic!("Expected key event"),
999        }
1000        match &events[1] {
1001            Event::Paste(text) => assert_eq!(text, "pasted"),
1002            _ => panic!("Expected Paste event"),
1003        }
1004    }
1005}