tui: implement escape sequence and key parsing without a Trie to save memory
3 files changed, 82 insertions(+), 61 deletions(-)

M cursive/src/keys.rs
M cursive/src/main.rs
M cursive/src/views/mod.rs
M cursive/src/keys.rs +59 -41
@@ 1,48 1,45 @@ 
-use {
-    cursive::event::{
-        Event,
-        Key
-    },
-    ted::trie::{
-        SequenceTrie,
-        TrieState
-    }
+use cursive::event::{
+    Event,
+    Key
 };
 
 /// Cursive does not parse escape sequences, so for example,
 /// `AltChar(':')` is never produced. Instead we receive a
 /// sequence of `Key(Esc)` followed by `Char(':')`.
-// XXX return a function that performs the mapping instead (lower memory overhead)
-pub fn parse_esc_seq() -> TrieState<Event, Event> {
-    let mut bindings = SequenceTrie::new();
+pub fn parse_esc_seq() -> impl FnMut(&Event) -> Option<Event> {
+    let mut esc = false;
 
-    // chars
-    for c in u8::min_value()..=u8::max_value() {
-        let c = c as char;
-        bindings.insert(&[Event::Key(Key::Esc), Event::Char(c)], Event::AltChar(c));
-    }
+    move |event| {
+        let result = if esc {
+            match event {
+                &Event::Char(char)    => Some(Event::AltChar(char)),
+                &Event::Key(Key::Esc) => Some(Event::Key(Key::Esc)),
+                &Event::Key(key)      => Some(Event::Alt(key)),
+                &Event::Shift(key)    => Some(Event::AltShift(key)),
+                _ => None
+            }
+        } else {
+            None
+        };
 
-    // keys
-    for key in &[ // all except escape
-        Key::Enter, Key::Backspace,
-        Key::Tab, /* Key::Esc */
-        Key::Left, Key::Right, Key::Up, Key::Down,
-        Key::Ins, Key::Del,
-        Key::Home, Key::End,
-        Key::PageUp, Key::PageDown,
-        Key::PauseBreak, Key::NumpadCenter,
-        Key::F0, Key::F1, Key::F2, Key::F3, Key::F4, Key::F5, Key::F6, Key::F7, Key::F8, Key::F9, Key::F10, Key::F11, Key::F12
-    ] {
-        bindings.insert(&[Event::Key(Key::Esc), Event::Key(*key)], Event::Alt(*key));
+        esc = !esc && *event == Event::Key(Key::Esc);
+
+        result
     }
+}
 
-    // ctrl
-    bindings.insert(&[Event::Unknown(vec![0; 4])], Event::CtrlChar(' '));
+pub fn translate() -> impl FnMut(Event) -> Option<Event> {
+    let mut esc = parse_esc_seq();
 
-    // esc
-    bindings.insert(&[Event::Key(Key::Esc), Event::Key(Key::Esc)], Event::Key(Key::Esc));
-
-    TrieState::new(bindings)
+    move |event| match esc(&event) {
+        Some(event) => Some(event),
+        // ignore if this is the first esc (escape esc itself)
+        None if event == Event::Key(Key::Esc) => None,
+        None => Some(match event {
+            Event::Unknown(ref bytes) if *bytes == [0; 4] => Event::CtrlChar(' '),
+            _ => event
+        })
+    }
 }
 
 #[cfg(test)]

          
@@ 51,14 48,35 @@ mod tests {
 
     #[test]
     fn parse_esc_seq() {
-        let mut trie = super::parse_esc_seq();
+        let mut esc = super::parse_esc_seq();
+
+        assert_eq!(esc(&Event::Key(Key::Esc)), None);
+        assert_eq!(esc(&Event::Char(':')), Some(Event::AltChar(':')));
 
-        assert_eq!(trie.next(&Event::Key(Key::Esc)), None);
-        assert_eq!(trie.next(&Event::Char(':')), Some(&Event::AltChar(':')));
+        assert_eq!(esc(&Event::Key(Key::Esc)), None);
+        assert_eq!(esc(&Event::Key(Key::Backspace)), Some(Event::Alt(Key::Backspace)));
+
+        assert_eq!(esc(&Event::Key(Key::Esc)), None);
+        assert_eq!(esc(&Event::Shift(Key::Backspace)), Some(Event::AltShift(Key::Backspace)));
+
+        assert_eq!(esc(&Event::Key(Key::Esc)), None);
+        assert_eq!(esc(&Event::Key(Key::Esc)), Some(Event::Key(Key::Esc)));
+        assert_eq!(esc(&Event::Char('a')), None);
 
-        assert_eq!(trie.next(&Event::Unknown(vec![0; 4])), Some(&Event::CtrlChar(' ')));
+        assert_eq!(esc(&Event::Char('a')), None);
+        assert_eq!(esc(&Event::Char('a')), None);
+    }
+
+    #[test]
+    fn translate() {
+        let mut trans = super::translate();
 
-        assert_eq!(trie.next(&Event::Key(Key::Esc)), None);
-        assert_eq!(trie.next(&Event::Key(Key::Backspace)), Some(&Event::Alt(Key::Backspace)));
+        assert_eq!(trans(Event::Key(Key::Esc)), None);
+        assert_eq!(trans(Event::Key(Key::Esc)), Some(Event::Key(Key::Esc)));
+
+        assert_eq!(trans(Event::Key(Key::Esc)), None);
+        assert_eq!(trans(Event::Char('a')), Some(Event::AltChar('a')));
+
+        assert_eq!(trans(Event::Unknown(vec![0; 4])), Some(Event::CtrlChar(' ')));
     }
 }

          
M cursive/src/main.rs +2 -20
@@ 7,7 7,6 @@ extern crate env_logger;
 
 use ted_tui::{
     bindings,
-    keys,
     cursive::{
         self,
         Cursive,

          
@@ 33,6 32,7 @@ use ted_tui::{
         }
     },
     views::{
+        self,
         EditorView,
         LineNumbers,
         Line

          
@@ 121,7 121,7 @@ fn main() {
         editor.build()
     };
 
-    siv.add_fullscreen_layer(parse_esc_seq_events(
+    siv.add_fullscreen_layer(views::translate_keys(
         visualize_keymap(
             layout_default(
                 editor,

          
@@ 134,24 134,6 @@ fn main() {
     siv.run();
 }
 
-fn parse_esc_seq_events<V: View>(view: V) -> Canvas<V> {
-    let mut keys = keys::parse_esc_seq();
-
-    Canvas::wrap(view).with_on_event(move |view, event| {
-        use cursive::event::{Event, EventResult, Key};
-
-        // translate key presses
-        let event = match keys.next(&event) {
-            Some(event) => (*event).clone(),
-            // ignore if this is the first esc (escape esc itself)
-            None if event == Event::Key(Key::Esc) => return EventResult::Ignored,
-            None => event
-        };
-
-        view.on_event(event)
-    })
-}
-
 fn layout_default<S>(editor: EditorView, gutter_style: S) -> Canvas<LinearLayout>
 where S: 'static + Into<Style> {
     let line_numbers = LineNumbers::new(

          
M cursive/src/views/mod.rs +21 -0
@@ 8,3 8,24 @@ pub mod line;
 pub use self::editor::EditorView;
 pub use self::gutter::LineNumbers;
 pub use self::line::Line;
+
+use {
+    keys,
+    cursive::{
+        view::View,
+        views::Canvas
+    }
+};
+
+pub fn translate_keys<V: View>(view: V) -> Canvas<V> {
+    let mut translate = keys::translate();
+
+    Canvas::wrap(view).with_on_event(move |view, event| {
+        use cursive::event::EventResult;
+
+        view.on_event(match translate(event) {
+            Some(event) => event,
+            None => return EventResult::Ignored
+        })
+    })
+}