aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src/keymap.rs
diff options
context:
space:
mode:
authorBlaž Hrastnik2021-11-21 11:06:45 +0000
committerBlaž Hrastnik2021-11-21 11:06:45 +0000
commitd1854d8e6af07cd78ab6c24c859a4471afb3514e (patch)
tree301e4212e7fc88dd5f626f884bd78b700cf3e4a6 /helix-term/src/keymap.rs
parent8b85903116fdfdc177bf2ca171831674144de70a (diff)
parentb95c9470de9f9199f109fdbfb6ec9a951fbe8866 (diff)
Merge remote-tracking branch 'origin/master' into debug
Diffstat (limited to 'helix-term/src/keymap.rs')
-rw-r--r--helix-term/src/keymap.rs171
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`"
+ );
+ }
}