rustyline/
keys.rs

1//! Key constants
2
3/// Input key pressed and modifiers
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub struct KeyEvent(pub KeyCode, pub Modifiers);
6
7impl KeyEvent {
8    /// Constant value representing an unmodified press of `KeyCode::Backspace`.
9    pub(crate) const BACKSPACE: Self = Self(KeyCode::Backspace, Modifiers::NONE);
10    /// Constant value representing an unmodified press of `KeyCode::Enter`.
11    pub(crate) const ENTER: Self = Self(KeyCode::Enter, Modifiers::NONE);
12    /// Constant value representing an unmodified press of `KeyCode::Esc`.
13    pub(crate) const ESC: Self = Self(KeyCode::Esc, Modifiers::NONE);
14
15    /// Constructor from `char` and modifiers
16    #[must_use]
17    pub fn new(c: char, mut mods: Modifiers) -> Self {
18        use {KeyCode as K, KeyEvent as E, Modifiers as M};
19
20        if !c.is_control() {
21            if !mods.is_empty() {
22                mods.remove(M::SHIFT); // TODO Validate: no SHIFT even if
23                                       // `c` is uppercase
24            }
25            return E(K::Char(c), mods);
26        }
27        #[allow(clippy::match_same_arms)]
28        match c {
29            '\x00' => E(K::Char('@'), mods | M::CTRL), // '\0'
30            '\x01' => E(K::Char('A'), mods | M::CTRL),
31            '\x02' => E(K::Char('B'), mods | M::CTRL),
32            '\x03' => E(K::Char('C'), mods | M::CTRL),
33            '\x04' => E(K::Char('D'), mods | M::CTRL),
34            '\x05' => E(K::Char('E'), mods | M::CTRL),
35            '\x06' => E(K::Char('F'), mods | M::CTRL),
36            '\x07' => E(K::Char('G'), mods | M::CTRL), // '\a'
37            #[cfg(unix)]
38            '\x08' => E(K::Backspace, mods), // '\b'
39            #[cfg(windows)]
40            '\x08' => E(K::Char('H'), mods | M::CTRL),
41            #[cfg(unix)]
42            '\x09' => {
43                // '\t'
44                if mods.contains(M::SHIFT) {
45                    mods.remove(M::SHIFT);
46                    E(K::BackTab, mods)
47                } else {
48                    E(K::Tab, mods)
49                }
50            }
51            #[cfg(windows)]
52            '\x09' => E(K::Char('I'), mods | M::CTRL),
53            '\x0a' => E(K::Char('J'), mods | M::CTRL), // '\n' (10)
54            '\x0b' => E(K::Char('K'), mods | M::CTRL),
55            '\x0c' => E(K::Char('L'), mods | M::CTRL),
56            #[cfg(unix)]
57            '\x0d' => E(K::Enter, mods), // '\r' (13)
58            #[cfg(windows)]
59            '\x0d' => E(K::Char('M'), mods | M::CTRL),
60            '\x0e' => E(K::Char('N'), mods | M::CTRL),
61            '\x0f' => E(K::Char('O'), mods | M::CTRL),
62            '\x10' => E(K::Char('P'), mods | M::CTRL),
63            '\x11' => E(K::Char('Q'), mods | M::CTRL),
64            '\x12' => E(K::Char('R'), mods | M::CTRL),
65            '\x13' => E(K::Char('S'), mods | M::CTRL),
66            '\x14' => E(K::Char('T'), mods | M::CTRL),
67            '\x15' => E(K::Char('U'), mods | M::CTRL),
68            '\x16' => E(K::Char('V'), mods | M::CTRL),
69            '\x17' => E(K::Char('W'), mods | M::CTRL),
70            '\x18' => E(K::Char('X'), mods | M::CTRL),
71            '\x19' => E(K::Char('Y'), mods | M::CTRL),
72            '\x1a' => E(K::Char('Z'), mods | M::CTRL),
73            '\x1b' => E(K::Esc, mods), // Ctrl-[, '\e'
74            '\x1c' => E(K::Char('\\'), mods | M::CTRL),
75            '\x1d' => E(K::Char(']'), mods | M::CTRL),
76            '\x1e' => E(K::Char('^'), mods | M::CTRL),
77            '\x1f' => E(K::Char('_'), mods | M::CTRL),
78            '\x7f' => E(K::Backspace, mods), // Rubout, Ctrl-?
79            '\u{9b}' => E(K::Esc, mods | M::SHIFT),
80            _ => E(K::Null, mods),
81        }
82    }
83
84    /// Constructor from `char` with Ctrl modifier
85    #[must_use]
86    pub fn ctrl(c: char) -> Self {
87        Self::new(c, Modifiers::CTRL)
88    }
89
90    /// Constructor from `char` with Alt modifier
91    #[must_use]
92    pub fn alt(c: char) -> Self {
93        Self::new(c, Modifiers::ALT)
94    }
95
96    /// ctrl-a => ctrl-A (uppercase)
97    /// shift-A => A (no SHIFT modifier)
98    /// shift-Tab => `BackTab`
99    #[must_use]
100    pub fn normalize(e: Self) -> Self {
101        use {KeyCode as K, KeyEvent as E, Modifiers as M};
102
103        match e {
104            E(K::Char(c), m) if c.is_ascii_control() => Self::new(c, m),
105            E(K::Char(c), m) if c.is_ascii_lowercase() && m.contains(M::CTRL) => {
106                E(K::Char(c.to_ascii_uppercase()), m)
107            }
108            E(K::Char(c), m) if c.is_ascii_uppercase() && m.contains(M::SHIFT) => {
109                E(K::Char(c), m ^ M::SHIFT)
110            }
111            E(K::Tab, m) if m.contains(M::SHIFT) => E(K::BackTab, m ^ M::SHIFT),
112            _ => e,
113        }
114    }
115}
116
117impl From<char> for KeyEvent {
118    fn from(c: char) -> Self {
119        Self::new(c, Modifiers::NONE)
120    }
121}
122
123/// Input key pressed
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
125#[non_exhaustive]
126pub enum KeyCode {
127    /// Unsupported escape sequence (on unix platform)
128    UnknownEscSeq,
129    /// ⌫ or Ctrl-H
130    Backspace,
131    /// ⇤ (usually Shift-Tab)
132    BackTab,
133    /// Paste (on unix platform)
134    BracketedPasteStart,
135    /// Paste (on unix platform)
136    BracketedPasteEnd,
137    /// Single char
138    Char(char),
139    /// ⌦
140    Delete,
141    /// ↓ arrow key
142    Down,
143    /// ⇲
144    End,
145    /// ↵ or Ctrl-M
146    Enter,
147    /// Escape or Ctrl-[
148    Esc,
149    /// Function key
150    F(u8),
151    /// ⇱
152    Home,
153    /// Insert key
154    Insert,
155    /// ← arrow key
156    Left,
157    /// \0
158    Null,
159    /// ⇟
160    PageDown,
161    /// ⇞
162    PageUp,
163    /// → arrow key
164    Right,
165    /// ⇥ or Ctrl-I
166    Tab,
167    /// ↑ arrow key
168    Up,
169}
170
171bitflags::bitflags! {
172    /// The set of modifier keys that were triggered along with a key press.
173    pub struct Modifiers: u8 {
174        /// Control modifier
175        const CTRL  = 1<<3;
176        /// Escape or Alt modifier
177        const ALT  = 1<<2;
178        /// Shift modifier
179        const SHIFT = 1<<1;
180
181        /// No modifier
182        const NONE = 0;
183        /// Ctrl + Shift
184        const CTRL_SHIFT = Self::CTRL.bits | Self::SHIFT.bits;
185        /// Alt + Shift
186        const ALT_SHIFT = Self::ALT.bits | Self::SHIFT.bits;
187        /// Ctrl + Alt
188        const CTRL_ALT = Self::CTRL.bits | Self::ALT.bits;
189        /// Ctrl + Alt + Shift
190        const CTRL_ALT_SHIFT = Self::CTRL.bits | Self::ALT.bits | Self::SHIFT.bits;
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use super::{KeyCode as K, KeyEvent as E, Modifiers as M};
197
198    #[test]
199    fn new() {
200        assert_eq!(E::ESC, E::new('\x1b', M::NONE));
201    }
202
203    #[test]
204    #[cfg(unix)]
205    fn from() {
206        assert_eq!(E(K::Tab, M::NONE), E::from('\t'));
207    }
208
209    #[test]
210    #[cfg(windows)]
211    fn from() {
212        assert_eq!(E(K::Char('I'), M::CTRL), E::from('\t'));
213    }
214
215    #[test]
216    fn normalize() {
217        assert_eq!(E::ctrl('A'), E::normalize(E(K::Char('\x01'), M::NONE)));
218        assert_eq!(E::ctrl('A'), E::normalize(E::ctrl('a')));
219        assert_eq!(E::from('A'), E::normalize(E(K::Char('A'), M::SHIFT)));
220        assert_eq!(E(K::BackTab, M::NONE), E::normalize(E(K::Tab, M::SHIFT)));
221    }
222}