on 6c11708 help sign in
helix/helix-term/src/keymap.rs
936 lines 30 kB history raw
1pub use crate::commands::MappableCommand;
2use crate::config::Config;
3use helix_core::hashmap;
4use helix_view::{document::Mode, info::Info, input::KeyEvent};
5use serde::Deserialize;
6use std::{
7 borrow::Cow,
8 collections::{BTreeSet, HashMap},
9 ops::{Deref, DerefMut},
10};
12#[macro_export]
13macro_rules! key {
14 ($key:ident) => {
15 ::helix_view::input::KeyEvent {
16 code: ::helix_view::keyboard::KeyCode::$key,
17 modifiers: ::helix_view::keyboard::KeyModifiers::NONE,
18 }
19 };
20 ($($ch:tt)*) => {
21 ::helix_view::input::KeyEvent {
22 code: ::helix_view::keyboard::KeyCode::Char($($ch)*),
23 modifiers: ::helix_view::keyboard::KeyModifiers::NONE,
24 }
25 };
28#[macro_export]
29macro_rules! shift {
30 ($key:ident) => {
31 ::helix_view::input::KeyEvent {
32 code: ::helix_view::keyboard::KeyCode::$key,
33 modifiers: ::helix_view::keyboard::KeyModifiers::SHIFT,
34 }
35 };
36 ($($ch:tt)*) => {
37 ::helix_view::input::KeyEvent {
38 code: ::helix_view::keyboard::KeyCode::Char($($ch)*),
39 modifiers: ::helix_view::keyboard::KeyModifiers::SHIFT,
40 }
41 };
44#[macro_export]
45macro_rules! ctrl {
46 ($key:ident) => {
47 ::helix_view::input::KeyEvent {
48 code: ::helix_view::keyboard::KeyCode::$key,
49 modifiers: ::helix_view::keyboard::KeyModifiers::CONTROL,
50 }
51 };
52 ($($ch:tt)*) => {
53 ::helix_view::input::KeyEvent {
54 code: ::helix_view::keyboard::KeyCode::Char($($ch)*),
55 modifiers: ::helix_view::keyboard::KeyModifiers::CONTROL,
56 }
57 };
60#[macro_export]
61macro_rules! alt {
62 ($key:ident) => {
63 ::helix_view::input::KeyEvent {
64 code: ::helix_view::keyboard::KeyCode::$key,
65 modifiers: ::helix_view::keyboard::KeyModifiers::ALT,
66 }
67 };
68 ($($ch:tt)*) => {
69 ::helix_view::input::KeyEvent {
70 code: ::helix_view::keyboard::KeyCode::Char($($ch)*),
71 modifiers: ::helix_view::keyboard::KeyModifiers::ALT,
72 }
73 };
76/// Macro for defining the root of a `Keymap` object. Example:
77///
78/// ```
79/// # use helix_core::hashmap;
80/// # use helix_term::keymap;
81/// # use helix_term::keymap::Keymap;
82/// let normal_mode = keymap!({ "Normal mode"
83/// "i" => insert_mode,
84/// "g" => { "Goto"
85/// "g" => goto_file_start,
86/// "e" => goto_file_end,
87/// },
88/// "j" | "down" => move_line_down,
89/// });
90/// let keymap = Keymap::new(normal_mode);
91/// ```
92#[macro_export]
93macro_rules! keymap {
94 (@trie $cmd:ident) => {
95 $crate::keymap::KeyTrie::Leaf($crate::commands::MappableCommand::$cmd)
96 };
98 (@trie
99 { $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
100 ) => {
101 keymap!({ $label $(sticky=$sticky)? $($($key)|+ => $value,)+ })
102 };
104 (@trie [$($cmd:ident),* $(,)?]) => {
105 $crate::keymap::KeyTrie::Sequence(vec![$($crate::commands::Command::$cmd),*])
106 };
108 (
109 { $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
110 ) => {
111 // modified from the hashmap! macro
112 {
113 let _cap = hashmap!(@count $($($key),+),*);
114 let mut _map = ::std::collections::HashMap::with_capacity(_cap);
115 let mut _order = ::std::vec::Vec::with_capacity(_cap);
116 $(
117 $(
118 let _key = $key.parse::<::helix_view::input::KeyEvent>().unwrap();
119 let _duplicate = _map.insert(
120 _key,
121 keymap!(@trie $value)
122 );
123 assert!(_duplicate.is_none(), "Duplicate key found: {:?}", _duplicate.unwrap());
124 _order.push(_key);
125 )+
126 )*
127 let mut _node = $crate::keymap::KeyTrieNode::new($label, _map, _order);
128 $( _node.is_sticky = $sticky; )?
129 $crate::keymap::KeyTrie::Node(_node)
130 }
131 };
134#[derive(Debug, Clone)]
135pub struct KeyTrieNode {
136 /// A label for keys coming under this node, like "Goto mode"
137 name: String,
138 map: HashMap<KeyEvent, KeyTrie>,
139 order: Vec<KeyEvent>,
140 pub is_sticky: bool,
143impl<'de> Deserialize<'de> for KeyTrieNode {
144 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
145 where
146 D: serde::Deserializer<'de>,
147 {
148 let map = HashMap::<KeyEvent, KeyTrie>::deserialize(deserializer)?;
149 let order = map.keys().copied().collect::<Vec<_>>(); // NOTE: map.keys() has arbitrary order
150 Ok(Self {
151 map,
152 order,
153 ..Default::default()
154 })
155 }
158impl KeyTrieNode {
159 pub fn new(name: &str, map: HashMap<KeyEvent, KeyTrie>, order: Vec<KeyEvent>) -> Self {
160 Self {
161 name: name.to_string(),
162 map,
163 order,
164 is_sticky: false,
165 }
166 }
168 pub fn name(&self) -> &str {
169 &self.name
170 }
172 /// Merge another Node in. Leaves and subnodes from the other node replace
173 /// corresponding keyevent in self, except when both other and self have
174 /// subnodes for same key. In that case the merge is recursive.
175 pub fn merge(&mut self, mut other: Self) {
176 for (key, trie) in std::mem::take(&mut other.map) {
177 if let Some(KeyTrie::Node(node)) = self.map.get_mut(&key) {
178 if let KeyTrie::Node(other_node) = trie {
179 node.merge(other_node);
180 continue;
181 }
182 }
183 self.map.insert(key, trie);
184 }
185 for &key in self.map.keys() {
186 if !self.order.contains(&key) {
187 self.order.push(key);
188 }
189 }
190 }
192 pub fn infobox(&self) -> Info {
193 let mut body: Vec<(&str, BTreeSet<KeyEvent>)> = Vec::with_capacity(self.len());
194 for (&key, trie) in self.iter() {
195 let desc = match trie {
196 KeyTrie::Leaf(cmd) => {
197 if cmd.name() == "no_op" {
198 continue;
199 }
200 cmd.doc()
201 }
202 KeyTrie::Node(n) => n.name(),
203 KeyTrie::Sequence(_) => "[Multiple commands]",
204 };
205 match body.iter().position(|(d, _)| d == &desc) {
206 Some(pos) => {
207 body[pos].1.insert(key);
208 }
209 None => body.push((desc, BTreeSet::from([key]))),
210 }
211 }
212 body.sort_unstable_by_key(|(_, keys)| {
213 self.order
214 .iter()
215 .position(|&k| k == *keys.iter().next().unwrap())
216 .unwrap()
217 });
218 let prefix = format!("{} ", self.name());
219 if body.iter().all(|(desc, _)| desc.starts_with(&prefix)) {
220 body = body
221 .into_iter()
222 .map(|(desc, keys)| (desc.strip_prefix(&prefix).unwrap(), keys))
223 .collect();
224 }
225 Info::new(self.name(), body)
226 }
228 /// Get a reference to the key trie node's order.
229 pub fn order(&self) -> &[KeyEvent] {
230 self.order.as_slice()
231 }
234impl Default for KeyTrieNode {
235 fn default() -> Self {
236 Self::new("", HashMap::new(), Vec::new())
237 }
240impl PartialEq for KeyTrieNode {
241 fn eq(&self, other: &Self) -> bool {
242 self.map == other.map
243 }
246impl Deref for KeyTrieNode {
247 type Target = HashMap<KeyEvent, KeyTrie>;
249 fn deref(&self) -> &Self::Target {
250 &self.map
251 }
254impl DerefMut for KeyTrieNode {
255 fn deref_mut(&mut self) -> &mut Self::Target {
256 &mut self.map
257 }
260#[derive(Debug, Clone, PartialEq, Deserialize)]
261#[serde(untagged)]
262pub enum KeyTrie {
263 Leaf(MappableCommand),
264 Sequence(Vec<MappableCommand>),
265 Node(KeyTrieNode),
268impl KeyTrie {
269 pub fn node(&self) -> Option<&KeyTrieNode> {
270 match *self {
271 KeyTrie::Node(ref node) => Some(node),
272 KeyTrie::Leaf(_) | KeyTrie::Sequence(_) => None,
273 }
274 }
276 pub fn node_mut(&mut self) -> Option<&mut KeyTrieNode> {
277 match *self {
278 KeyTrie::Node(ref mut node) => Some(node),
279 KeyTrie::Leaf(_) | KeyTrie::Sequence(_) => None,
280 }
281 }
283 /// Merge another KeyTrie in, assuming that this KeyTrie and the other
284 /// are both Nodes. Panics otherwise.
285 pub fn merge_nodes(&mut self, mut other: Self) {
286 let node = std::mem::take(other.node_mut().unwrap());
287 self.node_mut().unwrap().merge(node);
288 }
290 pub fn search(&self, keys: &[KeyEvent]) -> Option<&KeyTrie> {
291 let mut trie = self;
292 for key in keys {
293 trie = match trie {
294 KeyTrie::Node(map) => map.get(key),
295 // leaf encountered while keys left to process
296 KeyTrie::Leaf(_) | KeyTrie::Sequence(_) => None,
297 }?
298 }
299 Some(trie)
300 }
303#[derive(Debug, Clone, PartialEq)]
304pub enum KeymapResultKind {
305 /// Needs more keys to execute a command. Contains valid keys for next keystroke.
306 Pending(KeyTrieNode),
307 Matched(MappableCommand),
308 /// Matched a sequence of commands to execute.
309 MatchedSequence(Vec<MappableCommand>),
310 /// Key was not found in the root keymap
311 NotFound,
312 /// Key is invalid in combination with previous keys. Contains keys leading upto
313 /// and including current (invalid) key.
314 Cancelled(Vec<KeyEvent>),
317/// Returned after looking up a key in [`Keymap`]. The `sticky` field has a
318/// reference to the sticky node if one is currently active.
319#[derive(Debug)]
320pub struct KeymapResult<'a> {
321 pub kind: KeymapResultKind,
322 pub sticky: Option<&'a KeyTrieNode>,
325impl<'a> KeymapResult<'a> {
326 pub fn new(kind: KeymapResultKind, sticky: Option<&'a KeyTrieNode>) -> Self {
327 Self { kind, sticky }
328 }
331#[derive(Debug, Clone, PartialEq, Deserialize)]
332pub struct Keymap {
333 /// Always a Node
334 #[serde(flatten)]
335 root: KeyTrie,
336 /// Stores pending keys waiting for the next key. This is relative to a
337 /// sticky node if one is in use.
338 #[serde(skip)]
339 state: Vec<KeyEvent>,
340 /// Stores the sticky node if one is activated.
341 #[serde(skip)]
342 sticky: Option<KeyTrieNode>,
345impl Keymap {
346 pub fn new(root: KeyTrie) -> Self {
347 Self {
348 root,
349 state: Vec::new(),
350 sticky: None,
351 }
352 }
354 pub fn root(&self) -> &KeyTrie {
355 &self.root
356 }
358 pub fn sticky(&self) -> Option<&KeyTrieNode> {
359 self.sticky.as_ref()
360 }
362 /// Returns list of keys waiting to be disambiguated.
363 pub fn pending(&self) -> &[KeyEvent] {
364 &self.state
365 }
367 /// Lookup `key` in the keymap to try and find a command to execute. Escape
368 /// key cancels pending keystrokes. If there are no pending keystrokes but a
369 /// sticky node is in use, it will be cleared.
370 pub fn get(&mut self, key: KeyEvent) -> KeymapResult {
371 if key!(Esc) == key {
372 if !self.state.is_empty() {
373 return KeymapResult::new(
374 // Note that Esc is not included here
375 KeymapResultKind::Cancelled(self.state.drain(..).collect()),
376 self.sticky(),
377 );
378 }
379 self.sticky = None;
380 }
382 let first = self.state.get(0).unwrap_or(&key);
383 let trie_node = match self.sticky {
384 Some(ref trie) => Cow::Owned(KeyTrie::Node(trie.clone())),
385 None => Cow::Borrowed(&self.root),
386 };
388 let trie = match trie_node.search(&[*first]) {
389 Some(KeyTrie::Leaf(ref cmd)) => {
390 return KeymapResult::new(KeymapResultKind::Matched(cmd.clone()), self.sticky())
391 }
392 Some(KeyTrie::Sequence(ref cmds)) => {
393 return KeymapResult::new(
394 KeymapResultKind::MatchedSequence(cmds.clone()),
395 self.sticky(),
396 )
397 }
398 None => return KeymapResult::new(KeymapResultKind::NotFound, self.sticky()),
399 Some(t) => t,
400 };
402 self.state.push(key);
403 match trie.search(&self.state[1..]) {
404 Some(&KeyTrie::Node(ref map)) => {
405 if map.is_sticky {
406 self.state.clear();
407 self.sticky = Some(map.clone());
408 }
409 KeymapResult::new(KeymapResultKind::Pending(map.clone()), self.sticky())
410 }
411 Some(&KeyTrie::Leaf(ref cmd)) => {
412 self.state.clear();
413 return KeymapResult::new(KeymapResultKind::Matched(cmd.clone()), self.sticky());
414 }
415 Some(&KeyTrie::Sequence(ref cmds)) => {
416 self.state.clear();
417 KeymapResult::new(
418 KeymapResultKind::MatchedSequence(cmds.clone()),
419 self.sticky(),
420 )
421 }
422 None => KeymapResult::new(
423 KeymapResultKind::Cancelled(self.state.drain(..).collect()),
424 self.sticky(),
425 ),
426 }
427 }
429 pub fn merge(&mut self, other: Self) {
430 self.root.merge_nodes(other.root);
431 }
434impl Deref for Keymap {
435 type Target = KeyTrieNode;
437 fn deref(&self) -> &Self::Target {
438 self.root.node().unwrap()
439 }
442impl Default for Keymap {
443 fn default() -> Self {
444 Self::new(KeyTrie::Node(KeyTrieNode::default()))
445 }
448#[derive(Debug, Clone, PartialEq, Deserialize)]
449#[serde(transparent)]
450pub struct Keymaps(pub HashMap<Mode, Keymap>);
452impl Keymaps {
453 /// Returns list of keys waiting to be disambiguated in current mode.
454 pub fn pending(&self) -> &[KeyEvent] {
455 self.0
456 .values()
457 .find_map(|keymap| match keymap.pending().is_empty() {
458 true => None,
459 false => Some(keymap.pending()),
460 })
461 .unwrap_or_default()
462 }
465impl Deref for Keymaps {
466 type Target = HashMap<Mode, Keymap>;
468 fn deref(&self) -> &Self::Target {
469 &self.0
470 }
473impl DerefMut for Keymaps {
474 fn deref_mut(&mut self) -> &mut Self::Target {
475 &mut self.0
476 }
479impl Default for Keymaps {
480 fn default() -> Self {
481 let normal = keymap!({ "Normal mode"
482 "h" | "left" => move_char_left,
483 "j" | "down" => move_line_down,
484 "k" | "up" => move_line_up,
485 "l" | "right" => move_char_right,
487 "t" => find_till_char,
488 "f" => find_next_char,
489 "T" => till_prev_char,
490 "F" => find_prev_char,
491 "r" => replace,
492 "R" => replace_with_yanked,
493 "A-." => repeat_last_motion,
495 "~" => switch_case,
496 "`" => switch_to_lowercase,
497 "A-`" => switch_to_uppercase,
499 "home" => goto_line_start,
500 "end" => goto_line_end,
502 "w" => move_next_word_start,
503 "b" => move_prev_word_start,
504 "e" => move_next_word_end,
506 "W" => move_next_long_word_start,
507 "B" => move_prev_long_word_start,
508 "E" => move_next_long_word_end,
510 "v" => select_mode,
511 "G" => goto_line,
512 "g" => { "Goto"
513 "g" => goto_file_start,
514 "e" => goto_last_line,
515 "f" => goto_file,
516 "h" => goto_line_start,
517 "l" => goto_line_end,
518 "s" => goto_first_nonwhitespace,
519 "d" => goto_definition,
520 "y" => goto_type_definition,
521 "r" => goto_reference,
522 "i" => goto_implementation,
523 "t" => goto_window_top,
524 "c" => goto_window_center,
525 "b" => goto_window_bottom,
526 "a" => goto_last_accessed_file,
527 "m" => goto_last_modified_file,
528 "n" => goto_next_buffer,
529 "p" => goto_previous_buffer,
530 "." => goto_last_modification,
531 },
532 ":" => command_mode,
534 "i" => insert_mode,
535 "I" => prepend_to_line,
536 "a" => append_mode,
537 "A" => append_to_line,
538 "o" => open_below,
539 "O" => open_above,
541 "d" => delete_selection,
542 "A-d" => delete_selection_noyank,
543 "c" => change_selection,
544 "A-c" => change_selection_noyank,
546 "C" => copy_selection_on_next_line,
547 "A-C" => copy_selection_on_prev_line,
550 "s" => select_regex,
551 "A-s" => split_selection_on_newline,
552 "S" => split_selection,
553 ";" => collapse_selection,
554 "A-;" => flip_selections,
555 "A-k" => expand_selection,
556 "A-j" => shrink_selection,
557 "A-h" => select_prev_sibling,
558 "A-l" => select_next_sibling,
560 "%" => select_all,
561 "x" => extend_line,
562 "X" => extend_to_line_bounds,
563 // crop_to_whole_line
565 "m" => { "Match"
566 "m" => match_brackets,
567 "s" => surround_add,
568 "r" => surround_replace,
569 "d" => surround_delete,
570 "a" => select_textobject_around,
571 "i" => select_textobject_inner,
572 },
573 "[" => { "Left bracket"
574 "d" => goto_prev_diag,
575 "D" => goto_first_diag,
576 "space" => add_newline_above,
577 },
578 "]" => { "Right bracket"
579 "d" => goto_next_diag,
580 "D" => goto_last_diag,
581 "space" => add_newline_below,
582 },
584 "/" => search,
585 "?" => rsearch,
586 "n" => search_next,
587 "N" => search_prev,
588 "*" => search_selection,
590 "u" => undo,
591 "U" => redo,
592 "A-u" => earlier,
593 "A-U" => later,
595 "y" => yank,
596 // yank_all
597 "p" => paste_after,
598 // paste_all
599 "P" => paste_before,
601 "Q" => record_macro,
602 "q" => replay_macro,
604 ">" => indent,
605 "<" => unindent,
606 "=" => format_selections,
607 "J" => join_selections,
608 "K" => keep_selections,
609 "A-K" => remove_selections,
611 "," => keep_primary_selection,
612 "A-," => remove_primary_selection,
614 // "q" => record_macro,
615 // "Q" => replay_macro,
617 "&" => align_selections,
618 "_" => trim_selections,
620 "(" => rotate_selections_backward,
621 ")" => rotate_selections_forward,
622 "A-(" => rotate_selection_contents_backward,
623 "A-)" => rotate_selection_contents_forward,
625 "A-:" => ensure_selections_forward,
627 "esc" => normal_mode,
628 "C-b" | "pageup" => page_up,
629 "C-f" | "pagedown" => page_down,
630 "C-u" => half_page_up,
631 "C-d" => half_page_down,
633 "C-w" => { "Window"
634 "C-w" | "w" => rotate_view,
635 "C-s" | "s" => hsplit,
636 "C-v" | "v" => vsplit,
637 "f" => goto_file_hsplit,
638 "F" => goto_file_vsplit,
639 "C-q" | "q" => wclose,
640 "C-o" | "o" => wonly,
641 "C-h" | "h" | "left" => jump_view_left,
642 "C-j" | "j" | "down" => jump_view_down,
643 "C-k" | "k" | "up" => jump_view_up,
644 "C-l" | "l" | "right" => jump_view_right,
645 },
647 // move under <space>c
648 "C-c" => toggle_comments,
650 // z family for save/restore/combine from/to sels from register
652 "tab" => jump_forward, // tab == <C-i>
653 "C-o" => jump_backward,
654 "C-s" => save_selection,
656 "space" => { "Space"
657 "f" => file_picker,
658 "b" => buffer_picker,
659 "s" => symbol_picker,
660 "S" => workspace_symbol_picker,
661 "a" => code_action,
662 "'" => last_picker,
663 "w" => { "Window"
664 "C-w" | "w" => rotate_view,
665 "C-s" | "s" => hsplit,
666 "C-v" | "v" => vsplit,
667 "f" => goto_file_hsplit,
668 "F" => goto_file_vsplit,
669 "C-q" | "q" => wclose,
670 "C-o" | "o" => wonly,
671 "C-h" | "h" | "left" => jump_view_left,
672 "C-j" | "j" | "down" => jump_view_down,
673 "C-k" | "k" | "up" => jump_view_up,
674 "C-l" | "l" | "right" => jump_view_right,
675 },
676 "y" => yank_joined_to_clipboard,
677 "Y" => yank_main_selection_to_clipboard,
678 "p" => paste_clipboard_after,
679 "P" => paste_clipboard_before,
680 "R" => replace_selections_with_clipboard,
681 "/" => global_search,
682 "k" => hover,
683 "r" => rename_symbol,
684 },
685 "z" => { "View"
686 "z" | "c" => align_view_center,
687 "t" => align_view_top,
688 "b" => align_view_bottom,
689 "m" => align_view_middle,
690 "k" | "up" => scroll_up,
691 "j" | "down" => scroll_down,
692 "C-b" | "pageup" => page_up,
693 "C-f" | "pagedown" => page_down,
694 "C-u" => half_page_up,
695 "C-d" => half_page_down,
696 },
697 "Z" => { "View" sticky=true
698 "z" | "c" => align_view_center,
699 "t" => align_view_top,
700 "b" => align_view_bottom,
701 "m" => align_view_middle,
702 "k" | "up" => scroll_up,
703 "j" | "down" => scroll_down,
704 "C-b" | "pageup" => page_up,
705 "C-f" | "pagedown" => page_down,
706 "C-u" => half_page_up,
707 "C-d" => half_page_down,
708 },
710 "\"" => select_register,
711 "|" => shell_pipe,
712 "A-|" => shell_pipe_to,
713 "!" => shell_insert_output,
714 "A-!" => shell_append_output,
715 "$" => shell_keep_pipe,
716 "C-z" => suspend,
718 "C-a" => increment,
719 "C-x" => decrement,
720 });
721 let mut select = normal.clone();
722 select.merge_nodes(keymap!({ "Select mode"
723 "h" | "left" => extend_char_left,
724 "j" | "down" => extend_line_down,
725 "k" | "up" => extend_line_up,
726 "l" | "right" => extend_char_right,
728 "w" => extend_next_word_start,
729 "b" => extend_prev_word_start,
730 "e" => extend_next_word_end,
731 "W" => extend_next_long_word_start,
732 "B" => extend_prev_long_word_start,
733 "E" => extend_next_long_word_end,
735 "n" => extend_search_next,
736 "N" => extend_search_prev,
738 "t" => extend_till_char,
739 "f" => extend_next_char,
740 "T" => extend_till_prev_char,
741 "F" => extend_prev_char,
743 "home" => extend_to_line_start,
744 "end" => extend_to_line_end,
745 "esc" => exit_select_mode,
747 "v" => normal_mode,
748 }));
749 let insert = keymap!({ "Insert mode"
750 "esc" => normal_mode,
752 "backspace" => delete_char_backward,
753 "C-h" => delete_char_backward,
754 "del" => delete_char_forward,
755 "C-d" => delete_char_forward,
756 "ret" => insert_newline,
757 "C-j" => insert_newline,
758 "tab" => insert_tab,
759 "C-w" => delete_word_backward,
760 "A-backspace" => delete_word_backward,
761 "A-d" => delete_word_forward,
763 "left" => move_char_left,
764 "C-b" => move_char_left,
765 "down" => move_line_down,
766 "C-n" => move_line_down,
767 "up" => move_line_up,
768 "C-p" => move_line_up,
769 "right" => move_char_right,
770 "C-f" => move_char_right,
771 "A-b" => move_prev_word_end,
772 "A-left" => move_prev_word_end,
773 "A-f" => move_next_word_start,
774 "A-right" => move_next_word_start,
775 "A-<" => goto_file_start,
776 "A->" => goto_file_end,
777 "pageup" => page_up,
778 "pagedown" => page_down,
779 "home" => goto_line_start,
780 "C-a" => goto_line_start,
781 "end" => goto_line_end_newline,
782 "C-e" => goto_line_end_newline,
784 "C-k" => kill_to_line_end,
785 "C-u" => kill_to_line_start,
787 "C-x" => completion,
788 "C-r" => insert_register,
789 });
790 Self(hashmap!(
791 Mode::Normal => Keymap::new(normal),
792 Mode::Select => Keymap::new(select),
793 Mode::Insert => Keymap::new(insert),
794 ))
795 }
798/// Merge default config keys with user overwritten keys for custom user config.
799pub fn merge_keys(mut config: Config) -> Config {
800 let mut delta = std::mem::take(&mut config.keys);
801 for (mode, keys) in &mut *config.keys {
802 keys.merge(delta.remove(mode).unwrap_or_default())
803 }
804 config
807#[cfg(test)]
808mod tests {
809 use super::*;
811 #[test]
812 #[should_panic]
813 fn duplicate_keys_should_panic() {
814 keymap!({ "Normal mode"
815 "i" => normal_mode,
816 "i" => goto_definition,
817 });
818 }
820 #[test]
821 fn check_duplicate_keys_in_default_keymap() {
822 // will panic on duplicate keys, assumes that `Keymaps` uses keymap! macro
823 Keymaps::default();
824 }
826 #[test]
827 fn merge_partial_keys() {
828 let config = Config {
829 keys: Keymaps(hashmap! {
830 Mode::Normal => Keymap::new(
831 keymap!({ "Normal mode"
832 "i" => normal_mode,
833 "无" => insert_mode,
834 "z" => jump_backward,
835 "g" => { "Merge into goto mode"
836 "$" => goto_line_end,
837 "g" => delete_char_forward,
838 },
839 })
840 )
841 }),
842 ..Default::default()
843 };
844 let mut merged_config = merge_keys(config.clone());
845 assert_ne!(config, merged_config);
847 let keymap = merged_config.keys.0.get_mut(&Mode::Normal).unwrap();
848 assert_eq!(
849 keymap.get(key!('i')).kind,
850 KeymapResultKind::Matched(MappableCommand::normal_mode),
851 "Leaf should replace leaf"
852 );
853 assert_eq!(
854 keymap.get(key!('无')).kind,
855 KeymapResultKind::Matched(MappableCommand::insert_mode),
856 "New leaf should be present in merged keymap"
857 );
858 // Assumes that z is a node in the default keymap
859 assert_eq!(
860 keymap.get(key!('z')).kind,
861 KeymapResultKind::Matched(MappableCommand::jump_backward),
862 "Leaf should replace node"
863 );
864 // Assumes that `g` is a node in default keymap
865 assert_eq!(
866 keymap.root().search(&[key!('g'), key!('$')]).unwrap(),
867 &KeyTrie::Leaf(MappableCommand::goto_line_end),
868 "Leaf should be present in merged subnode"
869 );
870 // Assumes that `gg` is in default keymap
871 assert_eq!(
872 keymap.root().search(&[key!('g'), key!('g')]).unwrap(),
873 &KeyTrie::Leaf(MappableCommand::delete_char_forward),
874 "Leaf should replace old leaf in merged subnode"
875 );
876 // Assumes that `ge` is in default keymap
877 assert_eq!(
878 keymap.root().search(&[key!('g'), key!('e')]).unwrap(),
879 &KeyTrie::Leaf(MappableCommand::goto_last_line),
880 "Old leaves in subnode should be present in merged node"
881 );
883 assert!(merged_config.keys.0.get(&Mode::Normal).unwrap().len() > 1);
884 assert!(merged_config.keys.0.get(&Mode::Insert).unwrap().len() > 0);
885 }
887 #[test]
888 fn order_should_be_set() {
889 let config = Config {
890 keys: Keymaps(hashmap! {
891 Mode::Normal => Keymap::new(
892 keymap!({ "Normal mode"
893 "space" => { ""
894 "s" => { ""
895 "v" => vsplit,
896 "c" => hsplit,
897 },
898 },
899 })
900 )
901 }),
902 ..Default::default()
903 };
904 let mut merged_config = merge_keys(config.clone());
905 assert_ne!(config, merged_config);
906 let keymap = merged_config.keys.0.get_mut(&Mode::Normal).unwrap();
907 // Make sure mapping works
908 assert_eq!(
909 keymap
910 .root()
911 .search(&[key!(' '), key!('s'), key!('v')])
912 .unwrap(),
913 &KeyTrie::Leaf(MappableCommand::vsplit),
914 "Leaf should be present in merged subnode"
915 );
916 // Make sure an order was set during merge
917 let node = keymap.root().search(&[crate::key!(' ')]).unwrap();
918 assert!(!node.node().unwrap().order().is_empty())
919 }
921 #[test]
922 fn aliased_modes_are_same_in_default_keymap() {
923 let keymaps = Keymaps::default();
924 let root = keymaps.get(&Mode::Normal).unwrap().root();
925 assert_eq!(
926 root.search(&[key!(' '), key!('w')]).unwrap(),
927 root.search(&["C-w".parse::<KeyEvent>().unwrap()]).unwrap(),
928 "Mismatch for window mode on `Space-w` and `Ctrl-w`"
929 );
930 assert_eq!(
931 root.search(&[key!('z')]).unwrap(),
932 root.search(&[key!('Z')]).unwrap(),
933 "Mismatch for view mode on `z` and `Z`"
934 );
935 }