diff options
Diffstat (limited to 'helix-term/src/keymap.rs')
-rw-r--r-- | helix-term/src/keymap.rs | 171 |
1 files changed, 152 insertions, 19 deletions
diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index fd56a5c9..42a62fc2 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -25,6 +25,54 @@ macro_rules! key { }; } +#[macro_export] +macro_rules! shift { + ($key:ident) => { + ::helix_view::input::KeyEvent { + code: ::helix_view::keyboard::KeyCode::$key, + modifiers: ::helix_view::keyboard::KeyModifiers::SHIFT, + } + }; + ($($ch:tt)*) => { + ::helix_view::input::KeyEvent { + code: ::helix_view::keyboard::KeyCode::Char($($ch)*), + modifiers: ::helix_view::keyboard::KeyModifiers::SHIFT, + } + }; +} + +#[macro_export] +macro_rules! ctrl { + ($key:ident) => { + ::helix_view::input::KeyEvent { + code: ::helix_view::keyboard::KeyCode::$key, + modifiers: ::helix_view::keyboard::KeyModifiers::CONTROL, + } + }; + ($($ch:tt)*) => { + ::helix_view::input::KeyEvent { + code: ::helix_view::keyboard::KeyCode::Char($($ch)*), + modifiers: ::helix_view::keyboard::KeyModifiers::CONTROL, + } + }; +} + +#[macro_export] +macro_rules! alt { + ($key:ident) => { + ::helix_view::input::KeyEvent { + code: ::helix_view::keyboard::KeyCode::$key, + modifiers: ::helix_view::keyboard::KeyModifiers::ALT, + } + }; + ($($ch:tt)*) => { + ::helix_view::input::KeyEvent { + code: ::helix_view::keyboard::KeyCode::Char($($ch)*), + modifiers: ::helix_view::keyboard::KeyModifiers::ALT, + } + }; +} + /// Macro for defining the root of a `Keymap` object. Example: /// /// ``` @@ -53,6 +101,10 @@ macro_rules! keymap { keymap!({ $label $(sticky=$sticky)? $($($key)|+ => $value,)+ }) }; + (@trie [$($cmd:ident),* $(,)?]) => { + $crate::keymap::KeyTrie::Sequence(vec![$($crate::commands::Command::$cmd),*]) + }; + ( { $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ } ) => { @@ -64,10 +116,11 @@ macro_rules! keymap { $( $( let _key = $key.parse::<::helix_view::input::KeyEvent>().unwrap(); - _map.insert( + let _duplicate = _map.insert( _key, keymap!(@trie $value) ); + debug_assert!(_duplicate.is_none(), "Duplicate key found: {:?}", _duplicate.unwrap()); _order.push(_key); )+ )* @@ -147,6 +200,7 @@ impl KeyTrieNode { cmd.doc() } KeyTrie::Node(n) => n.name(), + KeyTrie::Sequence(_) => "[Multiple commands]", }; match body.iter().position(|(d, _)| d == &desc) { Some(pos) => { @@ -207,6 +261,7 @@ impl DerefMut for KeyTrieNode { #[serde(untagged)] pub enum KeyTrie { Leaf(Command), + Sequence(Vec<Command>), Node(KeyTrieNode), } @@ -214,14 +269,14 @@ impl KeyTrie { pub fn node(&self) -> Option<&KeyTrieNode> { match *self { KeyTrie::Node(ref node) => Some(node), - KeyTrie::Leaf(_) => None, + KeyTrie::Leaf(_) | KeyTrie::Sequence(_) => None, } } pub fn node_mut(&mut self) -> Option<&mut KeyTrieNode> { match *self { KeyTrie::Node(ref mut node) => Some(node), - KeyTrie::Leaf(_) => None, + KeyTrie::Leaf(_) | KeyTrie::Sequence(_) => None, } } @@ -238,7 +293,7 @@ impl KeyTrie { trie = match trie { KeyTrie::Node(map) => map.get(key), // leaf encountered while keys left to process - KeyTrie::Leaf(_) => None, + KeyTrie::Leaf(_) | KeyTrie::Sequence(_) => None, }? } Some(trie) @@ -250,6 +305,8 @@ pub enum KeymapResultKind { /// Needs more keys to execute a command. Contains valid keys for next keystroke. Pending(KeyTrieNode), Matched(Command), + /// Matched a sequence of commands to execute. + MatchedSequence(Vec<Command>), /// Key was not found in the root keymap NotFound, /// Key is invalid in combination with previous keys. Contains keys leading upto @@ -332,6 +389,12 @@ impl Keymap { Some(&KeyTrie::Leaf(cmd)) => { return KeymapResult::new(KeymapResultKind::Matched(cmd), self.sticky()) } + Some(&KeyTrie::Sequence(ref cmds)) => { + return KeymapResult::new( + KeymapResultKind::MatchedSequence(cmds.clone()), + self.sticky(), + ) + } None => return KeymapResult::new(KeymapResultKind::NotFound, self.sticky()), Some(t) => t, }; @@ -349,6 +412,13 @@ impl Keymap { self.state.clear(); return KeymapResult::new(KeymapResultKind::Matched(cmd), self.sticky()); } + Some(&KeyTrie::Sequence(ref cmds)) => { + self.state.clear(); + KeymapResult::new( + KeymapResultKind::MatchedSequence(cmds.clone()), + self.sticky(), + ) + } None => KeymapResult::new( KeymapResultKind::Cancelled(self.state.drain(..).collect()), self.sticky(), @@ -455,6 +525,7 @@ impl Default for Keymaps { "a" => goto_last_accessed_file, "n" => goto_next_buffer, "p" => goto_previous_buffer, + "." => goto_last_modification, }, ":" => command_mode, @@ -511,6 +582,8 @@ impl Default for Keymaps { "u" => undo, "U" => redo, + "A-u" => earlier, + "A-U" => later, "y" => yank, // yank_all @@ -523,7 +596,7 @@ impl Default for Keymaps { "=" => format_selections, "J" => join_selections, "K" => keep_selections, - // TODO: and another method for inverse + "A-K" => remove_selections, "," => keep_primary_selection, "A-," => remove_primary_selection, @@ -532,7 +605,7 @@ impl Default for Keymaps { // "Q" => replay_macro, // & align selections - // _ trim selections + "_" => trim_selections, "(" => rotate_selections_backward, ")" => rotate_selections_forward, @@ -550,6 +623,7 @@ impl Default for Keymaps { "C-s" | "s" => hsplit, "C-v" | "v" => vsplit, "C-q" | "q" => wclose, + "C-o" | "o" => wonly, "C-h" | "h" | "left" => jump_view_left, "C-j" | "j" | "down" => jump_view_down, "C-k" | "k" | "up" => jump_view_up, @@ -569,6 +643,7 @@ impl Default for Keymaps { "f" => file_picker, "b" => buffer_picker, "s" => symbol_picker, + "S" => workspace_symbol_picker, "a" => code_action, "'" => last_picker, "d" => { "Debug" sticky=true @@ -593,9 +668,14 @@ impl Default for Keymaps { }, "w" => { "Window" "C-w" | "w" => rotate_view, - "C-h" | "h" => hsplit, + "C-s" | "s" => hsplit, "C-v" | "v" => vsplit, "C-q" | "q" => wclose, + "C-o" | "o" => wonly, + "C-h" | "h" | "left" => jump_view_left, + "C-j" | "j" | "down" => jump_view_down, + "C-k" | "k" | "up" => jump_view_up, + "C-l" | "l" | "right" => jump_view_right, }, "y" => yank_joined_to_clipboard, "Y" => yank_main_selection_to_clipboard, @@ -604,30 +684,31 @@ impl Default for Keymaps { "R" => replace_selections_with_clipboard, "/" => global_search, "k" => hover, + "r" => rename_symbol, }, "z" => { "View" "z" | "c" => align_view_center, "t" => align_view_top, "b" => align_view_bottom, "m" => align_view_middle, - "k" => scroll_up, - "j" => scroll_down, - "b" => page_up, - "f" => page_down, - "u" => half_page_up, - "d" => half_page_down, + "k" | "up" => scroll_up, + "j" | "down" => scroll_down, + "C-b" | "pageup" => page_up, + "C-f" | "pagedown" => page_down, + "C-u" => half_page_up, + "C-d" => half_page_down, }, "Z" => { "View" sticky=true "z" | "c" => align_view_center, "t" => align_view_top, "b" => align_view_bottom, "m" => align_view_middle, - "k" => scroll_up, - "j" => scroll_down, - "b" => page_up, - "f" => page_down, - "u" => half_page_up, - "d" => half_page_down, + "k" | "up" => scroll_up, + "j" | "down" => scroll_down, + "C-b" | "pageup" => page_up, + "C-f" | "pagedown" => page_down, + "C-u" => half_page_up, + "C-d" => half_page_down, }, "\"" => select_register, @@ -637,6 +718,9 @@ impl Default for Keymaps { "A-!" => shell_append_output, "$" => shell_keep_pipe, "C-z" => suspend, + + "C-a" => increment, + "C-x" => decrement, }); let mut select = normal.clone(); select.merge_nodes(keymap!({ "Select mode" @@ -670,21 +754,38 @@ impl Default for Keymaps { "esc" => normal_mode, "backspace" => delete_char_backward, + "C-h" => delete_char_backward, "del" => delete_char_forward, + "C-d" => delete_char_forward, "ret" => insert_newline, "tab" => insert_tab, "C-w" => delete_word_backward, + "A-d" => delete_word_forward, "left" => move_char_left, + "C-b" => move_char_left, "down" => move_line_down, + "C-n" => move_line_down, "up" => move_line_up, + "C-p" => move_line_up, "right" => move_char_right, + "C-f" => move_char_right, + "A-b" => move_prev_word_end, + "A-left" => move_prev_word_end, + "A-f" => move_next_word_start, + "A-right" => move_next_word_start, "pageup" => page_up, "pagedown" => page_down, "home" => goto_line_start, + "C-a" => goto_line_start, "end" => goto_line_end_newline, + "C-e" => goto_line_end_newline, + + "C-k" => kill_to_line_end, + "C-u" => kill_to_line_start, "C-x" => completion, + "C-r" => insert_register, }); Keymaps(hashmap!( Mode::Normal => Keymap::new(normal), @@ -706,6 +807,22 @@ pub fn merge_keys(mut config: Config) -> Config { #[cfg(test)] mod tests { use super::*; + + #[test] + #[should_panic] + fn duplicate_keys_should_panic() { + keymap!({ "Normal mode" + "i" => normal_mode, + "i" => goto_definition, + }); + } + + #[test] + fn check_duplicate_keys_in_default_keymap() { + // will panic on duplicate keys, assumes that `Keymaps` uses keymap! macro + Keymaps::default(); + } + #[test] fn merge_partial_keys() { let config = Config { @@ -800,4 +917,20 @@ mod tests { let node = keymap.root().search(&[crate::key!(' ')]).unwrap(); assert!(!node.node().unwrap().order().is_empty()) } + + #[test] + fn aliased_modes_are_same_in_default_keymap() { + let keymaps = Keymaps::default(); + let root = keymaps.get(&Mode::Normal).unwrap().root(); + assert_eq!( + root.search(&[key!(' '), key!('w')]).unwrap(), + root.search(&["C-w".parse::<KeyEvent>().unwrap()]).unwrap(), + "Mismatch for window mode on `Space-w` and `Ctrl-w`" + ); + assert_eq!( + root.search(&[key!('z')]).unwrap(), + root.search(&[key!('Z')]).unwrap(), + "Mismatch for view mode on `z` and `Z`" + ); + } } |