rustyline/
binding.rs

1/// Custom event handlers
2use crate::{
3    Cmd, EditMode, InputMode, InputState, KeyCode, KeyEvent, Modifiers, Refresher, RepeatCount,
4};
5
6use radix_trie::TrieKey;
7
8/// Input event
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10pub enum Event {
11    /// Wildcard.
12    /// Useful if you want to filter out some keys.
13    Any,
14    /// Key sequence
15    KeySeq(Vec<KeyEvent>),
16    /// TODO Mouse event
17    Mouse(),
18}
19
20impl Event {
21    /// See [`KeyEvent::normalize`]
22    pub(crate) fn normalize(mut self) -> Self {
23        if let Self::KeySeq(ref mut keys) = self {
24            for key in keys.iter_mut() {
25                *key = KeyEvent::normalize(*key);
26            }
27        }
28        self
29    }
30
31    /// Return `i`th key event
32    #[must_use]
33    pub fn get(&self, i: usize) -> Option<&KeyEvent> {
34        if let Self::KeySeq(ref ks) = self {
35            ks.get(i)
36        } else {
37            None
38        }
39    }
40}
41
42impl From<KeyEvent> for Event {
43    fn from(k: KeyEvent) -> Self {
44        Self::KeySeq(vec![k])
45    }
46}
47
48const BASE: u32 = 0x0010_ffff + 1;
49const BASE_CONTROL: u32 = 0x0200_0000;
50const BASE_META: u32 = 0x0400_0000;
51const BASE_SHIFT: u32 = 0x0100_0000;
52const ESCAPE: u32 = 27;
53const PAGE_UP: u32 = BASE + 1;
54const PAGE_DOWN: u32 = PAGE_UP + 1;
55const DOWN: u32 = PAGE_DOWN + 1;
56const UP: u32 = DOWN + 1;
57const LEFT: u32 = UP + 1;
58const RIGHT: u32 = LEFT + 1;
59const HOME: u32 = RIGHT + 1;
60const END: u32 = HOME + 1;
61const DELETE: u32 = END + 1;
62const INSERT: u32 = DELETE + 1;
63//const F1: u32 = INSERT + 1;
64const MOUSE: u32 = /* F24 + 1 */ INSERT + 25;
65const PASTE_START: u32 = MOUSE + 1;
66const PASTE_FINISH: u32 = PASTE_START + 1;
67const ANY: u32 = PASTE_FINISH + 1;
68
69impl KeyEvent {
70    fn encode(&self) -> u32 {
71        let mut u = match self.0 {
72            KeyCode::UnknownEscSeq | KeyCode::Null => 0,
73            KeyCode::Backspace => u32::from('\x7f'),
74            KeyCode::BackTab => u32::from('\t') | BASE_SHIFT,
75            KeyCode::BracketedPasteStart => PASTE_START,
76            KeyCode::BracketedPasteEnd => PASTE_FINISH,
77            KeyCode::Char(c) => u32::from(c),
78            KeyCode::Delete => DELETE,
79            KeyCode::Down => DOWN,
80            KeyCode::End => END,
81            KeyCode::Enter => u32::from('\r'),
82            KeyCode::F(i) => INSERT + u32::from(i),
83            KeyCode::Esc => ESCAPE,
84            KeyCode::Home => HOME,
85            KeyCode::Insert => INSERT,
86            KeyCode::Left => LEFT,
87            KeyCode::PageDown => PAGE_DOWN,
88            KeyCode::PageUp => PAGE_UP,
89            KeyCode::Right => RIGHT,
90            KeyCode::Tab => u32::from('\t'),
91            KeyCode::Up => UP,
92        };
93        if self.1.contains(Modifiers::CTRL) {
94            u |= BASE_CONTROL;
95        }
96        if self.1.contains(Modifiers::ALT) {
97            u |= BASE_META;
98        }
99        if self.1.contains(Modifiers::SHIFT) {
100            u |= BASE_SHIFT;
101        }
102        u
103    }
104}
105
106impl TrieKey for Event {
107    fn encode_bytes(&self) -> Vec<u8> {
108        match self {
109            Self::Any => ANY.to_be_bytes().to_vec(),
110            Self::KeySeq(keys) => {
111                let mut dst = Vec::with_capacity(keys.len() * 4);
112                for key in keys {
113                    dst.extend_from_slice(&key.encode().to_be_bytes());
114                }
115                dst
116            }
117            Self::Mouse() => MOUSE.to_be_bytes().to_vec(),
118        }
119    }
120}
121
122/// Event handler
123pub enum EventHandler {
124    /// unconditional command
125    Simple(Cmd),
126    /// handler behaviour depends on input state
127    Conditional(Box<dyn ConditionalEventHandler>),
128    /* invoke multiple actions
129     * TODO Macro(), */
130}
131
132impl From<Cmd> for EventHandler {
133    fn from(c: Cmd) -> Self {
134        Self::Simple(c)
135    }
136}
137
138/// Give access to user input.
139pub struct EventContext<'r> {
140    mode: EditMode,
141    input_mode: InputMode,
142    wrt: &'r dyn Refresher,
143}
144
145impl<'r> EventContext<'r> {
146    pub(crate) fn new(is: &InputState, wrt: &'r dyn Refresher) -> Self {
147        Self {
148            mode: is.mode,
149            input_mode: is.input_mode,
150            wrt,
151        }
152    }
153
154    /// emacs or vi mode
155    #[must_use]
156    pub fn mode(&self) -> EditMode {
157        self.mode
158    }
159
160    /// vi input mode
161    #[must_use]
162    pub fn input_mode(&self) -> InputMode {
163        self.input_mode
164    }
165
166    /// Returns `true` if there is a hint displayed.
167    #[must_use]
168    pub fn has_hint(&self) -> bool {
169        self.wrt.has_hint()
170    }
171
172    /// Returns the hint text that is shown after the current cursor position.
173    #[must_use]
174    pub fn hint_text(&self) -> Option<&str> {
175        self.wrt.hint_text()
176    }
177
178    /// currently edited line
179    #[must_use]
180    pub fn line(&self) -> &str {
181        self.wrt.line()
182    }
183
184    /// Current cursor position (byte position)
185    #[must_use]
186    pub fn pos(&self) -> usize {
187        self.wrt.pos()
188    }
189}
190
191/// May behave differently depending on:
192///  * edit mode (emacs vs vi)
193///  * vi input mode (insert vs replace vs command modes)
194///  * empty line
195///  * cursor position
196///  * repeat count
197///  * original key pressed (when same command is bound to different key)
198///  * hint
199///  * ...
200pub trait ConditionalEventHandler: Send + Sync {
201    /// Takes the current input state and
202    /// returns the command to be performed or `None` to perform the default
203    /// one.
204    fn handle(
205        &self,
206        evt: &Event,
207        n: RepeatCount,
208        positive: bool,
209        ctx: &EventContext,
210    ) -> Option<Cmd>;
211}
212
213#[cfg(test)]
214mod test {
215    use super::{Event, EventHandler};
216    use crate::{Cmd, KeyCode, KeyEvent, Modifiers};
217    use radix_trie::Trie;
218
219    #[test]
220    fn encode() {
221        let mut trie = Trie::new();
222        let evt = Event::KeySeq(vec![KeyEvent::ctrl('X'), KeyEvent::ctrl('E')]);
223        trie.insert(evt.clone(), EventHandler::from(Cmd::Noop));
224        let prefix = Event::from(KeyEvent::ctrl('X'));
225        let subtrie = trie.get_raw_descendant(&prefix);
226        assert!(subtrie.is_some());
227        let subtrie = subtrie.unwrap();
228        let sub_result = subtrie.get(&evt);
229        assert!(sub_result.unwrap().is_some());
230        let prefix = Event::from(KeyEvent::ctrl('O'));
231        let subtrie = trie.get_raw_descendant(&prefix);
232        assert!(subtrie.is_none())
233    }
234
235    #[test]
236    fn no_collision() {
237        use {Event as E, EventHandler as H, KeyCode as C, KeyEvent as K, Modifiers as M};
238        let mut trie = Trie::new();
239        trie.insert(E::from(K(C::Backspace, M::NONE)), H::from(Cmd::Noop));
240        trie.insert(E::from(K(C::Enter, M::NONE)), H::from(Cmd::Noop));
241        trie.insert(E::from(K(C::Tab, M::NONE)), H::from(Cmd::Noop));
242        trie.insert(E::from(K(C::Backspace, M::CTRL)), H::from(Cmd::Noop));
243        trie.insert(E::from(K(C::Enter, M::CTRL)), H::from(Cmd::Noop));
244        trie.insert(E::from(K(C::Tab, M::CTRL)), H::from(Cmd::Noop));
245    }
246
247    #[test]
248    #[ignore]
249    #[cfg(target_arch = "x86_64")]
250    fn size_of_event() {
251        use core::mem::size_of;
252        assert_eq!(size_of::<Event>(), 32);
253    }
254}