on 2158366 help sign in
helix/helix-term/src/keymap.rs
641 lines 19 kB history raw
1pub use crate::commands::Command;
2use crate::config::Config;
3use helix_core::hashmap;
4use helix_view::{document::Mode, info::Info, input::KeyEvent};
5use serde::Deserialize;
6use std::{
7 collections::HashMap,
8 ops::{Deref, DerefMut},
9};
11#[macro_export]
12macro_rules! key {
13 ($key:ident) => {
14 KeyEvent {
15 code: ::helix_view::keyboard::KeyCode::$key,
16 modifiers: ::helix_view::keyboard::KeyModifiers::NONE,
17 }
18 };
19 ($($ch:tt)*) => {
20 KeyEvent {
21 code: ::helix_view::keyboard::KeyCode::Char($($ch)*),
22 modifiers: ::helix_view::keyboard::KeyModifiers::NONE,
23 }
24 };
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]
44macro_rules! keymap {
45 (@trie $cmd:ident) => {
46 $crate::keymap::KeyTrie::Leaf($crate::commands::Command::$cmd)
47 };
49 (@trie
50 { $label:literal $($($key:literal)|+ => $value:tt,)+ }
51 ) => {
52 keymap!({ $label $($($key)|+ => $value,)+ })
53 };
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))
74 }
75 };
78#[derive(Debug, Clone, Deserialize)]
79pub 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>,
89impl 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,
95 }
96 }
98 pub fn name(&self) -> &str {
99 &self.name
100 }
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 }
116 for &key in self.map.keys() {
117 if !self.order.contains(&key) {
118 self.order.push(key);
119 }
120 }
121 }
124impl 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::new(node.name(), body)
149 }
152impl Default for KeyTrieNode {
153 fn default() -> Self {
154 Self::new("", HashMap::new(), Vec::new())
155 }
158impl PartialEq for KeyTrieNode {
159 fn eq(&self, other: &Self) -> bool {
160 self.map == other.map
161 }
164impl Deref for KeyTrieNode {
165 type Target = HashMap<KeyEvent, KeyTrie>;
167 fn deref(&self) -> &Self::Target {
168 &self.map
169 }
172impl DerefMut for KeyTrieNode {
173 fn deref_mut(&mut self) -> &mut Self::Target {
174 &mut self.map
175 }
178#[derive(Debug, Clone, PartialEq, Deserialize)]
179#[serde(untagged)]
180pub enum KeyTrie {
181 Leaf(Command),
182 Node(KeyTrieNode),
185impl KeyTrie {
186 pub fn node(&self) -> Option<&KeyTrieNode> {
187 match *self {
188 KeyTrie::Node(ref node) => Some(node),
189 KeyTrie::Leaf(_) => None,
190 }
191 }
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 }
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 }
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 }
220#[derive(Debug, Clone, PartialEq)]
221pub 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>),
232#[derive(Debug, Clone, PartialEq, Deserialize)]
233pub struct Keymap {
234 /// Always a Node
235 #[serde(flatten)]
236 root: KeyTrie,
237 /// Stores pending keys waiting for the next key
238 #[serde(skip)]
239 state: Vec<KeyEvent>,
242impl Keymap {
243 pub fn new(root: KeyTrie) -> Self {
244 Keymap {
245 root,
246 state: Vec::new(),
247 }
248 }
250 pub fn root(&self) -> &KeyTrie {
251 &self.root
252 }
254 /// Returns list of keys waiting to be disambiguated.
255 pub fn pending(&self) -> &[KeyEvent] {
256 &self.state
257 }
259 /// Lookup `key` in the keymap to try and find a command to execute
260 pub fn get(&mut self, key: KeyEvent) -> KeymapResult {
261 let &first = self.state.get(0).unwrap_or(&key);
262 let trie = match self.root.search(&[first]) {
263 Some(&KeyTrie::Leaf(cmd)) => return KeymapResult::Matched(cmd),
264 None => return KeymapResult::NotFound,
265 Some(t) => t,
266 };
267 self.state.push(key);
268 match trie.search(&self.state[1..]) {
269 Some(&KeyTrie::Node(ref map)) => KeymapResult::Pending(map.clone()),
270 Some(&KeyTrie::Leaf(command)) => {
271 self.state.clear();
272 KeymapResult::Matched(command)
273 }
274 None => KeymapResult::Cancelled(self.state.drain(..).collect()),
275 }
276 }
278 pub fn merge(&mut self, other: Self) {
279 self.root.merge_nodes(other.root);
280 }
283impl Deref for Keymap {
284 type Target = KeyTrieNode;
286 fn deref(&self) -> &Self::Target {
287 self.root.node().unwrap()
288 }
291impl Default for Keymap {
292 fn default() -> Self {
293 Self::new(KeyTrie::Node(KeyTrieNode::default()))
294 }
297#[derive(Debug, Clone, PartialEq, Deserialize)]
298#[serde(transparent)]
299pub struct Keymaps(pub HashMap<Mode, Keymap>);
301impl Keymaps {
302 /// Returns list of keys waiting to be disambiguated in current mode.
303 pub fn pending(&self) -> &[KeyEvent] {
304 self.0
305 .values()
306 .find_map(|keymap| match keymap.pending().is_empty() {
307 true => None,
308 false => Some(keymap.pending()),
309 })
310 .unwrap_or_default()
311 }
314impl Deref for Keymaps {
315 type Target = HashMap<Mode, Keymap>;
317 fn deref(&self) -> &Self::Target {
318 &self.0
319 }
322impl DerefMut for Keymaps {
323 fn deref_mut(&mut self) -> &mut Self::Target {
324 &mut self.0
325 }
328impl Default for Keymaps {
329 fn default() -> Keymaps {
330 let normal = keymap!({ "Normal mode"
331 "h" | "left" => move_char_left,
332 "j" | "down" => move_line_down,
333 "k" | "up" => move_line_up,
334 "l" | "right" => move_char_right,
336 "t" => find_till_char,
337 "f" => find_next_char,
338 "T" => till_prev_char,
339 "F" => find_prev_char,
340 "r" => replace,
341 "R" => replace_with_yanked,
343 "~" => switch_case,
344 "`" => switch_to_lowercase,
345 "A-`" => switch_to_uppercase,
347 "home" => goto_line_start,
348 "end" => goto_line_end,
350 "w" => move_next_word_start,
351 "b" => move_prev_word_start,
352 "e" => move_next_word_end,
354 "W" => move_next_long_word_start,
355 "B" => move_prev_long_word_start,
356 "E" => move_next_long_word_end,
358 "v" => select_mode,
359 "G" => goto_line,
360 "g" => { "Goto"
361 "g" => goto_file_start,
362 "e" => goto_last_line,
363 "h" => goto_line_start,
364 "l" => goto_line_end,
365 "s" => goto_first_nonwhitespace,
366 "d" => goto_definition,
367 "y" => goto_type_definition,
368 "r" => goto_reference,
369 "i" => goto_implementation,
370 "t" => goto_window_top,
371 "m" => goto_window_middle,
372 "b" => goto_window_bottom,
373 "a" => goto_last_accessed_file,
374 },
375 ":" => command_mode,
377 "i" => insert_mode,
378 "I" => prepend_to_line,
379 "a" => append_mode,
380 "A" => append_to_line,
381 "o" => open_below,
382 "O" => open_above,
383 // [<space> ]<space> equivalents too (add blank new line, no edit)
385 "d" => delete_selection,
386 // TODO: also delete without yanking
387 "c" => change_selection,
388 // TODO: also change delete without yanking
390 "C" => copy_selection_on_next_line,
391 "A-C" => copy_selection_on_prev_line,
394 "s" => select_regex,
395 "A-s" => split_selection_on_newline,
396 "S" => split_selection,
397 ";" => collapse_selection,
398 "A-;" => flip_selections,
399 "%" => select_all,
400 "x" => extend_line,
401 "X" => extend_to_line_bounds,
402 // crop_to_whole_line
404 "m" => { "Match"
405 "m" => match_brackets,
406 "s" => surround_add,
407 "r" => surround_replace,
408 "d" => surround_delete,
409 "a" => select_textobject_around,
410 "i" => select_textobject_inner,
411 },
412 "[" => { "Left bracket"
413 "d" => goto_prev_diag,
414 "D" => goto_first_diag,
415 },
416 "]" => { "Right bracket"
417 "d" => goto_next_diag,
418 "D" => goto_last_diag,
419 },
421 "/" => search,
422 // ? for search_reverse
423 "n" => search_next,
424 "N" => extend_search_next,
425 // N for search_prev
426 "*" => search_selection,
428 "u" => undo,
429 "U" => redo,
431 "y" => yank,
432 // yank_all
433 "p" => paste_after,
434 // paste_all
435 "P" => paste_before,
437 ">" => indent,
438 "<" => unindent,
439 "=" => format_selections,
440 "J" => join_selections,
441 // TODO: conflicts hover/doc
442 "K" => keep_selections,
443 // TODO: and another method for inverse
445 // TODO: clashes with space mode
446 "space" => keep_primary_selection,
448 // "q" => record_macro,
449 // "Q" => replay_macro,
451 // & align selections
452 // _ trim selections
454 "(" => rotate_selections_backward,
455 ")" => rotate_selections_forward,
456 "A-(" => rotate_selection_contents_backward,
457 "A-)" => rotate_selection_contents_forward,
459 "esc" => normal_mode,
460 "C-b" | "pageup" => page_up,
461 "C-f" | "pagedown" => page_down,
462 "C-u" => half_page_up,
463 "C-d" => half_page_down,
465 "C-w" => { "Window"
466 "C-w" | "w" => rotate_view,
467 "C-h" | "h" => hsplit,
468 "C-v" | "v" => vsplit,
469 "C-q" | "q" => wclose,
470 },
472 // move under <space>c
473 "C-c" => toggle_comments,
474 "K" => hover,
476 // z family for save/restore/combine from/to sels from register
478 "tab" => jump_forward, // tab == <C-i>
479 "C-o" => jump_backward,
480 // "C-s" => save_selection,
482 "space" => { "Space"
483 "f" => file_picker,
484 "b" => buffer_picker,
485 "s" => symbol_picker,
486 "a" => code_action,
487 "'" => last_picker,
488 "d" => { "Debug"
489 "s" => dap_start,
490 "b" => dap_toggle_breakpoint,
491 "r" => dap_run,
492 "c" => dap_continue,
493 "h" => dap_pause,
494 "j" => dap_in,
495 "k" => dap_out,
496 "l" => dap_next,
497 "v" => dap_variables,
498 "t" => dap_terminate,
499 },
500 "w" => { "Window"
501 "C-w" | "w" => rotate_view,
502 "C-h" | "h" => hsplit,
503 "C-v" | "v" => vsplit,
504 "C-q" | "q" => wclose,
505 },
506 "y" => yank_joined_to_clipboard,
507 "Y" => yank_main_selection_to_clipboard,
508 "p" => paste_clipboard_after,
509 "P" => paste_clipboard_before,
510 "R" => replace_selections_with_clipboard,
511 "space" => keep_primary_selection,
512 },
513 "z" => { "View"
514 "z" | "c" => align_view_center,
515 "t" => align_view_top,
516 "b" => align_view_bottom,
517 "m" => align_view_middle,
518 "k" => scroll_up,
519 "j" => scroll_down,
520 },
522 "\"" => select_register,
523 "C-z" => suspend,
524 });
525 let mut select = normal.clone();
526 select.merge_nodes(keymap!({ "Select mode"
527 "h" | "left" => extend_char_left,
528 "j" | "down" => extend_line_down,
529 "k" | "up" => extend_line_up,
530 "l" | "right" => extend_char_right,
532 "w" => extend_next_word_start,
533 "b" => extend_prev_word_start,
534 "e" => extend_next_word_end,
536 "t" => extend_till_char,
537 "f" => extend_next_char,
538 "T" => extend_till_prev_char,
539 "F" => extend_prev_char,
541 "home" => goto_line_start,
542 "end" => goto_line_end,
543 "esc" => exit_select_mode,
544 }));
545 let insert = keymap!({ "Insert mode"
546 "esc" => normal_mode,
548 "backspace" => delete_char_backward,
549 "del" => delete_char_forward,
550 "ret" => insert_newline,
551 "tab" => insert_tab,
552 "C-w" => delete_word_backward,
554 "left" => move_char_left,
555 "down" => move_line_down,
556 "up" => move_line_up,
557 "right" => move_char_right,
558 "pageup" => page_up,
559 "pagedown" => page_down,
560 "home" => goto_line_start,
561 "end" => goto_line_end_newline,
563 "C-x" => completion,
564 });
565 Keymaps(hashmap!(
566 Mode::Normal => Keymap::new(normal),
567 Mode::Select => Keymap::new(select),
568 Mode::Insert => Keymap::new(insert),
569 ))
570 }
573/// Merge default config keys with user overwritten keys for custom user config.
574pub fn merge_keys(mut config: Config) -> Config {
575 let mut delta = std::mem::take(&mut config.keys);
576 for (mode, keys) in &mut *config.keys {
577 keys.merge(delta.remove(mode).unwrap_or_default())
578 }
579 config
582#[test]
583fn merge_partial_keys() {
584 let config = Config {
585 keys: Keymaps(hashmap! {
586 Mode::Normal => Keymap::new(
587 keymap!({ "Normal mode"
588 "i" => normal_mode,
589 "无" => insert_mode,
590 "z" => jump_backward,
591 "g" => { "Merge into goto mode"
592 "$" => goto_line_end,
593 "g" => delete_char_forward,
594 },
595 })
596 )
597 }),
598 ..Default::default()
599 };
600 let mut merged_config = merge_keys(config.clone());
601 assert_ne!(config, merged_config);
603 let keymap = merged_config.keys.0.get_mut(&Mode::Normal).unwrap();
604 assert_eq!(
605 keymap.get(key!('i')),
606 KeymapResult::Matched(Command::normal_mode),
607 "Leaf should replace leaf"
608 );
609 assert_eq!(
610 keymap.get(key!('无')),
611 KeymapResult::Matched(Command::insert_mode),
612 "New leaf should be present in merged keymap"
613 );
614 // Assumes that z is a node in the default keymap
615 assert_eq!(
616 keymap.get(key!('z')),
617 KeymapResult::Matched(Command::jump_backward),
618 "Leaf should replace node"
619 );
620 // Assumes that `g` is a node in default keymap
621 assert_eq!(
622 keymap.root().search(&[key!('g'), key!('$')]).unwrap(),
623 &KeyTrie::Leaf(Command::goto_line_end),
624 "Leaf should be present in merged subnode"
625 );
626 // Assumes that `gg` is in default keymap
627 assert_eq!(
628 keymap.root().search(&[key!('g'), key!('g')]).unwrap(),
629 &KeyTrie::Leaf(Command::delete_char_forward),
630 "Leaf should replace old leaf in merged subnode"
631 );
632 // Assumes that `ge` is in default keymap
633 assert_eq!(
634 keymap.root().search(&[key!('g'), key!('e')]).unwrap(),
635 &KeyTrie::Leaf(Command::goto_last_line),
636 "Old leaves in subnode should be present in merged node"
637 );
639 assert!(merged_config.keys.0.get(&Mode::Normal).unwrap().len() > 1);
640 assert!(merged_config.keys.0.get(&Mode::Insert).unwrap().len() > 0);