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