1use std::io::{self, Write};
2use termion::event::Key;
3
4use crate::CursorPosition;
5use crate::Editor;
6use crate::KeyMap;
7
8#[derive(Default)]
16pub struct Emacs {
17 last_arg_fetch_index: Option<usize>,
18}
19
20impl Emacs {
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 fn handle_ctrl_key<W: Write>(&mut self, c: char, ed: &mut Editor<'_, W>) -> io::Result<()> {
26 match c {
27 'l' => ed.clear(),
28 'a' => ed.move_cursor_to_start_of_line(),
29 'e' => ed.move_cursor_to_end_of_line(),
30 'b' => ed.move_cursor_left(1),
31 'f' => ed.move_cursor_right(1),
32 'd' => ed.delete_after_cursor(),
33 'p' => ed.move_up(),
34 'n' => ed.move_down(),
35 'u' => ed.delete_all_before_cursor(),
36 'k' => ed.delete_all_after_cursor(),
37 'w' => ed.delete_word_before_cursor(true),
38 'x' => {
39 ed.undo()?;
40 Ok(())
41 }
42 _ => Ok(()),
43 }
44 }
45
46 fn handle_alt_key<W: Write>(&mut self, c: char, ed: &mut Editor<'_, W>) -> io::Result<()> {
47 match c {
48 '<' => ed.move_to_start_of_history(),
49 '>' => ed.move_to_end_of_history(),
50 '\x7F' => ed.delete_word_before_cursor(true),
51 'f' => emacs_move_word(ed, EmacsMoveDir::Right),
52 'b' => emacs_move_word(ed, EmacsMoveDir::Left),
53 'r' => {
54 ed.revert()?;
55 Ok(())
56 }
57 '.' => self.handle_last_arg_fetch(ed),
58 _ => Ok(()),
59 }
60 }
61
62 fn handle_last_arg_fetch<W: Write>(&mut self, ed: &mut Editor<'_, W>) -> io::Result<()> {
63 if ed.context().history.is_empty() {
65 return Ok(());
66 }
67
68 let history_index = match self.last_arg_fetch_index {
69 Some(0) => return Ok(()),
70 Some(x) => x - 1,
71 None => ed
72 .current_history_location()
73 .unwrap_or(ed.context().history.len() - 1),
74 };
75
76 if self.last_arg_fetch_index.is_some() {
79 let buffer_len = ed.current_buffer().num_chars();
80 if let Some(last_arg_len) = ed.current_buffer().last_arg().map(|x| x.len()) {
81 ed.delete_until(buffer_len - last_arg_len)?;
82 }
83 }
84
85 let buf = ed.context().history[history_index].clone();
87 if let Some(last_arg) = buf.last_arg() {
88 ed.insert_chars_after_cursor(last_arg)?;
89 }
90
91 self.last_arg_fetch_index = Some(history_index);
93
94 Ok(())
95 }
96}
97
98impl KeyMap for Emacs {
99 fn handle_key_core<W: Write>(&mut self, key: Key, ed: &mut Editor<'_, W>) -> io::Result<()> {
100 match key {
101 Key::Alt('.') => {}
102 _ => self.last_arg_fetch_index = None,
103 }
104
105 match key {
106 Key::Char(c) => ed.insert_after_cursor(c),
107 Key::Alt(c) => self.handle_alt_key(c, ed),
108 Key::Ctrl(c) => self.handle_ctrl_key(c, ed),
109 Key::Left => ed.move_cursor_left(1),
110 Key::Right => ed.move_cursor_right(1),
111 Key::Up => ed.move_up(),
112 Key::Down => ed.move_down(),
113 Key::Home => ed.move_cursor_to_start_of_line(),
114 Key::End => ed.move_cursor_to_end_of_line(),
115 Key::Backspace => ed.delete_before_cursor(),
116 Key::Delete => ed.delete_after_cursor(),
117 Key::Null => Ok(()),
118 _ => Ok(()),
119 }
120 }
121}
122
123#[derive(PartialEq, Clone, Copy)]
124enum EmacsMoveDir {
125 Left,
126 Right,
127}
128
129fn emacs_move_word<W: Write>(ed: &mut Editor<W>, direction: EmacsMoveDir) -> io::Result<()> {
130 let (words, pos) = ed.get_words_and_cursor_position();
131
132 let word_index = match pos {
133 CursorPosition::InWord(i) => Some(i),
134 CursorPosition::OnWordLeftEdge(mut i) => {
135 if i > 0 && direction == EmacsMoveDir::Left {
136 i -= 1;
137 }
138 Some(i)
139 }
140 CursorPosition::OnWordRightEdge(mut i) => {
141 if i < words.len() - 1 && direction == EmacsMoveDir::Right {
142 i += 1;
143 }
144 Some(i)
145 }
146 CursorPosition::InSpace(left, right) => match direction {
147 EmacsMoveDir::Left => left,
148 EmacsMoveDir::Right => right,
149 },
150 };
151
152 match word_index {
153 None => Ok(()),
154 Some(i) => {
155 let (start, end) = words[i];
156
157 let new_cursor_pos = match direction {
158 EmacsMoveDir::Left => start,
159 EmacsMoveDir::Right => end,
160 };
161
162 ed.move_cursor_to(new_cursor_pos)
163 }
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use crate::editor::Prompt;
171 use crate::{Completer, Context, Editor, KeyMap};
172 use std::io::Write;
173 use termion::event::Key;
174
175 fn simulate_keys<'a, 'b, W: Write, M: KeyMap, I>(
176 keymap: &mut M,
177 ed: &mut Editor<'a, W>,
178 keys: I,
179 ) -> bool
180 where
181 I: IntoIterator<Item = &'b Key>,
182 {
183 for k in keys {
184 if keymap.handle_key(*k, ed, &mut EmptyCompleter).unwrap() {
185 return true;
186 }
187 }
188
189 false
190 }
191
192 struct EmptyCompleter;
193
194 impl Completer for EmptyCompleter {
195 fn completions(&mut self, _start: &str) -> Vec<String> {
196 Vec::default()
197 }
198 }
199
200 #[test]
201 fn enter_is_done() {
202 let mut context = Context::new();
203 let out = Vec::new();
204 let mut ed = Editor::new(out, Prompt::from("prompt"), None, &mut context).unwrap();
205 let mut map = Emacs::new();
206 ed.insert_str_after_cursor("done").unwrap();
207 assert_eq!(ed.cursor(), 4);
208
209 assert!(simulate_keys(&mut map, &mut ed, [Key::Char('\n')].iter()));
210
211 assert_eq!(ed.cursor(), 4);
212 assert_eq!(String::from(ed), "done");
213 }
214
215 #[test]
216 fn move_cursor_left() {
217 let mut context = Context::new();
218 let out = Vec::new();
219 let mut ed = Editor::new(out, Prompt::from("prompt"), None, &mut context).unwrap();
220 let mut map = Emacs::new();
221 ed.insert_str_after_cursor("let").unwrap();
222 assert_eq!(ed.cursor(), 3);
223
224 simulate_keys(&mut map, &mut ed, [Key::Left, Key::Char('f')].iter());
225
226 assert_eq!(ed.cursor(), 3);
227 assert_eq!(String::from(ed), "left");
228 }
229
230 #[test]
231 fn move_word() {
232 let mut context = Context::new();
233 let out = Vec::new();
234
235 let mut ed = Editor::new(out, Prompt::from("prompt"), None, &mut context).unwrap();
236 let mut map = Emacs::new();
237 ed.insert_str_after_cursor("abc def ghi").unwrap();
238 assert_eq!(ed.cursor(), 11);
239
240 simulate_keys(&mut map, &mut ed, [Key::Alt('b')].iter());
241
242 assert_eq!(ed.cursor(), 8);
244
245 simulate_keys(&mut map, &mut ed, [Key::Alt('b'), Key::Alt('f')].iter());
246
247 assert_eq!(ed.cursor(), 7);
249 }
250
251 #[test]
252 fn cursor_movement() {
253 let mut context = Context::new();
254 let out = Vec::new();
255 let mut ed = Editor::new(out, Prompt::from("prompt"), None, &mut context).unwrap();
256 let mut map = Emacs::new();
257 ed.insert_str_after_cursor("right").unwrap();
258 assert_eq!(ed.cursor(), 5);
259
260 simulate_keys(&mut map, &mut ed, [Key::Left, Key::Left, Key::Right].iter());
261
262 assert_eq!(ed.cursor(), 4);
263 }
264
265 #[test]
266 fn ctrl_h() {
268 let mut context = Context::new();
269 let out = Vec::new();
270 let mut ed = Editor::new(out, Prompt::from("prompt"), None, &mut context).unwrap();
271 let mut map = Emacs::new();
272 ed.insert_str_after_cursor("not empty").unwrap();
273
274 let res = map.handle_key(Key::Ctrl('h'), &mut ed, &mut EmptyCompleter);
275 assert_eq!(res.is_ok(), true);
276 assert_eq!(ed.current_buffer().to_string(), "not empt".to_string());
277 }
278}