Skip to main content

bubbletea/
key.rs

1//! Keyboard input handling.
2//!
3//! This module provides types for representing keyboard events, including
4//! special keys, control combinations, and regular character input.
5
6use std::fmt;
7
8/// Keyboard key event message.
9///
10/// KeyMsg is sent to the program's update function when a key is pressed.
11///
12/// # Example
13///
14/// ```rust
15/// use bubbletea::{KeyMsg, KeyType};
16///
17/// fn handle_key(key: KeyMsg) {
18///     match key.key_type {
19///         KeyType::Enter => println!("Enter pressed"),
20///         KeyType::Runes => println!("Typed: {:?}", key.runes),
21///         _ => {}
22///     }
23/// }
24/// ```
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct KeyMsg {
27    /// The type of key pressed.
28    pub key_type: KeyType,
29    /// For KeyType::Runes, the characters typed.
30    pub runes: Vec<char>,
31    /// Whether Alt was held.
32    pub alt: bool,
33    /// Whether this came from a paste operation.
34    pub paste: bool,
35}
36
37impl KeyMsg {
38    /// Create a new key message from a key type.
39    pub fn from_type(key_type: KeyType) -> Self {
40        Self {
41            key_type,
42            runes: Vec::new(),
43            alt: false,
44            paste: false,
45        }
46    }
47
48    /// Create a new key message from a character.
49    pub fn from_char(c: char) -> Self {
50        Self {
51            key_type: KeyType::Runes,
52            runes: vec![c],
53            alt: false,
54            paste: false,
55        }
56    }
57
58    /// Create a new key message from multiple characters (e.g., from IME).
59    pub fn from_runes(runes: Vec<char>) -> Self {
60        Self {
61            key_type: KeyType::Runes,
62            runes,
63            alt: false,
64            paste: false,
65        }
66    }
67
68    /// Set the alt modifier.
69    pub fn with_alt(mut self) -> Self {
70        self.alt = true;
71        self
72    }
73
74    /// Set the paste flag.
75    pub fn with_paste(mut self) -> Self {
76        self.paste = true;
77        self
78    }
79}
80
81impl fmt::Display for KeyMsg {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        if self.alt {
84            write!(f, "alt+")?;
85        }
86        if self.key_type == KeyType::Runes {
87            if self.paste {
88                write!(f, "[")?;
89            }
90            for c in &self.runes {
91                write!(f, "{}", c)?;
92            }
93            if self.paste {
94                write!(f, "]")?;
95            }
96        } else {
97            write!(f, "{}", self.key_type)?;
98        }
99        Ok(())
100    }
101}
102
103/// Key type enumeration.
104///
105/// Represents different types of keys that can be pressed.
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
107#[repr(i16)]
108pub enum KeyType {
109    // Control keys (ASCII values)
110    /// Null character (Ctrl+@).
111    Null = 0,
112    /// Ctrl+A.
113    CtrlA = 1,
114    /// Ctrl+B.
115    CtrlB = 2,
116    /// Break/Interrupt (Ctrl+C).
117    CtrlC = 3,
118    /// Ctrl+D (EOF).
119    CtrlD = 4,
120    /// Ctrl+E.
121    CtrlE = 5,
122    /// Ctrl+F.
123    CtrlF = 6,
124    /// Ctrl+G (Bell).
125    CtrlG = 7,
126    /// Ctrl+H (Backspace on some systems).
127    CtrlH = 8,
128    /// Tab (Ctrl+I).
129    Tab = 9,
130    /// Ctrl+J (Line feed).
131    CtrlJ = 10,
132    /// Ctrl+K.
133    CtrlK = 11,
134    /// Ctrl+L.
135    CtrlL = 12,
136    /// Enter (Ctrl+M, Carriage return).
137    Enter = 13,
138    /// Ctrl+N.
139    CtrlN = 14,
140    /// Ctrl+O.
141    CtrlO = 15,
142    /// Ctrl+P.
143    CtrlP = 16,
144    /// Ctrl+Q.
145    CtrlQ = 17,
146    /// Ctrl+R.
147    CtrlR = 18,
148    /// Ctrl+S.
149    CtrlS = 19,
150    /// Ctrl+T.
151    CtrlT = 20,
152    /// Ctrl+U.
153    CtrlU = 21,
154    /// Ctrl+V.
155    CtrlV = 22,
156    /// Ctrl+W.
157    CtrlW = 23,
158    /// Ctrl+X.
159    CtrlX = 24,
160    /// Ctrl+Y.
161    CtrlY = 25,
162    /// Ctrl+Z.
163    CtrlZ = 26,
164    /// Escape (Ctrl+[).
165    Esc = 27,
166    /// Ctrl+\.
167    CtrlBackslash = 28,
168    /// Ctrl+].
169    CtrlCloseBracket = 29,
170    /// Ctrl+^.
171    CtrlCaret = 30,
172    /// Ctrl+_.
173    CtrlUnderscore = 31,
174    /// Delete (127).
175    Backspace = 127,
176
177    // Special keys (negative values to avoid collision)
178    /// Regular character(s) input.
179    Runes = -1,
180    /// Up arrow.
181    Up = -2,
182    /// Down arrow.
183    Down = -3,
184    /// Right arrow.
185    Right = -4,
186    /// Left arrow.
187    Left = -5,
188    /// Shift+Tab.
189    ShiftTab = -6,
190    /// Home key.
191    Home = -7,
192    /// End key.
193    End = -8,
194    /// Page Up.
195    PgUp = -9,
196    /// Page Down.
197    PgDown = -10,
198    /// Ctrl+Page Up.
199    CtrlPgUp = -11,
200    /// Ctrl+Page Down.
201    CtrlPgDown = -12,
202    /// Delete key.
203    Delete = -13,
204    /// Insert key.
205    Insert = -14,
206    /// Space key.
207    Space = -15,
208    /// Ctrl+Up.
209    CtrlUp = -16,
210    /// Ctrl+Down.
211    CtrlDown = -17,
212    /// Ctrl+Right.
213    CtrlRight = -18,
214    /// Ctrl+Left.
215    CtrlLeft = -19,
216    /// Ctrl+Home.
217    CtrlHome = -20,
218    /// Ctrl+End.
219    CtrlEnd = -21,
220    /// Shift+Up.
221    ShiftUp = -22,
222    /// Shift+Down.
223    ShiftDown = -23,
224    /// Shift+Right.
225    ShiftRight = -24,
226    /// Shift+Left.
227    ShiftLeft = -25,
228    /// Shift+Home.
229    ShiftHome = -26,
230    /// Shift+End.
231    ShiftEnd = -27,
232    /// Ctrl+Shift+Up.
233    CtrlShiftUp = -28,
234    /// Ctrl+Shift+Down.
235    CtrlShiftDown = -29,
236    /// Ctrl+Shift+Left.
237    CtrlShiftLeft = -30,
238    /// Ctrl+Shift+Right.
239    CtrlShiftRight = -31,
240    /// Ctrl+Shift+Home.
241    CtrlShiftHome = -32,
242    /// Ctrl+Shift+End.
243    CtrlShiftEnd = -33,
244    /// F1.
245    F1 = -34,
246    /// F2.
247    F2 = -35,
248    /// F3.
249    F3 = -36,
250    /// F4.
251    F4 = -37,
252    /// F5.
253    F5 = -38,
254    /// F6.
255    F6 = -39,
256    /// F7.
257    F7 = -40,
258    /// F8.
259    F8 = -41,
260    /// F9.
261    F9 = -42,
262    /// F10.
263    F10 = -43,
264    /// F11.
265    F11 = -44,
266    /// F12.
267    F12 = -45,
268    /// F13.
269    F13 = -46,
270    /// F14.
271    F14 = -47,
272    /// F15.
273    F15 = -48,
274    /// F16.
275    F16 = -49,
276    /// F17.
277    F17 = -50,
278    /// F18.
279    F18 = -51,
280    /// F19.
281    F19 = -52,
282    /// F20.
283    F20 = -53,
284    /// Shift+Enter.
285    ShiftEnter = -54,
286    /// Ctrl+Enter.
287    CtrlEnter = -55,
288    /// Ctrl+Shift+Enter.
289    CtrlShiftEnter = -56,
290}
291
292impl fmt::Display for KeyType {
293    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294        let name = match self {
295            KeyType::Null => "ctrl+@",
296            KeyType::CtrlA => "ctrl+a",
297            KeyType::CtrlB => "ctrl+b",
298            KeyType::CtrlC => "ctrl+c",
299            KeyType::CtrlD => "ctrl+d",
300            KeyType::CtrlE => "ctrl+e",
301            KeyType::CtrlF => "ctrl+f",
302            KeyType::CtrlG => "ctrl+g",
303            KeyType::CtrlH => "ctrl+h",
304            KeyType::Tab => "tab",
305            KeyType::CtrlJ => "ctrl+j",
306            KeyType::CtrlK => "ctrl+k",
307            KeyType::CtrlL => "ctrl+l",
308            KeyType::Enter => "enter",
309            KeyType::CtrlN => "ctrl+n",
310            KeyType::CtrlO => "ctrl+o",
311            KeyType::CtrlP => "ctrl+p",
312            KeyType::CtrlQ => "ctrl+q",
313            KeyType::CtrlR => "ctrl+r",
314            KeyType::CtrlS => "ctrl+s",
315            KeyType::CtrlT => "ctrl+t",
316            KeyType::CtrlU => "ctrl+u",
317            KeyType::CtrlV => "ctrl+v",
318            KeyType::CtrlW => "ctrl+w",
319            KeyType::CtrlX => "ctrl+x",
320            KeyType::CtrlY => "ctrl+y",
321            KeyType::CtrlZ => "ctrl+z",
322            KeyType::Esc => "esc",
323            KeyType::CtrlBackslash => "ctrl+\\",
324            KeyType::CtrlCloseBracket => "ctrl+]",
325            KeyType::CtrlCaret => "ctrl+^",
326            KeyType::CtrlUnderscore => "ctrl+_",
327            KeyType::Backspace => "backspace",
328            KeyType::Runes => "runes",
329            KeyType::Up => "up",
330            KeyType::Down => "down",
331            KeyType::Right => "right",
332            KeyType::Left => "left",
333            KeyType::ShiftTab => "shift+tab",
334            KeyType::Home => "home",
335            KeyType::End => "end",
336            KeyType::PgUp => "pgup",
337            KeyType::PgDown => "pgdown",
338            KeyType::CtrlPgUp => "ctrl+pgup",
339            KeyType::CtrlPgDown => "ctrl+pgdown",
340            KeyType::Delete => "delete",
341            KeyType::Insert => "insert",
342            KeyType::Space => " ",
343            KeyType::CtrlUp => "ctrl+up",
344            KeyType::CtrlDown => "ctrl+down",
345            KeyType::CtrlRight => "ctrl+right",
346            KeyType::CtrlLeft => "ctrl+left",
347            KeyType::CtrlHome => "ctrl+home",
348            KeyType::CtrlEnd => "ctrl+end",
349            KeyType::ShiftUp => "shift+up",
350            KeyType::ShiftDown => "shift+down",
351            KeyType::ShiftRight => "shift+right",
352            KeyType::ShiftLeft => "shift+left",
353            KeyType::ShiftHome => "shift+home",
354            KeyType::ShiftEnd => "shift+end",
355            KeyType::CtrlShiftUp => "ctrl+shift+up",
356            KeyType::CtrlShiftDown => "ctrl+shift+down",
357            KeyType::CtrlShiftLeft => "ctrl+shift+left",
358            KeyType::CtrlShiftRight => "ctrl+shift+right",
359            KeyType::CtrlShiftHome => "ctrl+shift+home",
360            KeyType::CtrlShiftEnd => "ctrl+shift+end",
361            KeyType::F1 => "f1",
362            KeyType::F2 => "f2",
363            KeyType::F3 => "f3",
364            KeyType::F4 => "f4",
365            KeyType::F5 => "f5",
366            KeyType::F6 => "f6",
367            KeyType::F7 => "f7",
368            KeyType::F8 => "f8",
369            KeyType::F9 => "f9",
370            KeyType::F10 => "f10",
371            KeyType::F11 => "f11",
372            KeyType::F12 => "f12",
373            KeyType::F13 => "f13",
374            KeyType::F14 => "f14",
375            KeyType::F15 => "f15",
376            KeyType::F16 => "f16",
377            KeyType::F17 => "f17",
378            KeyType::F18 => "f18",
379            KeyType::F19 => "f19",
380            KeyType::F20 => "f20",
381            KeyType::ShiftEnter => "shift+enter",
382            KeyType::CtrlEnter => "ctrl+enter",
383            KeyType::CtrlShiftEnter => "ctrl+shift+enter",
384        };
385        write!(f, "{}", name)
386    }
387}
388
389impl KeyType {
390    /// Check if this key type represents a control character.
391    pub fn is_ctrl(&self) -> bool {
392        let val = *self as i16;
393        (0..=31).contains(&val) || val == 127
394    }
395
396    /// Check if this is a function key (F1-F20).
397    pub fn is_function_key(&self) -> bool {
398        matches!(
399            self,
400            KeyType::F1
401                | KeyType::F2
402                | KeyType::F3
403                | KeyType::F4
404                | KeyType::F5
405                | KeyType::F6
406                | KeyType::F7
407                | KeyType::F8
408                | KeyType::F9
409                | KeyType::F10
410                | KeyType::F11
411                | KeyType::F12
412                | KeyType::F13
413                | KeyType::F14
414                | KeyType::F15
415                | KeyType::F16
416                | KeyType::F17
417                | KeyType::F18
418                | KeyType::F19
419                | KeyType::F20
420        )
421    }
422
423    /// Check if this is a cursor movement key.
424    pub fn is_cursor(&self) -> bool {
425        matches!(
426            self,
427            KeyType::Up
428                | KeyType::Down
429                | KeyType::Left
430                | KeyType::Right
431                | KeyType::Home
432                | KeyType::End
433                | KeyType::PgUp
434                | KeyType::PgDown
435                | KeyType::CtrlUp
436                | KeyType::CtrlDown
437                | KeyType::CtrlLeft
438                | KeyType::CtrlRight
439                | KeyType::CtrlHome
440                | KeyType::CtrlEnd
441                | KeyType::ShiftUp
442                | KeyType::ShiftDown
443                | KeyType::ShiftLeft
444                | KeyType::ShiftRight
445                | KeyType::ShiftHome
446                | KeyType::ShiftEnd
447                | KeyType::CtrlShiftUp
448                | KeyType::CtrlShiftDown
449                | KeyType::CtrlShiftLeft
450                | KeyType::CtrlShiftRight
451                | KeyType::CtrlShiftHome
452                | KeyType::CtrlShiftEnd
453                | KeyType::CtrlPgUp
454                | KeyType::CtrlPgDown
455        )
456    }
457}
458
459/// Convert a crossterm KeyCode to our KeyType.
460pub fn from_crossterm_key(
461    code: crossterm::event::KeyCode,
462    modifiers: crossterm::event::KeyModifiers,
463) -> KeyMsg {
464    use crossterm::event::{KeyCode, KeyModifiers};
465
466    let ctrl = modifiers.contains(KeyModifiers::CONTROL);
467    let shift = modifiers.contains(KeyModifiers::SHIFT);
468    let alt = modifiers.contains(KeyModifiers::ALT);
469
470    let (key_type, runes) = match code {
471        KeyCode::Char(c) if ctrl => {
472            let kt = match c.to_ascii_lowercase() {
473                '@' => KeyType::Null,
474                'a' => KeyType::CtrlA,
475                'b' => KeyType::CtrlB,
476                'c' => KeyType::CtrlC,
477                'd' => KeyType::CtrlD,
478                'e' => KeyType::CtrlE,
479                'f' => KeyType::CtrlF,
480                'g' => KeyType::CtrlG,
481                'h' => KeyType::CtrlH,
482                'i' => KeyType::Tab,
483                'j' => KeyType::CtrlJ,
484                'k' => KeyType::CtrlK,
485                'l' => KeyType::CtrlL,
486                'm' => KeyType::Enter,
487                'n' => KeyType::CtrlN,
488                'o' => KeyType::CtrlO,
489                'p' => KeyType::CtrlP,
490                'q' => KeyType::CtrlQ,
491                'r' => KeyType::CtrlR,
492                's' => KeyType::CtrlS,
493                't' => KeyType::CtrlT,
494                'u' => KeyType::CtrlU,
495                'v' => KeyType::CtrlV,
496                'w' => KeyType::CtrlW,
497                'x' => KeyType::CtrlX,
498                'y' => KeyType::CtrlY,
499                'z' => KeyType::CtrlZ,
500                '\\' => KeyType::CtrlBackslash,
501                ']' => KeyType::CtrlCloseBracket,
502                '^' => KeyType::CtrlCaret,
503                '_' => KeyType::CtrlUnderscore,
504                _ => {
505                    return KeyMsg {
506                        key_type: KeyType::Runes,
507                        runes: vec![c],
508                        alt,
509                        paste: false,
510                    };
511                }
512            };
513            (kt, Vec::new())
514        }
515        KeyCode::Char(' ') => (KeyType::Space, Vec::new()),
516        KeyCode::Char(c) => (KeyType::Runes, vec![c]),
517        KeyCode::Enter if ctrl && shift => (KeyType::CtrlShiftEnter, Vec::new()),
518        KeyCode::Enter if ctrl => (KeyType::CtrlEnter, Vec::new()),
519        KeyCode::Enter if shift => (KeyType::ShiftEnter, Vec::new()),
520        KeyCode::Enter => (KeyType::Enter, Vec::new()),
521        KeyCode::Backspace => (KeyType::Backspace, Vec::new()),
522        KeyCode::Tab if shift => (KeyType::ShiftTab, Vec::new()),
523        KeyCode::Tab => (KeyType::Tab, Vec::new()),
524        KeyCode::Esc => (KeyType::Esc, Vec::new()),
525        KeyCode::Delete => (KeyType::Delete, Vec::new()),
526        KeyCode::Insert => (KeyType::Insert, Vec::new()),
527        KeyCode::Up if ctrl && shift => (KeyType::CtrlShiftUp, Vec::new()),
528        KeyCode::Up if ctrl => (KeyType::CtrlUp, Vec::new()),
529        KeyCode::Up if shift => (KeyType::ShiftUp, Vec::new()),
530        KeyCode::Up => (KeyType::Up, Vec::new()),
531        KeyCode::Down if ctrl && shift => (KeyType::CtrlShiftDown, Vec::new()),
532        KeyCode::Down if ctrl => (KeyType::CtrlDown, Vec::new()),
533        KeyCode::Down if shift => (KeyType::ShiftDown, Vec::new()),
534        KeyCode::Down => (KeyType::Down, Vec::new()),
535        KeyCode::Left if ctrl && shift => (KeyType::CtrlShiftLeft, Vec::new()),
536        KeyCode::Left if ctrl => (KeyType::CtrlLeft, Vec::new()),
537        KeyCode::Left if shift => (KeyType::ShiftLeft, Vec::new()),
538        KeyCode::Left => (KeyType::Left, Vec::new()),
539        KeyCode::Right if ctrl && shift => (KeyType::CtrlShiftRight, Vec::new()),
540        KeyCode::Right if ctrl => (KeyType::CtrlRight, Vec::new()),
541        KeyCode::Right if shift => (KeyType::ShiftRight, Vec::new()),
542        KeyCode::Right => (KeyType::Right, Vec::new()),
543        KeyCode::Home if ctrl && shift => (KeyType::CtrlShiftHome, Vec::new()),
544        KeyCode::Home if ctrl => (KeyType::CtrlHome, Vec::new()),
545        KeyCode::Home if shift => (KeyType::ShiftHome, Vec::new()),
546        KeyCode::Home => (KeyType::Home, Vec::new()),
547        KeyCode::End if ctrl && shift => (KeyType::CtrlShiftEnd, Vec::new()),
548        KeyCode::End if ctrl => (KeyType::CtrlEnd, Vec::new()),
549        KeyCode::End if shift => (KeyType::ShiftEnd, Vec::new()),
550        KeyCode::End => (KeyType::End, Vec::new()),
551        KeyCode::PageUp if ctrl => (KeyType::CtrlPgUp, Vec::new()),
552        KeyCode::PageUp => (KeyType::PgUp, Vec::new()),
553        KeyCode::PageDown if ctrl => (KeyType::CtrlPgDown, Vec::new()),
554        KeyCode::PageDown => (KeyType::PgDown, Vec::new()),
555        KeyCode::F(1) => (KeyType::F1, Vec::new()),
556        KeyCode::F(2) => (KeyType::F2, Vec::new()),
557        KeyCode::F(3) => (KeyType::F3, Vec::new()),
558        KeyCode::F(4) => (KeyType::F4, Vec::new()),
559        KeyCode::F(5) => (KeyType::F5, Vec::new()),
560        KeyCode::F(6) => (KeyType::F6, Vec::new()),
561        KeyCode::F(7) => (KeyType::F7, Vec::new()),
562        KeyCode::F(8) => (KeyType::F8, Vec::new()),
563        KeyCode::F(9) => (KeyType::F9, Vec::new()),
564        KeyCode::F(10) => (KeyType::F10, Vec::new()),
565        KeyCode::F(11) => (KeyType::F11, Vec::new()),
566        KeyCode::F(12) => (KeyType::F12, Vec::new()),
567        KeyCode::F(13) => (KeyType::F13, Vec::new()),
568        KeyCode::F(14) => (KeyType::F14, Vec::new()),
569        KeyCode::F(15) => (KeyType::F15, Vec::new()),
570        KeyCode::F(16) => (KeyType::F16, Vec::new()),
571        KeyCode::F(17) => (KeyType::F17, Vec::new()),
572        KeyCode::F(18) => (KeyType::F18, Vec::new()),
573        KeyCode::F(19) => (KeyType::F19, Vec::new()),
574        KeyCode::F(20) => (KeyType::F20, Vec::new()),
575        _ => (KeyType::Runes, Vec::new()),
576    };
577
578    KeyMsg {
579        key_type,
580        runes,
581        alt,
582        paste: false,
583    }
584}
585
586/// Parse a raw ANSI escape sequence into a KeyMsg.
587///
588/// This function parses terminal escape sequences (like arrow keys, function keys,
589/// etc.) into their corresponding KeyMsg values. It matches the behavior of the
590/// Go bubbletea library's sequence parsing.
591///
592/// # Arguments
593///
594/// * `input` - A byte slice containing an ANSI escape sequence
595///
596/// # Returns
597///
598/// Returns `Some(KeyMsg)` if the sequence was recognized, `None` otherwise.
599///
600/// # Example
601///
602/// ```rust
603/// use bubbletea::{parse_sequence, KeyType};
604///
605/// // Parse arrow up sequence
606/// let key = parse_sequence(b"\x1b[A").unwrap();
607/// assert_eq!(key.key_type, KeyType::Up);
608/// assert!(!key.alt);
609/// ```
610pub fn parse_sequence(input: &[u8]) -> Option<KeyMsg> {
611    // Convert to string for easier matching
612    let seq = std::str::from_utf8(input).ok()?;
613
614    // Try to match known sequences (longest match first approach like Go)
615    SEQUENCES.get(seq).cloned()
616}
617
618/// Parse a raw ANSI escape sequence from the start of the input.
619///
620/// This function attempts to find the longest known ANSI escape sequence
621/// that matches the beginning of the input slice.
622///
623/// # Arguments
624///
625/// * `input` - A byte slice
626///
627/// # Returns
628///
629/// Returns `Some((KeyMsg, usize))` where usize is the number of bytes consumed,
630/// or `None` if no sequence matched the start of the input.
631///
632/// # Example
633///
634/// ```rust
635/// use bubbletea::{parse_sequence_prefix, KeyType};
636///
637/// let input = b"\x1b[A\x1b[B";
638/// let (key, len) = parse_sequence_prefix(input).unwrap();
639/// assert_eq!(key.key_type, KeyType::Up);
640/// assert_eq!(len, 3);
641/// ```
642pub fn parse_sequence_prefix(input: &[u8]) -> Option<(KeyMsg, usize)> {
643    let s = std::str::from_utf8(input).ok()?;
644
645    // We need to find the longest sequence that 's' starts with.
646    // Iterating the whole map is O(N), where N is ~100. This is fast enough.
647    let mut best_match: Option<(KeyMsg, usize)> = None;
648
649    for (seq, key) in SEQUENCES.iter() {
650        if s.starts_with(seq) {
651            let len = seq.len();
652            match best_match {
653                None => best_match = Some((key.clone(), len)),
654                Some((_, best_len)) => {
655                    if len > best_len {
656                        best_match = Some((key.clone(), len));
657                    }
658                }
659            }
660        }
661    }
662
663    best_match
664}
665
666/// Check if the input bytes form a prefix of any known ANSI sequence.
667///
668/// # Arguments
669///
670/// * `input` - A byte slice
671///
672/// # Returns
673///
674/// Returns `true` if the input is a prefix of a known sequence, `false` otherwise.
675pub fn is_sequence_prefix(input: &[u8]) -> bool {
676    let s = match std::str::from_utf8(input) {
677        Ok(s) => s,
678        Err(_) => return false,
679    };
680
681    SEQUENCES.keys().any(|seq| seq.starts_with(s))
682}
683
684use std::collections::HashMap;
685use std::sync::LazyLock;
686
687/// Mapping of ANSI escape sequences to KeyMsg values.
688/// This matches the Go bubbletea library's `sequences` map.
689static SEQUENCES: LazyLock<HashMap<&'static str, KeyMsg>> = LazyLock::new(|| {
690    let mut m = HashMap::new();
691
692    // Arrow keys
693    m.insert("\x1b[A", KeyMsg::from_type(KeyType::Up));
694    m.insert("\x1b[B", KeyMsg::from_type(KeyType::Down));
695    m.insert("\x1b[C", KeyMsg::from_type(KeyType::Right));
696    m.insert("\x1b[D", KeyMsg::from_type(KeyType::Left));
697
698    // Shift + Arrow keys
699    m.insert("\x1b[1;2A", KeyMsg::from_type(KeyType::ShiftUp));
700    m.insert("\x1b[1;2B", KeyMsg::from_type(KeyType::ShiftDown));
701    m.insert("\x1b[1;2C", KeyMsg::from_type(KeyType::ShiftRight));
702    m.insert("\x1b[1;2D", KeyMsg::from_type(KeyType::ShiftLeft));
703    // DECCKM variants
704    m.insert("\x1b[OA", KeyMsg::from_type(KeyType::ShiftUp));
705    m.insert("\x1b[OB", KeyMsg::from_type(KeyType::ShiftDown));
706    m.insert("\x1b[OC", KeyMsg::from_type(KeyType::ShiftRight));
707    m.insert("\x1b[OD", KeyMsg::from_type(KeyType::ShiftLeft));
708    // urxvt variants
709    m.insert("\x1b[a", KeyMsg::from_type(KeyType::ShiftUp));
710    m.insert("\x1b[b", KeyMsg::from_type(KeyType::ShiftDown));
711    m.insert("\x1b[c", KeyMsg::from_type(KeyType::ShiftRight));
712    m.insert("\x1b[d", KeyMsg::from_type(KeyType::ShiftLeft));
713
714    // Alt + Arrow keys
715    m.insert("\x1b[1;3A", KeyMsg::from_type(KeyType::Up).with_alt());
716    m.insert("\x1b[1;3B", KeyMsg::from_type(KeyType::Down).with_alt());
717    m.insert("\x1b[1;3C", KeyMsg::from_type(KeyType::Right).with_alt());
718    m.insert("\x1b[1;3D", KeyMsg::from_type(KeyType::Left).with_alt());
719
720    // Alt + Shift + Arrow keys
721    m.insert("\x1b[1;4A", KeyMsg::from_type(KeyType::ShiftUp).with_alt());
722    m.insert(
723        "\x1b[1;4B",
724        KeyMsg::from_type(KeyType::ShiftDown).with_alt(),
725    );
726    m.insert(
727        "\x1b[1;4C",
728        KeyMsg::from_type(KeyType::ShiftRight).with_alt(),
729    );
730    m.insert(
731        "\x1b[1;4D",
732        KeyMsg::from_type(KeyType::ShiftLeft).with_alt(),
733    );
734
735    // Ctrl + Arrow keys
736    m.insert("\x1b[1;5A", KeyMsg::from_type(KeyType::CtrlUp));
737    m.insert("\x1b[1;5B", KeyMsg::from_type(KeyType::CtrlDown));
738    m.insert("\x1b[1;5C", KeyMsg::from_type(KeyType::CtrlRight));
739    m.insert("\x1b[1;5D", KeyMsg::from_type(KeyType::CtrlLeft));
740    // urxvt Ctrl+Arrow variants (with Alt)
741    m.insert("\x1b[Oa", KeyMsg::from_type(KeyType::CtrlUp).with_alt());
742    m.insert("\x1b[Ob", KeyMsg::from_type(KeyType::CtrlDown).with_alt());
743    m.insert("\x1b[Oc", KeyMsg::from_type(KeyType::CtrlRight).with_alt());
744    m.insert("\x1b[Od", KeyMsg::from_type(KeyType::CtrlLeft).with_alt());
745
746    // Ctrl + Shift + Arrow keys
747    m.insert("\x1b[1;6A", KeyMsg::from_type(KeyType::CtrlShiftUp));
748    m.insert("\x1b[1;6B", KeyMsg::from_type(KeyType::CtrlShiftDown));
749    m.insert("\x1b[1;6C", KeyMsg::from_type(KeyType::CtrlShiftRight));
750    m.insert("\x1b[1;6D", KeyMsg::from_type(KeyType::CtrlShiftLeft));
751
752    // Ctrl + Alt + Arrow keys
753    m.insert("\x1b[1;7A", KeyMsg::from_type(KeyType::CtrlUp).with_alt());
754    m.insert("\x1b[1;7B", KeyMsg::from_type(KeyType::CtrlDown).with_alt());
755    m.insert(
756        "\x1b[1;7C",
757        KeyMsg::from_type(KeyType::CtrlRight).with_alt(),
758    );
759    m.insert("\x1b[1;7D", KeyMsg::from_type(KeyType::CtrlLeft).with_alt());
760
761    // Ctrl + Shift + Alt + Arrow keys
762    m.insert(
763        "\x1b[1;8A",
764        KeyMsg::from_type(KeyType::CtrlShiftUp).with_alt(),
765    );
766    m.insert(
767        "\x1b[1;8B",
768        KeyMsg::from_type(KeyType::CtrlShiftDown).with_alt(),
769    );
770    m.insert(
771        "\x1b[1;8C",
772        KeyMsg::from_type(KeyType::CtrlShiftRight).with_alt(),
773    );
774    m.insert(
775        "\x1b[1;8D",
776        KeyMsg::from_type(KeyType::CtrlShiftLeft).with_alt(),
777    );
778
779    // Shift+Tab
780    m.insert("\x1b[Z", KeyMsg::from_type(KeyType::ShiftTab));
781
782    // Insert
783    m.insert("\x1b[2~", KeyMsg::from_type(KeyType::Insert));
784    m.insert("\x1b[3;2~", KeyMsg::from_type(KeyType::Insert).with_alt());
785
786    // Delete
787    m.insert("\x1b[3~", KeyMsg::from_type(KeyType::Delete));
788    m.insert("\x1b[3;3~", KeyMsg::from_type(KeyType::Delete).with_alt());
789
790    // Page Up
791    m.insert("\x1b[5~", KeyMsg::from_type(KeyType::PgUp));
792    m.insert("\x1b[5;3~", KeyMsg::from_type(KeyType::PgUp).with_alt());
793    m.insert("\x1b[5;5~", KeyMsg::from_type(KeyType::CtrlPgUp));
794    m.insert("\x1b[5^", KeyMsg::from_type(KeyType::CtrlPgUp)); // urxvt
795    m.insert("\x1b[5;7~", KeyMsg::from_type(KeyType::CtrlPgUp).with_alt());
796
797    // Page Down
798    m.insert("\x1b[6~", KeyMsg::from_type(KeyType::PgDown));
799    m.insert("\x1b[6;3~", KeyMsg::from_type(KeyType::PgDown).with_alt());
800    m.insert("\x1b[6;5~", KeyMsg::from_type(KeyType::CtrlPgDown));
801    m.insert("\x1b[6^", KeyMsg::from_type(KeyType::CtrlPgDown)); // urxvt
802    m.insert(
803        "\x1b[6;7~",
804        KeyMsg::from_type(KeyType::CtrlPgDown).with_alt(),
805    );
806
807    // Home
808    m.insert("\x1b[1~", KeyMsg::from_type(KeyType::Home));
809    m.insert("\x1b[H", KeyMsg::from_type(KeyType::Home)); // xterm, lxterm
810    m.insert("\x1b[1;3H", KeyMsg::from_type(KeyType::Home).with_alt());
811    m.insert("\x1b[1;5H", KeyMsg::from_type(KeyType::CtrlHome));
812    m.insert("\x1b[1;7H", KeyMsg::from_type(KeyType::CtrlHome).with_alt());
813    m.insert("\x1b[1;2H", KeyMsg::from_type(KeyType::ShiftHome));
814    m.insert(
815        "\x1b[1;4H",
816        KeyMsg::from_type(KeyType::ShiftHome).with_alt(),
817    );
818    m.insert("\x1b[1;6H", KeyMsg::from_type(KeyType::CtrlShiftHome));
819    m.insert(
820        "\x1b[1;8H",
821        KeyMsg::from_type(KeyType::CtrlShiftHome).with_alt(),
822    );
823    m.insert("\x1b[7~", KeyMsg::from_type(KeyType::Home)); // urxvt
824    m.insert("\x1b[7^", KeyMsg::from_type(KeyType::CtrlHome)); // urxvt
825    m.insert("\x1b[7$", KeyMsg::from_type(KeyType::ShiftHome)); // urxvt
826    m.insert("\x1b[7@", KeyMsg::from_type(KeyType::CtrlShiftHome)); // urxvt
827
828    // End
829    m.insert("\x1b[4~", KeyMsg::from_type(KeyType::End));
830    m.insert("\x1b[F", KeyMsg::from_type(KeyType::End)); // xterm, lxterm
831    m.insert("\x1b[1;3F", KeyMsg::from_type(KeyType::End).with_alt());
832    m.insert("\x1b[1;5F", KeyMsg::from_type(KeyType::CtrlEnd));
833    m.insert("\x1b[1;7F", KeyMsg::from_type(KeyType::CtrlEnd).with_alt());
834    m.insert("\x1b[1;2F", KeyMsg::from_type(KeyType::ShiftEnd));
835    m.insert("\x1b[1;4F", KeyMsg::from_type(KeyType::ShiftEnd).with_alt());
836    m.insert("\x1b[1;6F", KeyMsg::from_type(KeyType::CtrlShiftEnd));
837    m.insert(
838        "\x1b[1;8F",
839        KeyMsg::from_type(KeyType::CtrlShiftEnd).with_alt(),
840    );
841    m.insert("\x1b[8~", KeyMsg::from_type(KeyType::End)); // urxvt
842    m.insert("\x1b[8^", KeyMsg::from_type(KeyType::CtrlEnd)); // urxvt
843    m.insert("\x1b[8$", KeyMsg::from_type(KeyType::ShiftEnd)); // urxvt
844    m.insert("\x1b[8@", KeyMsg::from_type(KeyType::CtrlShiftEnd)); // urxvt
845
846    // Function keys - Linux console
847    m.insert("\x1b[[A", KeyMsg::from_type(KeyType::F1));
848    m.insert("\x1b[[B", KeyMsg::from_type(KeyType::F2));
849    m.insert("\x1b[[C", KeyMsg::from_type(KeyType::F3));
850    m.insert("\x1b[[D", KeyMsg::from_type(KeyType::F4));
851    m.insert("\x1b[[E", KeyMsg::from_type(KeyType::F5));
852
853    // Function keys - VT100/xterm F1-F4
854    m.insert("\x1bOP", KeyMsg::from_type(KeyType::F1));
855    m.insert("\x1bOQ", KeyMsg::from_type(KeyType::F2));
856    m.insert("\x1bOR", KeyMsg::from_type(KeyType::F3));
857    m.insert("\x1bOS", KeyMsg::from_type(KeyType::F4));
858
859    // Function keys - VT100/xterm F1-F4 with Alt
860    m.insert("\x1b[1;3P", KeyMsg::from_type(KeyType::F1).with_alt());
861    m.insert("\x1b[1;3Q", KeyMsg::from_type(KeyType::F2).with_alt());
862    m.insert("\x1b[1;3R", KeyMsg::from_type(KeyType::F3).with_alt());
863    m.insert("\x1b[1;3S", KeyMsg::from_type(KeyType::F4).with_alt());
864
865    // Function keys - urxvt F1-F4
866    m.insert("\x1b[11~", KeyMsg::from_type(KeyType::F1));
867    m.insert("\x1b[12~", KeyMsg::from_type(KeyType::F2));
868    m.insert("\x1b[13~", KeyMsg::from_type(KeyType::F3));
869    m.insert("\x1b[14~", KeyMsg::from_type(KeyType::F4));
870
871    // Function keys F5-F12
872    m.insert("\x1b[15~", KeyMsg::from_type(KeyType::F5));
873    m.insert("\x1b[15;3~", KeyMsg::from_type(KeyType::F5).with_alt());
874    m.insert("\x1b[17~", KeyMsg::from_type(KeyType::F6));
875    m.insert("\x1b[17;3~", KeyMsg::from_type(KeyType::F6).with_alt());
876    m.insert("\x1b[18~", KeyMsg::from_type(KeyType::F7));
877    m.insert("\x1b[18;3~", KeyMsg::from_type(KeyType::F7).with_alt());
878    m.insert("\x1b[19~", KeyMsg::from_type(KeyType::F8));
879    m.insert("\x1b[19;3~", KeyMsg::from_type(KeyType::F8).with_alt());
880    m.insert("\x1b[20~", KeyMsg::from_type(KeyType::F9));
881    m.insert("\x1b[20;3~", KeyMsg::from_type(KeyType::F9).with_alt());
882    m.insert("\x1b[21~", KeyMsg::from_type(KeyType::F10));
883    m.insert("\x1b[21;3~", KeyMsg::from_type(KeyType::F10).with_alt());
884    m.insert("\x1b[23~", KeyMsg::from_type(KeyType::F11));
885    m.insert("\x1b[23;3~", KeyMsg::from_type(KeyType::F11).with_alt());
886    m.insert("\x1b[24~", KeyMsg::from_type(KeyType::F12));
887    m.insert("\x1b[24;3~", KeyMsg::from_type(KeyType::F12).with_alt());
888
889    // Function keys F13-F16
890    m.insert("\x1b[1;2P", KeyMsg::from_type(KeyType::F13));
891    m.insert("\x1b[1;2Q", KeyMsg::from_type(KeyType::F14));
892    m.insert("\x1b[25~", KeyMsg::from_type(KeyType::F13));
893    m.insert("\x1b[26~", KeyMsg::from_type(KeyType::F14));
894    m.insert("\x1b[25;3~", KeyMsg::from_type(KeyType::F13).with_alt());
895    m.insert("\x1b[26;3~", KeyMsg::from_type(KeyType::F14).with_alt());
896    m.insert("\x1b[1;2R", KeyMsg::from_type(KeyType::F15));
897    m.insert("\x1b[1;2S", KeyMsg::from_type(KeyType::F16));
898    m.insert("\x1b[28~", KeyMsg::from_type(KeyType::F15));
899    m.insert("\x1b[29~", KeyMsg::from_type(KeyType::F16));
900    m.insert("\x1b[28;3~", KeyMsg::from_type(KeyType::F15).with_alt());
901    m.insert("\x1b[29;3~", KeyMsg::from_type(KeyType::F16).with_alt());
902
903    // Function keys F17-F20
904    m.insert("\x1b[15;2~", KeyMsg::from_type(KeyType::F17));
905    m.insert("\x1b[17;2~", KeyMsg::from_type(KeyType::F18));
906    m.insert("\x1b[18;2~", KeyMsg::from_type(KeyType::F19));
907    m.insert("\x1b[19;2~", KeyMsg::from_type(KeyType::F20));
908    m.insert("\x1b[31~", KeyMsg::from_type(KeyType::F17));
909    m.insert("\x1b[32~", KeyMsg::from_type(KeyType::F18));
910    m.insert("\x1b[33~", KeyMsg::from_type(KeyType::F19));
911    m.insert("\x1b[34~", KeyMsg::from_type(KeyType::F20));
912
913    // PowerShell sequences
914    m.insert("\x1bOA", KeyMsg::from_type(KeyType::Up));
915    m.insert("\x1bOB", KeyMsg::from_type(KeyType::Down));
916    m.insert("\x1bOC", KeyMsg::from_type(KeyType::Right));
917    m.insert("\x1bOD", KeyMsg::from_type(KeyType::Left));
918
919    m
920});
921
922#[cfg(test)]
923mod tests {
924    use super::*;
925    use proptest::prelude::*;
926
927    fn sequence_strategy() -> impl Strategy<Value = &'static str> {
928        let sequences: Vec<&'static str> = SEQUENCES.keys().copied().collect();
929        prop::sample::select(sequences)
930    }
931
932    #[test]
933    fn test_parse_sequence_arrows() {
934        assert_eq!(
935            parse_sequence(b"\x1b[A"),
936            Some(KeyMsg::from_type(KeyType::Up))
937        );
938        assert_eq!(
939            parse_sequence(b"\x1b[B"),
940            Some(KeyMsg::from_type(KeyType::Down))
941        );
942        assert_eq!(
943            parse_sequence(b"\x1b[C"),
944            Some(KeyMsg::from_type(KeyType::Right))
945        );
946        assert_eq!(
947            parse_sequence(b"\x1b[D"),
948            Some(KeyMsg::from_type(KeyType::Left))
949        );
950    }
951
952    #[test]
953    fn test_parse_sequence_alt_arrows() {
954        assert_eq!(
955            parse_sequence(b"\x1b[1;3A"),
956            Some(KeyMsg::from_type(KeyType::Up).with_alt())
957        );
958        assert_eq!(
959            parse_sequence(b"\x1b[1;3B"),
960            Some(KeyMsg::from_type(KeyType::Down).with_alt())
961        );
962    }
963
964    #[test]
965    fn test_parse_sequence_ctrl_arrows() {
966        assert_eq!(
967            parse_sequence(b"\x1b[1;5A"),
968            Some(KeyMsg::from_type(KeyType::CtrlUp))
969        );
970        assert_eq!(
971            parse_sequence(b"\x1b[1;5B"),
972            Some(KeyMsg::from_type(KeyType::CtrlDown))
973        );
974        assert_eq!(
975            parse_sequence(b"\x1b[1;5C"),
976            Some(KeyMsg::from_type(KeyType::CtrlRight))
977        );
978        assert_eq!(
979            parse_sequence(b"\x1b[1;5D"),
980            Some(KeyMsg::from_type(KeyType::CtrlLeft))
981        );
982    }
983
984    #[test]
985    fn test_parse_sequence_ctrl_alt_arrows() {
986        // Ctrl+Alt+Arrow keys (modifier 7)
987        assert_eq!(
988            parse_sequence(b"\x1b[1;7A"),
989            Some(KeyMsg::from_type(KeyType::CtrlUp).with_alt())
990        );
991        assert_eq!(
992            parse_sequence(b"\x1b[1;7B"),
993            Some(KeyMsg::from_type(KeyType::CtrlDown).with_alt())
994        );
995        assert_eq!(
996            parse_sequence(b"\x1b[1;7C"),
997            Some(KeyMsg::from_type(KeyType::CtrlRight).with_alt())
998        );
999        assert_eq!(
1000            parse_sequence(b"\x1b[1;7D"),
1001            Some(KeyMsg::from_type(KeyType::CtrlLeft).with_alt())
1002        );
1003
1004        // Verify alt flag is properly set
1005        let key = parse_sequence(b"\x1b[1;7A").unwrap();
1006        assert!(key.alt);
1007        assert_eq!(key.key_type, KeyType::CtrlUp);
1008    }
1009
1010    #[test]
1011    fn test_parse_sequence_ctrl_shift_alt_arrows() {
1012        // Ctrl+Shift+Alt+Arrow keys (modifier 8)
1013        assert_eq!(
1014            parse_sequence(b"\x1b[1;8A"),
1015            Some(KeyMsg::from_type(KeyType::CtrlShiftUp).with_alt())
1016        );
1017        assert_eq!(
1018            parse_sequence(b"\x1b[1;8B"),
1019            Some(KeyMsg::from_type(KeyType::CtrlShiftDown).with_alt())
1020        );
1021        assert_eq!(
1022            parse_sequence(b"\x1b[1;8C"),
1023            Some(KeyMsg::from_type(KeyType::CtrlShiftRight).with_alt())
1024        );
1025        assert_eq!(
1026            parse_sequence(b"\x1b[1;8D"),
1027            Some(KeyMsg::from_type(KeyType::CtrlShiftLeft).with_alt())
1028        );
1029    }
1030
1031    #[test]
1032    fn test_parse_sequence_function_keys() {
1033        assert_eq!(
1034            parse_sequence(b"\x1bOP"),
1035            Some(KeyMsg::from_type(KeyType::F1))
1036        );
1037        assert_eq!(
1038            parse_sequence(b"\x1b[15~"),
1039            Some(KeyMsg::from_type(KeyType::F5))
1040        );
1041        assert_eq!(
1042            parse_sequence(b"\x1b[24~"),
1043            Some(KeyMsg::from_type(KeyType::F12))
1044        );
1045    }
1046
1047    #[test]
1048    fn test_parse_sequence_special_keys() {
1049        assert_eq!(
1050            parse_sequence(b"\x1b[Z"),
1051            Some(KeyMsg::from_type(KeyType::ShiftTab))
1052        );
1053        assert_eq!(
1054            parse_sequence(b"\x1b[2~"),
1055            Some(KeyMsg::from_type(KeyType::Insert))
1056        );
1057        assert_eq!(
1058            parse_sequence(b"\x1b[3~"),
1059            Some(KeyMsg::from_type(KeyType::Delete))
1060        );
1061        assert_eq!(
1062            parse_sequence(b"\x1b[5~"),
1063            Some(KeyMsg::from_type(KeyType::PgUp))
1064        );
1065        assert_eq!(
1066            parse_sequence(b"\x1b[6~"),
1067            Some(KeyMsg::from_type(KeyType::PgDown))
1068        );
1069    }
1070
1071    #[test]
1072    fn test_parse_sequence_home_end() {
1073        assert_eq!(
1074            parse_sequence(b"\x1b[H"),
1075            Some(KeyMsg::from_type(KeyType::Home))
1076        );
1077        assert_eq!(
1078            parse_sequence(b"\x1b[1~"),
1079            Some(KeyMsg::from_type(KeyType::Home))
1080        );
1081        assert_eq!(
1082            parse_sequence(b"\x1b[F"),
1083            Some(KeyMsg::from_type(KeyType::End))
1084        );
1085        assert_eq!(
1086            parse_sequence(b"\x1b[4~"),
1087            Some(KeyMsg::from_type(KeyType::End))
1088        );
1089    }
1090
1091    #[test]
1092    fn test_parse_sequence_unknown() {
1093        assert_eq!(parse_sequence(b"unknown"), None);
1094        assert_eq!(parse_sequence(b"\x1b[999~"), None);
1095    }
1096
1097    #[test]
1098    fn test_key_msg_display() {
1099        let key = KeyMsg::from_type(KeyType::Enter);
1100        assert_eq!(key.to_string(), "enter");
1101
1102        let key = KeyMsg::from_char('a');
1103        assert_eq!(key.to_string(), "a");
1104
1105        let key = KeyMsg::from_char('a').with_alt();
1106        assert_eq!(key.to_string(), "alt+a");
1107
1108        let key = KeyMsg::from_runes(vec!['h', 'e', 'l', 'l', 'o']).with_paste();
1109        assert_eq!(key.to_string(), "[hello]");
1110    }
1111
1112    #[test]
1113    fn test_key_type_display() {
1114        assert_eq!(KeyType::Enter.to_string(), "enter");
1115        assert_eq!(KeyType::CtrlC.to_string(), "ctrl+c");
1116        assert_eq!(KeyType::F1.to_string(), "f1");
1117    }
1118
1119    #[test]
1120    fn test_key_type_is_ctrl() {
1121        assert!(KeyType::CtrlC.is_ctrl());
1122        assert!(KeyType::Enter.is_ctrl());
1123        assert!(!KeyType::Up.is_ctrl());
1124    }
1125
1126    #[test]
1127    fn test_key_type_is_function_key() {
1128        assert!(KeyType::F1.is_function_key());
1129        assert!(KeyType::F12.is_function_key());
1130        assert!(!KeyType::Enter.is_function_key());
1131    }
1132
1133    #[test]
1134    fn test_key_type_is_cursor() {
1135        assert!(KeyType::Up.is_cursor());
1136        assert!(KeyType::CtrlLeft.is_cursor());
1137        assert!(!KeyType::Enter.is_cursor());
1138    }
1139
1140    proptest! {
1141        #[test]
1142        fn prop_parse_sequence_matches_table(seq in sequence_strategy()) {
1143            let expected = SEQUENCES.get(seq).cloned();
1144            prop_assert_eq!(parse_sequence(seq.as_bytes()), expected);
1145        }
1146    }
1147}