rustyline/
binding.rs
1use crate::{
3 Cmd, EditMode, InputMode, InputState, KeyCode, KeyEvent, Modifiers, Refresher, RepeatCount,
4};
5
6use radix_trie::TrieKey;
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10#[cfg_attr(docsrs, doc(cfg(feature = "custom-bindings")))]
11pub enum Event {
12 Any,
15 KeySeq(Vec<KeyEvent>),
17 Mouse(),
19}
20
21impl Event {
22 pub(crate) fn normalize(mut self) -> Self {
24 if let Self::KeySeq(ref mut keys) = self {
25 for key in keys.iter_mut() {
26 *key = KeyEvent::normalize(*key);
27 }
28 }
29 self
30 }
31
32 #[must_use]
34 pub fn get(&self, i: usize) -> Option<&KeyEvent> {
35 if let Self::KeySeq(ref ks) = self {
36 ks.get(i)
37 } else {
38 None
39 }
40 }
41}
42
43impl From<KeyEvent> for Event {
44 fn from(k: KeyEvent) -> Self {
45 Self::KeySeq(vec![k])
46 }
47}
48
49const BASE: u32 = 0x0010_ffff + 1;
50const BASE_CONTROL: u32 = 0x0200_0000;
51const BASE_META: u32 = 0x0400_0000;
52const BASE_SHIFT: u32 = 0x0100_0000;
53const ESCAPE: u32 = 27;
54const PAGE_UP: u32 = BASE + 1;
55const PAGE_DOWN: u32 = PAGE_UP + 1;
56const DOWN: u32 = PAGE_DOWN + 1;
57const UP: u32 = DOWN + 1;
58const LEFT: u32 = UP + 1;
59const RIGHT: u32 = LEFT + 1;
60const HOME: u32 = RIGHT + 1;
61const END: u32 = HOME + 1;
62const DELETE: u32 = END + 1;
63const INSERT: u32 = DELETE + 1;
64const MOUSE: u32 = INSERT + 25;
66const PASTE_START: u32 = MOUSE + 1;
67const PASTE_FINISH: u32 = PASTE_START + 1;
68const ANY: u32 = PASTE_FINISH + 1;
69
70impl KeyEvent {
71 fn encode(&self) -> u32 {
72 let mut u = match self.0 {
73 KeyCode::UnknownEscSeq | KeyCode::Null => 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 + u32::from(i),
84 KeyCode::Esc => ESCAPE,
85 KeyCode::Home => HOME,
86 KeyCode::Insert => INSERT,
87 KeyCode::Left => LEFT,
88 KeyCode::PageDown => PAGE_DOWN,
89 KeyCode::PageUp => PAGE_UP,
90 KeyCode::Right => RIGHT,
91 KeyCode::Tab => u32::from('\t'),
92 KeyCode::Up => UP,
93 };
94 if self.1.contains(Modifiers::CTRL) {
95 u |= BASE_CONTROL;
96 }
97 if self.1.contains(Modifiers::ALT) {
98 u |= BASE_META;
99 }
100 if self.1.contains(Modifiers::SHIFT) {
101 u |= BASE_SHIFT;
102 }
103 u
104 }
105}
106
107impl TrieKey for Event {
108 fn encode_bytes(&self) -> Vec<u8> {
109 match self {
110 Self::Any => ANY.to_be_bytes().to_vec(),
111 Self::KeySeq(keys) => {
112 let mut dst = Vec::with_capacity(keys.len() * 4);
113 for key in keys {
114 dst.extend_from_slice(&key.encode().to_be_bytes());
115 }
116 dst
117 }
118 Self::Mouse() => MOUSE.to_be_bytes().to_vec(),
119 }
120 }
121}
122
123#[cfg_attr(docsrs, doc(cfg(feature = "custom-bindings")))]
125pub enum EventHandler {
126 Simple(Cmd),
128 Conditional(Box<dyn ConditionalEventHandler>),
130 }
133
134impl From<Cmd> for EventHandler {
135 fn from(c: Cmd) -> Self {
136 Self::Simple(c)
137 }
138}
139
140#[cfg_attr(docsrs, doc(cfg(feature = "custom-bindings")))]
142pub struct EventContext<'r> {
143 mode: EditMode,
144 input_mode: InputMode,
145 wrt: &'r dyn Refresher,
146}
147
148impl<'r> EventContext<'r> {
149 pub(crate) fn new(is: &InputState, wrt: &'r dyn Refresher) -> Self {
150 Self {
151 mode: is.mode,
152 input_mode: is.input_mode,
153 wrt,
154 }
155 }
156
157 #[must_use]
159 pub fn mode(&self) -> EditMode {
160 self.mode
161 }
162
163 #[must_use]
165 pub fn input_mode(&self) -> InputMode {
166 self.input_mode
167 }
168
169 #[must_use]
171 pub fn has_hint(&self) -> bool {
172 self.wrt.has_hint()
173 }
174
175 #[must_use]
177 pub fn hint_text(&self) -> Option<&str> {
178 self.wrt.hint_text()
179 }
180
181 #[must_use]
183 pub fn line(&self) -> &str {
184 self.wrt.line()
185 }
186
187 #[must_use]
189 pub fn pos(&self) -> usize {
190 self.wrt.pos()
191 }
192}
193
194#[cfg_attr(docsrs, doc(cfg(feature = "custom-bindings")))]
204pub trait ConditionalEventHandler: Send + Sync {
205 fn handle(
209 &self,
210 evt: &Event,
211 n: RepeatCount,
212 positive: bool,
213 ctx: &EventContext,
214 ) -> Option<Cmd>;
215}
216
217#[cfg(test)]
218mod test {
219 use super::{Event, EventHandler};
220 use crate::{Cmd, KeyCode, KeyEvent, Modifiers};
221 use radix_trie::Trie;
222
223 #[test]
224 fn encode() {
225 let mut trie = Trie::new();
226 let evt = Event::KeySeq(vec![KeyEvent::ctrl('X'), KeyEvent::ctrl('E')]);
227 trie.insert(evt.clone(), EventHandler::from(Cmd::Noop));
228 let prefix = Event::from(KeyEvent::ctrl('X'));
229 let subtrie = trie.get_raw_descendant(&prefix);
230 assert!(subtrie.is_some());
231 let subtrie = subtrie.unwrap();
232 let sub_result = subtrie.get(&evt);
233 assert!(sub_result.unwrap().is_some());
234 let prefix = Event::from(KeyEvent::ctrl('O'));
235 let subtrie = trie.get_raw_descendant(&prefix);
236 assert!(subtrie.is_none())
237 }
238
239 #[test]
240 fn no_collision() {
241 use {Event as E, EventHandler as H, KeyCode as C, KeyEvent as K, Modifiers as M};
242 let mut trie = Trie::new();
243 trie.insert(E::from(K(C::Backspace, M::NONE)), H::from(Cmd::Noop));
244 trie.insert(E::from(K(C::Enter, M::NONE)), H::from(Cmd::Noop));
245 trie.insert(E::from(K(C::Tab, M::NONE)), H::from(Cmd::Noop));
246 trie.insert(E::from(K(C::Backspace, M::CTRL)), H::from(Cmd::Noop));
247 trie.insert(E::from(K(C::Enter, M::CTRL)), H::from(Cmd::Noop));
248 trie.insert(E::from(K(C::Tab, M::CTRL)), H::from(Cmd::Noop));
249 }
250
251 #[test]
252 #[ignore]
253 #[cfg(target_arch = "x86_64")]
254 fn size_of_event() {
255 use core::mem::size_of;
256 assert_eq!(size_of::<Event>(), 32);
257 }
258}