afrim_preprocessor/lib.rs
1#![deny(missing_docs)]
2//! Preprocess keyboard events for an input method.
3//!
4//! Enables the generation of keyboard event responses from a keyboard input event in an input method
5//! engine.
6//! The `afrim-preprocessor` crate is built on the top of the [`afrim-memory`](afrim_memory) crate.
7//!
8//! # Example
9//!
10//! ```
11//! use afrim_preprocessor::{utils::{self, webdriver}, Command, Preprocessor, Node};
12//! use std::{collections::VecDeque, rc::Rc};
13//!
14//! // Prepares the memory.
15//! let data = utils::load_data("cc ç");
16//! let text_buffer: Node = utils::build_map(data);
17//! let memory = Rc::new(text_buffer);
18//!
19//! // Builds the preprocessor.
20//! let mut preprocessor = Preprocessor::new(memory, 8);
21//!
22//! // Process an input.
23//! let input = "cc";
24//! webdriver::send_keys(input)
25//! .into_iter()
26//! .for_each(|event| {
27//! match event {
28//! // Triggers the generated keyboard input event.
29//! webdriver::Event::Keyboard(event) => preprocessor.process(event),
30//! _ => unimplemented!(),
31//! };
32//! });
33//!
34//! // Now let's look at the generated commands.
35//! let mut expecteds = VecDeque::from(vec![
36//! Command::Pause,
37//! Command::Delete("c".to_string()),
38//! Command::Delete("c".to_string()),
39//! Command::CommitText("ç".to_owned()),
40//! Command::Resume,
41//! ]);
42//!
43//! // Verification.
44//! while let Some(command) = preprocessor.pop_queue() {
45//! assert_eq!(command, expecteds.pop_front().unwrap());
46//! }
47//! assert_eq!(expecteds.is_empty(), true);
48//! ```
49
50mod message;
51
52pub use crate::message::Command;
53use afrim_memory::Cursor;
54pub use afrim_memory::Node;
55pub use keyboard_types::{Key, KeyState, KeyboardEvent, NamedKey};
56use std::{collections::VecDeque, rc::Rc};
57
58/// Utilities.
59pub mod utils {
60 pub use afrim_memory::utils::{build_map, load_data};
61 pub use keyboard_types::webdriver;
62}
63
64/// The main structure of the preprocessor.
65#[derive(Debug)]
66pub struct Preprocessor {
67 cursor: Cursor,
68 queue: VecDeque<Command>,
69}
70
71impl Preprocessor {
72 /// Initializes a new preprocessor.
73 ///
74 /// The preprocessor needs a memory to operate. You have two options to build this memory.
75 /// - Use the [`afrim-memory`] crate.
76 /// - Use the [`utils`] module.
77 ///
78 /// It also needs you set the capacity of his cursor. We recommend to set a capacity equal
79 /// or greater than N times the maximun sequence length that you want to handle.
80 /// Where N is the number of sequences that you want track in the cursor.
81 ///
82 /// Note that the cursor is the internal memory of the `afrim_preprocessor`.
83 ///
84 /// # Example
85 ///
86 /// ```
87 /// use afrim_preprocessor::{Preprocessor, utils};
88 /// use std::rc::Rc;
89 ///
90 /// // We prepare the memory.
91 /// let data = utils::load_data("uuaf3 ʉ̄ɑ̄");
92 /// let text_buffer = utils::build_map(data);
93 /// let memory = Rc::new(text_buffer);
94 ///
95 /// // We initialize our preprocessor.
96 /// let preprocessor = Preprocessor::new(memory, 8);
97 /// ```
98 pub fn new(memory: Rc<Node>, buffer_size: usize) -> Self {
99 let cursor = Cursor::new(memory, buffer_size);
100 let queue = VecDeque::with_capacity(15);
101
102 Self { cursor, queue }
103 }
104
105 // Cancel the previous operation.
106 fn rollback(&mut self) -> bool {
107 let (_, _, _curr_char) = self.cursor.state();
108
109 if let Some(out) = self.cursor.undo() {
110 self.queue.push_back(Command::Delete(out));
111
112 // Clear the remaining code
113 while let (None, 1.., ..) = self.cursor.state() {
114 self.cursor.undo();
115 }
116
117 if let (Some(_in), ..) = self.cursor.state() {
118 self.queue.push_back(Command::CommitText(_in));
119 }
120
121 true
122 } else {
123 self.queue
124 .push_back(Command::Delete(_curr_char.to_string()));
125 self.cursor.resume();
126
127 false
128 }
129 }
130
131 /// Preprocess the keyboard input event and returns infos on his internal changes (change on
132 /// the cursor and/or something to commit).
133 ///
134 /// It's useful when you process keyboard input events in bulk. Whether there is something that
135 /// you want to do based on this information, you can decide how to continue.
136 ///
137 /// # Example
138 ///
139 /// ```
140 /// use afrim_preprocessor::{Command, Preprocessor, utils};
141 /// use keyboard_types::{Key::Character, KeyboardEvent};
142 /// use std::{collections::VecDeque, rc::Rc};
143 ///
144 /// // We prepare the memory.
145 /// let data = utils::load_data("i3 ī");
146 /// let text_buffer = utils::build_map(data);
147 /// let memory = Rc::new(text_buffer);
148 ///
149 /// let mut preprocessor = Preprocessor::new(memory, 8);
150 ///
151 /// // We process the input.
152 /// // let input = "si3";
153 ///
154 /// let info = preprocessor.process(KeyboardEvent {
155 /// key: Character("s".to_string()),
156 /// ..Default::default()
157 /// });
158 /// assert_eq!(info, (true, false));
159 ///
160 /// let info = preprocessor.process(KeyboardEvent {
161 /// key: Character("i".to_string()),
162 /// ..Default::default()
163 /// });
164 /// assert_eq!(info, (true, false));
165 ///
166 /// let info = preprocessor.process(KeyboardEvent {
167 /// key: Character("3".to_string()),
168 /// ..Default::default()
169 /// });
170 /// assert_eq!(info, (true, true));
171 ///
172 /// // The input inside the preprocessor.
173 /// assert_eq!(preprocessor.get_input(), "si3".to_owned());
174 ///
175 /// // The generated commands.
176 /// let mut expecteds = VecDeque::from(vec![
177 /// Command::Pause,
178 /// Command::Delete("3".to_string()),
179 /// Command::Delete("i".to_string()),
180 /// Command::CommitText("ī".to_owned()),
181 /// Command::Resume,
182 /// ]);
183 ///
184 /// // Verification.
185 /// while let Some(command) = preprocessor.pop_queue() {
186 /// assert_eq!(command, expecteds.pop_front().unwrap());
187 /// }
188 /// assert_eq!(expecteds.is_empty(), true);
189 /// ```
190 pub fn process(&mut self, event: KeyboardEvent) -> (bool, bool) {
191 let (mut changed, mut committed) = (false, false);
192
193 match (event.state, event.key) {
194 (KeyState::Down, Key::Named(NamedKey::Backspace)) => {
195 {
196 self.pause();
197 committed = self.rollback();
198 self.resume();
199 }
200 changed = true;
201 }
202 (KeyState::Down, Key::Character(character))
203 if character
204 .chars()
205 .next()
206 .map(|e| e.is_alphanumeric() || e.is_ascii_punctuation())
207 .unwrap_or(false) =>
208 {
209 let character = character.chars().next().unwrap();
210
211 if let Some(_in) = self.cursor.hit(character) {
212 self.pause();
213 let mut prev_cursor = self.cursor.clone();
214 prev_cursor.undo();
215 self.queue.push_back(Command::Delete(character.to_string()));
216
217 // Remove the remaining code
218 while let (None, 1.., _c) = prev_cursor.state() {
219 prev_cursor.undo();
220 self.queue.push_back(Command::Delete(_c.to_string()));
221 }
222
223 if let (Some(out), ..) = prev_cursor.state() {
224 self.queue.push_back(Command::Delete(out))
225 }
226
227 self.queue.push_back(Command::CommitText(_in));
228 self.resume();
229 committed = true;
230 };
231
232 changed = true;
233 }
234 (KeyState::Down, Key::Named(NamedKey::Shift) | Key::Named(NamedKey::CapsLock)) => (),
235 (KeyState::Down, _) => {
236 self.cursor.clear();
237 changed = true;
238 }
239 _ => (),
240 };
241
242 (changed, committed)
243 }
244
245 /// Commit a text.
246 ///
247 /// Generate a command to ensure the commitment of this text.
248 /// Useful when you want deal with auto-completion.
249 ///
250 /// **Note**: Before any commitment, the preprocessor make sure to discard the current input.
251 ///
252 /// # Example
253 ///
254 /// ```
255 /// use afrim_preprocessor::{Command, Preprocessor, utils, KeyboardEvent, Key::Character};
256 /// use std::{collections::VecDeque, rc::Rc};
257 ///
258 /// // We prepare the memory.
259 /// let data = utils::load_data("i3 ī");
260 /// let text_buffer = utils::build_map(data);
261 /// let memory = Rc::new(text_buffer);
262 ///
263 /// let mut preprocessor = Preprocessor::new(memory, 8);
264 ///
265 /// // We process the input.
266 /// // let input = "si3";
267 /// preprocessor.process(KeyboardEvent {
268 /// key: Character("s".to_string()),
269 /// ..Default::default()
270 /// });
271 ///
272 /// preprocessor.commit("sī".to_owned());
273 ///
274 /// // The generated commands.
275 /// let mut expecteds = VecDeque::from(vec![
276 /// Command::Pause,
277 /// Command::Delete("s".to_string()),
278 /// Command::CommitText("sī".to_owned()),
279 /// Command::Resume,
280 /// ]);
281 ///
282 /// // Verification.
283 /// while let Some(command) = preprocessor.pop_queue() {
284 /// assert_eq!(command, expecteds.pop_front().unwrap());
285 /// }
286 /// assert_eq!(expecteds.is_empty(), true);
287 /// ```
288 pub fn commit(&mut self, text: String) {
289 self.pause();
290
291 while !self.cursor.is_empty() {
292 self.rollback();
293 }
294 self.queue.push_back(Command::CommitText(text));
295 self.resume();
296 // We clear the buffer
297 self.cursor.clear();
298 }
299
300 // Pauses the keyboard event listerner.
301 fn pause(&mut self) {
302 self.queue.push_back(Command::Pause);
303 }
304
305 // Resumes the keyboard event listener.
306 fn resume(&mut self) {
307 self.queue.push_back(Command::Resume);
308 }
309
310 /// Returns the input present in the internal memory.
311 ///
312 /// It's always useful to know what is inside the memory of the preprocessor for debugging.
313 /// **Note**: The input inside the preprocessor is not always the same than the original because
314 /// of the limited capacity of his internal cursor.
315 ///
316 /// # Example
317 ///
318 /// ```
319 /// use afrim_preprocessor::{Command, Preprocessor, utils::{self, webdriver}};
320 /// use std::{collections::VecDeque, rc::Rc};
321 ///
322 /// // We prepare the memory.
323 /// let data = utils::load_data("i3 ī");
324 /// let text_buffer = utils::build_map(data);
325 /// let memory = Rc::new(text_buffer);
326 ///
327 /// let mut preprocessor = Preprocessor::new(memory, 4);
328 ///
329 /// // We process the input.
330 /// let input = "si3";
331 /// webdriver::send_keys(input)
332 /// .into_iter()
333 /// .for_each(|event| {
334 /// match event {
335 /// // Triggers the generated keyboard input event.
336 /// webdriver::Event::Keyboard(event) => preprocessor.process(event),
337 /// _ => unimplemented!(),
338 /// };
339 /// });
340 ///
341 /// // The input inside the processor.
342 /// assert_eq!(preprocessor.get_input(), "si3".to_owned());
343 pub fn get_input(&self) -> String {
344 self.cursor
345 .to_sequence()
346 .into_iter()
347 .filter(|c| *c != '\0')
348 .collect::<String>()
349 }
350
351 /// Returns the next command to be executed.
352 ///
353 /// The next command is dropped from the queue and can't be returned anymore.
354 ///
355 /// # Example
356 ///
357 /// ```
358 /// use afrim_preprocessor::{Command, Preprocessor, utils};
359 /// use std::{collections::VecDeque, rc::Rc};
360 ///
361 /// // We prepare the memory.
362 /// let text_buffer = utils::build_map(vec![]);
363 /// let memory = Rc::new(text_buffer);
364 ///
365 /// let mut preprocessor = Preprocessor::new(memory, 8);
366 /// preprocessor.commit("hello".to_owned());
367 ///
368 /// // The expected results.
369 /// let mut expecteds = VecDeque::from(vec![
370 /// Command::Pause,
371 /// Command::CommitText("hello".to_owned()),
372 /// Command::Resume,
373 /// ]);
374 ///
375 /// // Verification.
376 /// while let Some(command) = preprocessor.pop_queue() {
377 /// assert_eq!(command, expecteds.pop_front().unwrap());
378 /// }
379 /// assert_eq!(expecteds.is_empty(), true);
380 pub fn pop_queue(&mut self) -> Option<Command> {
381 self.queue.pop_front()
382 }
383
384 /// Clears the queue.
385 ///
386 /// # Example
387 ///
388 /// ```
389 /// use afrim_preprocessor::{Preprocessor, utils};
390 /// use std::rc::Rc;
391 ///
392 /// let data =
393 /// utils::load_data("n* ŋ");
394 /// let text_buffer = utils::build_map(data);
395 /// let memory = Rc::new(text_buffer);
396 ///
397 /// let mut preprocessor = Preprocessor::new(memory, 8);
398 /// preprocessor.commit("hi".to_owned());
399 /// preprocessor.clear_queue();
400 ///
401 /// assert_eq!(preprocessor.pop_queue(), None);
402 /// ```
403 pub fn clear_queue(&mut self) {
404 self.queue.clear();
405 }
406}
407
408#[cfg(test)]
409mod tests {
410 use crate::message::Command;
411 use crate::Preprocessor;
412 use crate::{
413 utils::{self, webdriver},
414 Key::{Character, Named},
415 KeyboardEvent, NamedKey, Node,
416 };
417 use std::collections::VecDeque;
418
419 #[test]
420 fn test_process() {
421 use std::rc::Rc;
422
423 let data = utils::load_data("ccced ç\ncc ç");
424 let memory = utils::build_map(data);
425 let mut preprocessor = Preprocessor::new(Rc::new(memory), 8);
426 webdriver::send_keys("ccced").into_iter().for_each(|e| {
427 match e {
428 webdriver::Event::Keyboard(e) => preprocessor.process(e),
429 _ => unimplemented!(),
430 };
431 });
432 let mut expecteds = VecDeque::from(vec![
433 // c c
434 Command::Pause,
435 Command::Delete("c".to_string()),
436 Command::Delete("c".to_string()),
437 Command::CommitText("ç".to_owned()),
438 Command::Resume,
439 // c e d
440 Command::Pause,
441 Command::Delete("d".to_string()),
442 Command::Delete("e".to_string()),
443 Command::Delete("c".to_string()),
444 Command::Delete("ç".to_string()),
445 Command::CommitText("ç".to_owned()),
446 Command::Resume,
447 ]);
448
449 while let Some(command) = preprocessor.pop_queue() {
450 assert_eq!(command, expecteds.pop_front().unwrap());
451 }
452 assert!(expecteds.is_empty());
453 }
454
455 #[test]
456 fn test_commit() {
457 let mut preprocessor = Preprocessor::new(Node::default().into(), 8);
458 preprocessor.process(KeyboardEvent {
459 key: Character("a".to_owned()),
460 ..Default::default()
461 });
462 preprocessor.commit("word".to_owned());
463
464 let mut expecteds = VecDeque::from(vec![
465 Command::Pause,
466 Command::Delete("a".to_string()),
467 Command::CommitText("word".to_owned()),
468 Command::Resume,
469 ]);
470
471 while let Some(command) = preprocessor.pop_queue() {
472 assert_eq!(command, expecteds.pop_front().unwrap());
473 }
474 assert!(expecteds.is_empty());
475 }
476
477 #[test]
478 fn test_rollback() {
479 use std::rc::Rc;
480
481 let data = utils::load_data("ccced ç\ncc ç");
482 let memory = utils::build_map(data);
483 let mut preprocessor = Preprocessor::new(Rc::new(memory), 8);
484 let backspace_event = KeyboardEvent {
485 key: Named(NamedKey::Backspace),
486 ..Default::default()
487 };
488
489 webdriver::send_keys("ccced").into_iter().for_each(|e| {
490 match e {
491 webdriver::Event::Keyboard(e) => preprocessor.process(e),
492 _ => unimplemented!(),
493 };
494 });
495
496 preprocessor.clear_queue();
497 assert_eq!(preprocessor.get_input(), "ccced".to_owned());
498 preprocessor.process(backspace_event.clone());
499 assert_eq!(preprocessor.get_input(), "cc".to_owned());
500 preprocessor.process(backspace_event);
501 assert_eq!(preprocessor.get_input(), "".to_owned());
502
503 let mut expecteds = VecDeque::from(vec![
504 Command::Pause,
505 Command::Delete("ç".to_string()),
506 Command::CommitText("ç".to_owned()),
507 Command::Resume,
508 Command::Pause,
509 Command::Delete("ç".to_string()),
510 Command::Resume,
511 ]);
512
513 while let Some(command) = preprocessor.pop_queue() {
514 assert_eq!(command, expecteds.pop_front().unwrap());
515 }
516 assert!(expecteds.is_empty());
517 }
518
519 #[test]
520 fn test_advanced() {
521 use std::rc::Rc;
522
523 let data = include_str!("../data/sample.txt");
524 let data = utils::load_data(data);
525 let memory = utils::build_map(data);
526 let mut preprocessor = Preprocessor::new(Rc::new(memory), 64);
527
528 webdriver::send_keys(
529 "u\u{E003}uu\u{E003}uc_ceduuaf3afafaff3uu3\
530 \u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}"
531 ).into_iter().for_each(|e| {
532 match e {
533 webdriver::Event::Keyboard(e) => preprocessor.process(e),
534 _ => unimplemented!(),
535 };
536 });
537
538 let mut expecteds = VecDeque::from(vec![
539 // Process
540 // u backspace
541 Command::Pause,
542 Command::Delete("u".to_string()),
543 Command::Resume,
544 // u u backspace
545 Command::Pause,
546 Command::Delete("u".to_string()),
547 Command::Delete("u".to_string()),
548 Command::CommitText("ʉ".to_owned()),
549 Command::Resume,
550 Command::Pause,
551 Command::Delete("ʉ".to_string()),
552 Command::Resume,
553 // u
554 // NOP
555 // c _
556 Command::Pause,
557 Command::Delete("_".to_string()),
558 Command::Delete("c".to_string()),
559 Command::CommitText("ç".to_owned()),
560 Command::Resume,
561 // c e d
562 Command::Pause,
563 Command::Delete("d".to_string()),
564 Command::Delete("e".to_string()),
565 Command::Delete("c".to_string()),
566 Command::Delete("ç".to_string()),
567 Command::CommitText("ç".to_owned()),
568 Command::Resume,
569 // u u
570 Command::Pause,
571 Command::Delete("u".to_string()),
572 Command::Delete("u".to_string()),
573 Command::CommitText("ʉ".to_owned()),
574 Command::Resume,
575 // a f 3
576 Command::Pause,
577 Command::Delete("3".to_string()),
578 Command::Delete("f".to_string()),
579 Command::Delete("a".to_string()),
580 Command::Delete("ʉ".to_string()),
581 Command::CommitText("ʉ\u{304}ɑ\u{304}".to_owned()),
582 Command::Resume,
583 // a f
584 Command::Pause,
585 Command::Delete("f".to_string()),
586 Command::Delete("a".to_string()),
587 Command::CommitText("ɑ".to_owned()),
588 Command::Resume,
589 // a f
590 Command::Pause,
591 Command::Delete("f".to_string()),
592 Command::Delete("a".to_string()),
593 Command::CommitText("ɑ".to_owned()),
594 Command::Resume,
595 // a f
596 Command::Pause,
597 Command::Delete("f".to_string()),
598 Command::Delete("a".to_string()),
599 Command::CommitText("ɑ".to_owned()),
600 Command::Resume,
601 // f
602 Command::Pause,
603 Command::Delete("f".to_string()),
604 Command::Delete("ɑ".to_string()),
605 Command::CommitText("ɑɑ".to_owned()),
606 Command::Resume,
607 // 3
608 Command::Pause,
609 Command::Delete("3".to_string()),
610 Command::Delete("ɑɑ".to_string()),
611 Command::CommitText("ɑ\u{304}ɑ\u{304}".to_owned()),
612 Command::Resume,
613 // uu
614 Command::Pause,
615 Command::Delete("u".to_string()),
616 Command::Delete("u".to_string()),
617 Command::CommitText("ʉ".to_owned()),
618 Command::Resume,
619 // 3
620 Command::Pause,
621 Command::Delete("3".to_string()),
622 Command::Delete("ʉ".to_string()),
623 Command::CommitText("ʉ\u{304}".to_owned()),
624 Command::Resume,
625 // Rollback
626 Command::Pause,
627 Command::Delete("ʉ\u{304}".to_string()),
628 Command::CommitText("ʉ".to_owned()),
629 Command::Resume,
630 Command::Pause,
631 Command::Delete("ʉ".to_string()),
632 Command::Resume,
633 Command::Pause,
634 Command::Delete("ɑ\u{304}ɑ\u{304}".to_string()),
635 Command::CommitText("ɑɑ".to_owned()),
636 Command::Resume,
637 Command::Pause,
638 Command::Delete("ɑɑ".to_string()),
639 Command::CommitText("ɑ".to_owned()),
640 Command::Resume,
641 Command::Pause,
642 Command::Delete("ɑ".to_string()),
643 Command::Resume,
644 Command::Pause,
645 Command::Delete("ɑ".to_string()),
646 Command::Resume,
647 Command::Pause,
648 Command::Delete("ɑ".to_string()),
649 Command::Resume,
650 Command::Pause,
651 Command::Delete("ʉ\u{304}ɑ\u{304}".to_string()),
652 Command::CommitText("ʉ".to_owned()),
653 Command::Resume,
654 Command::Pause,
655 Command::Delete("ʉ".to_string()),
656 Command::Resume,
657 Command::Pause,
658 Command::Delete("ç".to_string()),
659 Command::CommitText("ç".to_owned()),
660 Command::Resume,
661 Command::Pause,
662 Command::Delete("ç".to_string()),
663 Command::Resume,
664 // yes, the buffer is empty, but we want leave the user use the backspace.
665 Command::Pause,
666 Command::Delete("\0".to_string()),
667 Command::Resume,
668 ]);
669
670 while let Some(command) = preprocessor.pop_queue() {
671 let c = expecteds.pop_front().unwrap();
672 assert_eq!(command, c);
673 }
674 assert!(expecteds.is_empty());
675 }
676}