Skip to main content

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}