liner/keymap/
mod.rs

1use crate::complete::Completer;
2use crate::event::*;
3use crate::Editor;
4use std::io::{self, ErrorKind, Write};
5use termion::event::Key;
6
7pub trait KeyMap: Default {
8    fn handle_key_core<W: Write>(&mut self, key: Key, editor: &mut Editor<'_, W>)
9        -> io::Result<()>;
10
11    fn init<W: Write>(&mut self, _editor: &mut Editor<'_, W>) {}
12
13    fn handle_key<W: Write, C: Completer>(
14        &mut self,
15        mut key: Key,
16        editor: &mut Editor<'_, W>,
17        handler: &mut C,
18    ) -> io::Result<bool> {
19        let mut done = false;
20
21        handler.on_event(Event::new(editor, EventKind::BeforeKey(key)));
22
23        let is_empty = editor.current_buffer().is_empty();
24
25        if key == Key::Ctrl('h') {
26            // XXX: Might need to change this when remappable keybindings are added.
27            key = Key::Backspace;
28        }
29
30        match key {
31            Key::Ctrl('c') => {
32                editor.handle_newline()?;
33                return Err(io::Error::new(ErrorKind::Interrupted, "ctrl-c"));
34            }
35            // if the current buffer is empty, treat ctrl-d as eof
36            Key::Ctrl('d') if is_empty => {
37                editor.handle_newline()?;
38                return Err(io::Error::new(ErrorKind::UnexpectedEof, "ctrl-d"));
39            }
40            Key::Char('\t') => editor.complete(handler)?,
41            Key::Char('\n') => {
42                done = editor.handle_newline()?;
43            }
44            Key::Ctrl('f') if editor.is_currently_showing_autosuggestion() => {
45                editor.accept_autosuggestion()?;
46            }
47            Key::Ctrl('r') => {
48                editor.search(false)?;
49            }
50            Key::Ctrl('s') => {
51                editor.search(true)?;
52            }
53            Key::Right
54                if editor.is_currently_showing_autosuggestion()
55                    && editor.cursor_is_at_end_of_line() =>
56            {
57                editor.accept_autosuggestion()?;
58            }
59            _ => {
60                self.handle_key_core(key, editor)?;
61                editor.skip_completions_hint();
62            }
63        };
64
65        handler.on_event(Event::new(editor, EventKind::AfterKey(key)));
66
67        editor.flush()?;
68
69        Ok(done)
70    }
71}
72
73pub mod vi;
74pub use vi::Vi;
75
76pub mod emacs;
77pub use emacs::Emacs;
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::editor::Prompt;
83    use crate::Context;
84    use std::io::ErrorKind;
85    use termion::event::Key::*;
86
87    #[derive(Default)]
88    struct TestKeyMap;
89
90    impl KeyMap for TestKeyMap {
91        fn handle_key_core<W: Write>(&mut self, _: Key, _: &mut Editor<'_, W>) -> io::Result<()> {
92            Ok(())
93        }
94    }
95
96    struct EmptyCompleter;
97
98    impl Completer for EmptyCompleter {
99        fn completions(&mut self, _start: &str) -> Vec<String> {
100            Vec::default()
101        }
102    }
103
104    #[test]
105    /// when the current buffer is empty, ctrl-d generates and eof error
106    fn ctrl_d_empty() {
107        let mut context = Context::new();
108        let out = Vec::new();
109        let mut ed = Editor::new(out, Prompt::from("prompt"), None, &mut context).unwrap();
110        let mut map = TestKeyMap;
111
112        let res = map.handle_key(Ctrl('d'), &mut ed, &mut EmptyCompleter);
113        assert_eq!(res.is_err(), true);
114        assert_eq!(res.err().unwrap().kind(), ErrorKind::UnexpectedEof);
115    }
116
117    #[test]
118    /// when the current buffer is not empty, ctrl-d should be ignored
119    fn ctrl_d_non_empty() {
120        let mut context = Context::new();
121        let out = Vec::new();
122        let mut ed = Editor::new(out, Prompt::from("prompt"), None, &mut context).unwrap();
123        let mut map = TestKeyMap;
124        ed.insert_str_after_cursor("not empty").unwrap();
125
126        let res = map.handle_key(Ctrl('d'), &mut ed, &mut EmptyCompleter);
127        assert_eq!(res.is_ok(), true);
128    }
129
130    #[test]
131    /// ctrl-c should generate an error
132    fn ctrl_c() {
133        let mut context = Context::new();
134        let out = Vec::new();
135        let mut ed = Editor::new(out, Prompt::from("prompt"), None, &mut context).unwrap();
136        let mut map = TestKeyMap;
137
138        let res = map.handle_key(Ctrl('c'), &mut ed, &mut EmptyCompleter);
139        assert_eq!(res.is_err(), true);
140        assert_eq!(res.err().unwrap().kind(), ErrorKind::Interrupted);
141    }
142}