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