@@ -1,7 +1,7 @@
11pub use crate::commands::Command;
22use crate::config::Config;
33use helix_core::hashmap;
4−use helix_view::{document::Mode, input::KeyEvent};
4+use helix_view::{document::Mode, info::Info, input::KeyEvent};
55use serde::Deserialize;
66use std::{
77 collections::HashMap,
@@ -24,30 +24,276 @@ macro_rules! key {
2424 };
2525}
2626
27−macro_rules! ctrl {
28− ($($ch:tt)*) => {
29− KeyEvent {
30− code: ::helix_view::keyboard::KeyCode::Char($($ch)*),
31− modifiers: ::helix_view::keyboard::KeyModifiers::CONTROL,
27+/// Macro for defining the root of a `Keymap` object. Example:
28+///
29+/// ```
30+/// # use helix_core::hashmap;
31+/// # use helix_term::keymap;
32+/// # use helix_term::keymap::Keymap;
33+/// let normal_mode = keymap!({ "Normal mode"
34+/// "i" => insert_mode,
35+/// "g" => { "Goto"
36+/// "g" => goto_file_start,
37+/// "e" => goto_file_end,
38+/// },
39+/// "j" | "down" => move_line_down,
40+/// });
41+/// let keymap = Keymap::new(normal_mode);
42+/// ```
43+#[macro_export]
44+macro_rules! keymap {
45+ (@trie $cmd:ident) => {
46+ $crate::keymap::KeyTrie::Leaf($crate::commands::Command::$cmd)
47+ };
48+
49+ (@trie
50+ { $label:literal $($($key:literal)|+ => $value:tt,)+ }
51+ ) => {
52+ keymap!({ $label $($($key)|+ => $value,)+ })
53+ };
54+
55+ (
56+ { $label:literal $($($key:literal)|+ => $value:tt,)+ }
57+ ) => {
58+ // modified from the hashmap! macro
59+ {
60+ let _cap = hashmap!(@count $($($key),+),*);
61+ let mut _map = ::std::collections::HashMap::with_capacity(_cap);
62+ let mut _order = ::std::vec::Vec::with_capacity(_cap);
63+ $(
64+ $(
65+ let _key = $key.parse::<::helix_view::input::KeyEvent>().unwrap();
66+ _map.insert(
67+ _key,
68+ keymap!(@trie $value)
69+ );
70+ _order.push(_key);
71+ )+
72+ )*
73+ $crate::keymap::KeyTrie::Node($crate::keymap::KeyTrieNode::new($label, _map, _order))
3274 }
3375 };
3476}
3577
36−macro_rules! alt {
37− ($($ch:tt)*) => {
38− KeyEvent {
39− code: ::helix_view::keyboard::KeyCode::Char($($ch)*),
40− modifiers: ::helix_view::keyboard::KeyModifiers::ALT,
78+#[derive(Debug, Clone, Deserialize)]
79+pub struct KeyTrieNode {
80+ /// A label for keys coming under this node, like "Goto mode"
81+ #[serde(skip)]
82+ name: String,
83+ #[serde(flatten)]
84+ map: HashMap<KeyEvent, KeyTrie>,
85+ #[serde(skip)]
86+ order: Vec<KeyEvent>,
87+}
88+
89+impl KeyTrieNode {
90+ pub fn new(name: &str, map: HashMap<KeyEvent, KeyTrie>, order: Vec<KeyEvent>) -> Self {
91+ Self {
92+ name: name.to_string(),
93+ map,
94+ order,
4195 }
42− };
96+ }
97+
98+ pub fn name(&self) -> &str {
99+ &self.name
100+ }
101+
102+ /// Merge another Node in. Leaves and subnodes from the other node replace
103+ /// corresponding keyevent in self, except when both other and self have
104+ /// subnodes for same key. In that case the merge is recursive.
105+ pub fn merge(&mut self, mut other: Self) {
106+ for (key, trie) in std::mem::take(&mut other.map) {
107+ if let Some(KeyTrie::Node(node)) = self.map.get_mut(&key) {
108+ if let KeyTrie::Node(other_node) = trie {
109+ node.merge(other_node);
110+ continue;
111+ }
112+ }
113+ self.map.insert(key, trie);
114+ }
115+
116+ for &key in self.map.keys() {
117+ if !self.order.contains(&key) {
118+ self.order.push(key);
119+ }
120+ }
121+ }
122+}
123+
124+impl From<KeyTrieNode> for Info {
125+ fn from(node: KeyTrieNode) -> Self {
126+ let mut body: Vec<(&str, Vec<KeyEvent>)> = Vec::with_capacity(node.len());
127+ for (&key, trie) in node.iter() {
128+ let desc = match trie {
129+ KeyTrie::Leaf(cmd) => cmd.doc(),
130+ KeyTrie::Node(n) => n.name(),
131+ };
132+ match body.iter().position(|(d, _)| d == &desc) {
133+ // FIXME: multiple keys are ordered randomly (use BTreeSet)
134+ Some(pos) => body[pos].1.push(key),
135+ None => body.push((desc, vec![key])),
136+ }
137+ }
138+ body.sort_unstable_by_key(|(_, keys)| {
139+ node.order.iter().position(|&k| k == keys[0]).unwrap()
140+ });
141+ let prefix = format!("{} ", node.name());
142+ if body.iter().all(|(desc, _)| desc.starts_with(&prefix)) {
143+ body = body
144+ .into_iter()
145+ .map(|(desc, keys)| (desc.strip_prefix(&prefix).unwrap(), keys))
146+ .collect();
147+ }
148+ Info::key(node.name(), body)
149+ }
150+}
151+
152+impl Default for KeyTrieNode {
153+ fn default() -> Self {
154+ Self::new("", HashMap::new(), Vec::new())
155+ }
156+}
157+
158+impl PartialEq for KeyTrieNode {
159+ fn eq(&self, other: &Self) -> bool {
160+ self.map == other.map
161+ }
162+}
163+
164+impl Deref for KeyTrieNode {
165+ type Target = HashMap<KeyEvent, KeyTrie>;
166+
167+ fn deref(&self) -> &Self::Target {
168+ &self.map
169+ }
170+}
171+
172+impl DerefMut for KeyTrieNode {
173+ fn deref_mut(&mut self) -> &mut Self::Target {
174+ &mut self.map
175+ }
176+}
177+
178+#[derive(Debug, Clone, PartialEq, Deserialize)]
179+#[serde(untagged)]
180+pub enum KeyTrie {
181+ Leaf(Command),
182+ Node(KeyTrieNode),
183+}
184+
185+impl KeyTrie {
186+ pub fn node(&self) -> Option<&KeyTrieNode> {
187+ match *self {
188+ KeyTrie::Node(ref node) => Some(node),
189+ KeyTrie::Leaf(_) => None,
190+ }
191+ }
192+
193+ pub fn node_mut(&mut self) -> Option<&mut KeyTrieNode> {
194+ match *self {
195+ KeyTrie::Node(ref mut node) => Some(node),
196+ KeyTrie::Leaf(_) => None,
197+ }
198+ }
199+
200+ /// Merge another KeyTrie in, assuming that this KeyTrie and the other
201+ /// are both Nodes. Panics otherwise.
202+ pub fn merge_nodes(&mut self, mut other: Self) {
203+ let node = std::mem::take(other.node_mut().unwrap());
204+ self.node_mut().unwrap().merge(node);
205+ }
206+
207+ pub fn search(&self, keys: &[KeyEvent]) -> Option<&KeyTrie> {
208+ let mut trie = self;
209+ for key in keys {
210+ trie = match trie {
211+ KeyTrie::Node(map) => map.get(key),
212+ // leaf encountered while keys left to process
213+ KeyTrie::Leaf(_) => None,
214+ }?
215+ }
216+ Some(trie)
217+ }
218+}
219+
220+#[derive(Debug, Clone, PartialEq)]
221+pub enum KeymapResult {
222+ /// Needs more keys to execute a command. Contains valid keys for next keystroke.
223+ Pending(KeyTrieNode),
224+ Matched(Command),
225+ /// Key was not found in the root keymap
226+ NotFound,
227+ /// Key is invalid in combination with previous keys. Contains keys leading upto
228+ /// and including current (invalid) key.
229+ Cancelled(Vec<KeyEvent>),
230+}
231+
232+#[derive(Debug, Clone, PartialEq, Deserialize)]
233+pub struct Keymap {
234+ /// Always a Node
235+ #[serde(flatten)]
236+ root: KeyTrie,
237+ #[serde(skip)]
238+ state: Vec<KeyEvent>,
239+}
240+
241+impl Keymap {
242+ pub fn new(root: KeyTrie) -> Self {
243+ Keymap {
244+ root,
245+ state: Vec::new(),
246+ }
247+ }
248+
249+ pub fn root(&self) -> &KeyTrie {
250+ &self.root
251+ }
252+
253+ /// Lookup `key` in the keymap to try and find a command to execute
254+ pub fn get(&mut self, key: KeyEvent) -> KeymapResult {
255+ let &first = self.state.get(0).unwrap_or(&key);
256+ let trie = match self.root.search(&[first]) {
257+ Some(&KeyTrie::Leaf(cmd)) => return KeymapResult::Matched(cmd),
258+ None => return KeymapResult::NotFound,
259+ Some(t) => t,
260+ };
261+ self.state.push(key);
262+ match trie.search(&self.state[1..]) {
263+ Some(&KeyTrie::Node(ref map)) => KeymapResult::Pending(map.clone()),
264+ Some(&KeyTrie::Leaf(command)) => {
265+ self.state.clear();
266+ KeymapResult::Matched(command)
267+ }
268+ None => KeymapResult::Cancelled(self.state.drain(..).collect()),
269+ }
270+ }
271+
272+ pub fn merge(&mut self, other: Self) {
273+ self.root.merge_nodes(other.root);
274+ }
275+}
276+
277+impl Deref for Keymap {
278+ type Target = KeyTrieNode;
279+
280+ fn deref(&self) -> &Self::Target {
281+ &self.root.node().unwrap()
282+ }
283+}
284+
285+impl Default for Keymap {
286+ fn default() -> Self {
287+ Self::new(KeyTrie::Node(KeyTrieNode::default()))
288+ }
43289}
44290
45291#[derive(Debug, Clone, PartialEq, Deserialize)]
46292#[serde(transparent)]
47−pub struct Keymaps(pub HashMap<Mode, HashMap<KeyEvent, Command>>);
293+pub struct Keymaps(pub HashMap<Mode, Keymap>);
48294
49295impl Deref for Keymaps {
50− type Target = HashMap<Mode, HashMap<KeyEvent, Command>>;
296+ type Target = HashMap<Mode, Keymap>;
51297
52298 fn deref(&self) -> &Self::Target {
53299 &self.0
@@ -62,252 +308,298 @@ impl DerefMut for Keymaps {
62308
63309impl Default for Keymaps {
64310 fn default() -> Keymaps {
65− let normal = hashmap!(
66− key!('h') => Command::move_char_left,
67− key!('j') => Command::move_line_down,
68− key!('k') => Command::move_line_up,
69− key!('l') => Command::move_char_right,
70−
71− key!(Left) => Command::move_char_left,
72− key!(Down) => Command::move_line_down,
73− key!(Up) => Command::move_line_up,
74− key!(Right) => Command::move_char_right,
75−
76− key!('t') => Command::find_till_char,
77− key!('f') => Command::find_next_char,
78− key!('T') => Command::till_prev_char,
79− key!('F') => Command::find_prev_char,
80− // and matching set for select mode (extend)
81− //
82− key!('r') => Command::replace,
83− key!('R') => Command::replace_with_yanked,
84−
85− key!('~') => Command::switch_case,
86− alt!('`') => Command::switch_to_uppercase,
87− key!('`') => Command::switch_to_lowercase,
88−
89− key!(Home) => Command::goto_line_start,
90− key!(End) => Command::goto_line_end,
91−
92− key!('w') => Command::move_next_word_start,
93− key!('b') => Command::move_prev_word_start,
94− key!('e') => Command::move_next_word_end,
95−
96− key!('W') => Command::move_next_long_word_start,
97− key!('B') => Command::move_prev_long_word_start,
98− key!('E') => Command::move_next_long_word_end,
99−
100− key!('v') => Command::select_mode,
101− key!('g') => Command::goto_mode,
102− key!(':') => Command::command_mode,
103−
104− key!('i') => Command::insert_mode,
105− key!('I') => Command::prepend_to_line,
106− key!('a') => Command::append_mode,
107− key!('A') => Command::append_to_line,
108− key!('o') => Command::open_below,
109− key!('O') => Command::open_above,
311+ let normal = keymap!({ "Normal mode"
312+ "h" | "left" => move_char_left,
313+ "j" | "down" => move_line_down,
314+ "k" | "up" => move_line_up,
315+ "l" | "right" => move_char_right,
316+
317+ "t" => find_till_char,
318+ "f" => find_next_char,
319+ "T" => till_prev_char,
320+ "F" => find_prev_char,
321+ "r" => replace,
322+ "R" => replace_with_yanked,
323+
324+ "~" => switch_case,
325+ "`" => switch_to_lowercase,
326+ "A-`" => switch_to_uppercase,
327+
328+ "home" => goto_line_start,
329+ "end" => goto_line_end,
330+
331+ "w" => move_next_word_start,
332+ "b" => move_prev_word_start,
333+ "e" => move_next_word_end,
334+
335+ "W" => move_next_long_word_start,
336+ "B" => move_prev_long_word_start,
337+ "E" => move_next_long_word_end,
338+
339+ "v" => select_mode,
340+ "g" => { "Goto"
341+ "g" => goto_file_start,
342+ "e" => goto_file_end,
343+ "h" => goto_line_start,
344+ "l" => goto_line_end,
345+ "s" => goto_first_nonwhitespace,
346+ "d" => goto_definition,
347+ "y" => goto_type_definition,
348+ "r" => goto_reference,
349+ "i" => goto_implementation,
350+ "t" => goto_window_top,
351+ "m" => goto_window_middle,
352+ "b" => goto_window_bottom,
353+ "a" => goto_last_accessed_file,
354+ },
355+ ":" => command_mode,
356+
357+ "i" => insert_mode,
358+ "I" => prepend_to_line,
359+ "a" => append_mode,
360+ "A" => append_to_line,
361+ "o" => open_below,
362+ "O" => open_above,
110363 // [<space> ]<space> equivalents too (add blank new line, no edit)
111364
112−
113− key!('d') => Command::delete_selection,
365+ "d" => delete_selection,
114366 // TODO: also delete without yanking
115− key!('c') => Command::change_selection,
367+ "c" => change_selection,
116368 // TODO: also change delete without yanking
117369
118− // key!('r') => Command::replace_with_char,
119−
120− key!('s') => Command::select_regex,
121− alt!('s') => Command::split_selection_on_newline,
122− key!('S') => Command::split_selection,
123− key!(';') => Command::collapse_selection,
124− alt!(';') => Command::flip_selections,
125− key!('%') => Command::select_all,
126− key!('x') => Command::extend_line,
127− key!('X') => Command::extend_to_line_bounds,
370+ "s" => select_regex,
371+ "A-s" => split_selection_on_newline,
372+ "S" => split_selection,
373+ ";" => collapse_selection,
374+ "A-;" => flip_selections,
375+ "%" => select_all,
376+ "x" => extend_line,
377+ "X" => extend_to_line_bounds,
128378 // crop_to_whole_line
129379
380+ "m" => { "Match"
381+ "m" => match_brackets,
382+ "s" => surround_add,
383+ "r" => surround_replace,
384+ "d" => surround_delete,
385+ "a" => select_textobject_around,
386+ "i" => select_textobject_inner,
387+ },
388+ "[" => { "Left bracket"
389+ "d" => goto_prev_diag,
390+ "D" => goto_first_diag,
391+ },
392+ "]" => { "Right bracket"
393+ "d" => goto_next_diag,
394+ "D" => goto_last_diag,
395+ },
130396
131− key!('m') => Command::match_mode,
132− key!('[') => Command::left_bracket_mode,
133− key!(']') => Command::right_bracket_mode,
134−
135− key!('/') => Command::search,
397+ "/" => search,
136398 // ? for search_reverse
137− key!('n') => Command::search_next,
138− key!('N') => Command::extend_search_next,
399+ "n" => search_next,
400+ "N" => extend_search_next,
139401 // N for search_prev
140− key!('*') => Command::search_selection,
402+ "*" => search_selection,
141403
142− key!('u') => Command::undo,
143− key!('U') => Command::redo,
404+ "u" => undo,
405+ "U" => redo,
144406
145− key!('y') => Command::yank,
407+ "y" => yank,
146408 // yank_all
147− key!('p') => Command::paste_after,
409+ "p" => paste_after,
148410 // paste_all
149− key!('P') => Command::paste_before,
411+ "P" => paste_before,
150412
151− key!('>') => Command::indent,
152− key!('<') => Command::unindent,
153− key!('=') => Command::format_selections,
154− key!('J') => Command::join_selections,
413+ ">" => indent,
414+ "<" => unindent,
415+ "=" => format_selections,
416+ "J" => join_selections,
155417 // TODO: conflicts hover/doc
156− key!('K') => Command::keep_selections,
418+ "K" => keep_selections,
157419 // TODO: and another method for inverse
158420
159421 // TODO: clashes with space mode
160− key!(' ') => Command::keep_primary_selection,
422+ "space" => keep_primary_selection,
161423
162− // key!('q') => Command::record_macro,
163− // key!('Q') => Command::replay_macro,
424+ // "q" => record_macro,
425+ // "Q" => replay_macro,
164426
165− // ~ / apostrophe => change case
166427 // & align selections
167428 // _ trim selections
168429
169430 // C / altC = copy (repeat) selections on prev/next lines
170431
171− key!(Esc) => Command::normal_mode,
172− key!(PageUp) => Command::page_up,
173− key!(PageDown) => Command::page_down,
174− ctrl!('b') => Command::page_up,
175− ctrl!('f') => Command::page_down,
176− ctrl!('u') => Command::half_page_up,
177− ctrl!('d') => Command::half_page_down,
178−
179− ctrl!('w') => Command::window_mode,
432+ "esc" => normal_mode,
433+ "C-b" | "pageup" => page_up,
434+ "C-f" | "pagedown" => page_down,
435+ "C-u" => half_page_up,
436+ "C-d" => half_page_down,
437+
438+ "C-w" => { "Window"
439+ "C-w" | "w" => rotate_view,
440+ "C-h" | "h" => hsplit,
441+ "C-v" | "v" => vsplit,
442+ "C-q" | "q" => wclose,
443+ },
180444
181445 // move under <space>c
182− ctrl!('c') => Command::toggle_comments,
183− key!('K') => Command::hover,
446+ "C-c" => toggle_comments,
447+ "K" => hover,
184448
185449 // z family for save/restore/combine from/to sels from register
186450
187− // supposedly ctrl!('i') but did not work
188− key!(Tab) => Command::jump_forward,
189− ctrl!('o') => Command::jump_backward,
190− // ctrl!('s') => Command::save_selection,
191−
192− key!(' ') => Command::space_mode,
193− key!('z') => Command::view_mode,
451+ // supposedly "C-i" but did not work
452+ "tab" => jump_forward,
453+ "C-o" => jump_backward,
454+ // "C-s" => save_selection,
455+
456+ "space" => { "Space"
457+ "f" => file_picker,
458+ "b" => buffer_picker,
459+ "s" => symbol_picker,
460+ "a" => code_action,
461+ "'" => last_picker,
462+ "w" => { "Window"
463+ "C-w" | "w" => rotate_view,
464+ "C-h" | "h" => hsplit,
465+ "C-v" | "v" => vsplit,
466+ "C-q" | "q" => wclose,
467+ },
468+ "y" => yank_joined_to_clipboard,
469+ "Y" => yank_main_selection_to_clipboard,
470+ "p" => paste_clipboard_after,
471+ "P" => paste_clipboard_before,
472+ "R" => replace_selections_with_clipboard,
473+ "space" => keep_primary_selection,
474+ },
475+ "z" => { "View"
476+ "z" | "c" => align_view_center,
477+ "t" => align_view_top,
478+ "b" => align_view_bottom,
479+ "m" => align_view_middle,
480+ "k" => scroll_up,
481+ "j" => scroll_down,
482+ },
194483
195− key!('"') => Command::select_register,
196− );
484+ "\"" => select_register,
485+ });
197486 // TODO: decide whether we want normal mode to also be select mode (kakoune-like), or whether
198487 // we keep this separate select mode. More keys can fit into normal mode then, but it's weird
199488 // because some selection operations can now be done from normal mode, some from select mode.
200489 let mut select = normal.clone();
201− select.extend(
202− hashmap!(
203− key!('h') => Command::extend_char_left,
204− key!('j') => Command::extend_line_down,
205− key!('k') => Command::extend_line_up,
206− key!('l') => Command::extend_char_right,
207−
208− key!(Left) => Command::extend_char_left,
209− key!(Down) => Command::extend_line_down,
210− key!(Up) => Command::extend_line_up,
211− key!(Right) => Command::extend_char_right,
212−
213− key!('w') => Command::extend_next_word_start,
214− key!('b') => Command::extend_prev_word_start,
215− key!('e') => Command::extend_next_word_end,
216−
217− key!('t') => Command::extend_till_char,
218− key!('f') => Command::extend_next_char,
219−
220− key!('T') => Command::extend_till_prev_char,
221− key!('F') => Command::extend_prev_char,
222− key!(Home) => Command::goto_line_start,
223− key!(End) => Command::goto_line_end,
224− key!(Esc) => Command::exit_select_mode,
225− )
226− .into_iter(),
227− );
228−
490+ select.merge_nodes(keymap!({ "Select mode"
491+ "h" | "left" => extend_char_left,
492+ "j" | "down" => extend_line_down,
493+ "k" | "up" => extend_line_up,
494+ "l" | "right" => extend_char_right,
495+
496+ "w" => extend_next_word_start,
497+ "b" => extend_prev_word_start,
498+ "e" => extend_next_word_end,
499+
500+ "t" => extend_till_char,
501+ "f" => extend_next_char,
502+ "T" => extend_till_prev_char,
503+ "F" => extend_prev_char,
504+
505+ "home" => goto_line_start,
506+ "end" => goto_line_end,
507+ "esc" => exit_select_mode,
508+ }));
509+ let insert = keymap!({ "Insert mode"
510+ "esc" => normal_mode,
511+
512+ "backspace" => delete_char_backward,
513+ "del" => delete_char_forward,
514+ "ret" => insert_newline,
515+ "tab" => insert_tab,
516+ "C-w" => delete_word_backward,
517+
518+ "left" => move_char_left,
519+ "down" => move_line_down,
520+ "up" => move_line_up,
521+ "right" => move_char_right,
522+ "pageup" => page_up,
523+ "pagedown" => page_down,
524+ "home" => goto_line_start,
525+ "end" => goto_line_end_newline,
526+
527+ "C-x" => completion,
528+ });
229529 Keymaps(hashmap!(
230− // as long as you cast the first item, rust is able to infer the other cases
231− // TODO: select could be normal mode with some bindings merged over
232− Mode::Normal => normal,
233− Mode::Select => select,
234− Mode::Insert => hashmap!(
235− key!(Esc) => Command::normal_mode as Command,
236− key!(Backspace) => Command::delete_char_backward,
237− key!(Delete) => Command::delete_char_forward,
238− key!(Enter) => Command::insert_newline,
239− key!(Tab) => Command::insert_tab,
240− key!(Left) => Command::move_char_left,
241− key!(Down) => Command::move_line_down,
242− key!(Up) => Command::move_line_up,
243− key!(Right) => Command::move_char_right,
244− key!(PageUp) => Command::page_up,
245− key!(PageDown) => Command::page_down,
246− key!(Home) => Command::goto_line_start,
247− key!(End) => Command::goto_line_end_newline,
248− ctrl!('x') => Command::completion,
249− ctrl!('w') => Command::delete_word_backward,
250− ),
530+ Mode::Normal => Keymap::new(normal),
531+ Mode::Select => Keymap::new(select),
532+ Mode::Insert => Keymap::new(insert),
251533 ))
252534 }
253535}
254536
255−/// Merge default config keys with user overwritten keys for custom
256−/// user config.
537+/// Merge default config keys with user overwritten keys for custom user config.
257538pub fn merge_keys(mut config: Config) -> Config {
258539 let mut delta = std::mem::take(&mut config.keys);
259540 for (mode, keys) in &mut *config.keys {
260− keys.extend(delta.remove(mode).unwrap_or_default());
541+ keys.merge(delta.remove(mode).unwrap_or_default())
261542 }
262543 config
263544}
264545
265546#[test]
266547fn merge_partial_keys() {
267− use helix_view::keyboard::{KeyCode, KeyModifiers};
268548 let config = Config {
269549 keys: Keymaps(hashmap! {
270− Mode::Normal => hashmap! {
271− KeyEvent {
272− code: KeyCode::Char('i'),
273− modifiers: KeyModifiers::NONE,
274− } => Command::normal_mode,
275− KeyEvent { // key that does not exist
276− code: KeyCode::Char('无'),
277− modifiers: KeyModifiers::NONE,
278− } => Command::insert_mode,
279− },
550+ Mode::Normal => Keymap::new(
551+ keymap!({ "Normal mode"
552+ "i" => normal_mode,
553+ "无" => insert_mode,
554+ "z" => jump_backward,
555+ "g" => { "Merge into goto mode"
556+ "$" => goto_line_end,
557+ "g" => delete_char_forward,
558+ },
559+ })
560+ )
280561 }),
281562 ..Default::default()
282563 };
283− let merged_config = merge_keys(config.clone());
564+ let mut merged_config = merge_keys(config.clone());
284565 assert_ne!(config, merged_config);
566+
567+ let keymap = merged_config.keys.0.get_mut(&Mode::Normal).unwrap();
285568 assert_eq!(
286− *merged_config
287− .keys
288− .0
289− .get(&Mode::Normal)
290− .unwrap()
291− .get(&KeyEvent {
292− code: KeyCode::Char('i'),
293− modifiers: KeyModifiers::NONE
294− })
295− .unwrap(),
296− Command::normal_mode
569+ keymap.get(key!('i')),
570+ KeymapResult::Matched(Command::normal_mode),
571+ "Leaf should replace leaf"
297572 );
298573 assert_eq!(
299− *merged_config
300− .keys
301− .0
302− .get(&Mode::Normal)
303− .unwrap()
304− .get(&KeyEvent {
305− code: KeyCode::Char('无'),
306− modifiers: KeyModifiers::NONE
307− })
308− .unwrap(),
309− Command::insert_mode
574+ keymap.get(key!('无')),
575+ KeymapResult::Matched(Command::insert_mode),
576+ "New leaf should be present in merged keymap"
310577 );
578+ // Assumes that z is a node in the default keymap
579+ assert_eq!(
580+ keymap.get(key!('z')),
581+ KeymapResult::Matched(Command::jump_backward),
582+ "Leaf should replace node"
583+ );
584+ // Assumes that `g` is a node in default keymap
585+ assert_eq!(
586+ keymap.root().search(&[key!('g'), key!('$')]).unwrap(),
587+ &KeyTrie::Leaf(Command::goto_line_end),
588+ "Leaf should be present in merged subnode"
589+ );
590+ // Assumes that `gg` is in default keymap
591+ assert_eq!(
592+ keymap.root().search(&[key!('g'), key!('g')]).unwrap(),
593+ &KeyTrie::Leaf(Command::delete_char_forward),
594+ "Leaf should replace old leaf in merged subnode"
595+ );
596+ // Assumes that `ge` is in default keymap
597+ assert_eq!(
598+ keymap.root().search(&[key!('g'), key!('e')]).unwrap(),
599+ &KeyTrie::Leaf(Command::goto_file_end),
600+ "Old leaves in subnode should be present in merged node"
601+ );
602+
311603 assert!(merged_config.keys.0.get(&Mode::Normal).unwrap().len() > 1);
312604 assert!(merged_config.keys.0.get(&Mode::Insert).unwrap().len() > 0);
313605}