Allow keys to be mapped to sequences of commands (#589)

* Allow keys to be mapped to sequences of commands

* Handle `Sequence` at the start of `Keymap::get`

* Use `"[Multiple commands]"` as command sequence doc

* Add command sequence example to `remapping.md`
imgbot
Omnikar 3 years ago committed by GitHub
parent bf70cfd050
commit d131a9dd0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -15,6 +15,7 @@ a = "move_char_left" # Maps the 'a' key to the move_char_left command
w = "move_line_up" # Maps the 'w' key move_line_up w = "move_line_up" # Maps the 'w' key move_line_up
"C-S-esc" = "extend_line" # Maps Control-Shift-Escape to extend_line "C-S-esc" = "extend_line" # Maps Control-Shift-Escape to extend_line
g = { a = "code_action" } # Maps `ga` to show possible code actions g = { a = "code_action" } # Maps `ga` to show possible code actions
"ret" = ["open_below", "normal_mode"] # Maps the enter key to open_below then re-enter normal mode
[keys.insert] [keys.insert]
"A-x" = "normal_mode" # Maps Alt-X to enter normal mode "A-x" = "normal_mode" # Maps Alt-X to enter normal mode

@ -85,6 +85,10 @@ macro_rules! keymap {
keymap!({ $label $(sticky=$sticky)? $($($key)|+ => $value,)+ }) 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,)+ } { $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
) => { ) => {
@ -180,6 +184,7 @@ impl KeyTrieNode {
cmd.doc() cmd.doc()
} }
KeyTrie::Node(n) => n.name(), KeyTrie::Node(n) => n.name(),
KeyTrie::Sequence(_) => "[Multiple commands]",
}; };
match body.iter().position(|(d, _)| d == &desc) { match body.iter().position(|(d, _)| d == &desc) {
Some(pos) => { Some(pos) => {
@ -240,6 +245,7 @@ impl DerefMut for KeyTrieNode {
#[serde(untagged)] #[serde(untagged)]
pub enum KeyTrie { pub enum KeyTrie {
Leaf(Command), Leaf(Command),
Sequence(Vec<Command>),
Node(KeyTrieNode), Node(KeyTrieNode),
} }
@ -247,14 +253,14 @@ impl KeyTrie {
pub fn node(&self) -> Option<&KeyTrieNode> { pub fn node(&self) -> Option<&KeyTrieNode> {
match *self { match *self {
KeyTrie::Node(ref node) => Some(node), KeyTrie::Node(ref node) => Some(node),
KeyTrie::Leaf(_) => None, KeyTrie::Leaf(_) | KeyTrie::Sequence(_) => None,
} }
} }
pub fn node_mut(&mut self) -> Option<&mut KeyTrieNode> { pub fn node_mut(&mut self) -> Option<&mut KeyTrieNode> {
match *self { match *self {
KeyTrie::Node(ref mut node) => Some(node), KeyTrie::Node(ref mut node) => Some(node),
KeyTrie::Leaf(_) => None, KeyTrie::Leaf(_) | KeyTrie::Sequence(_) => None,
} }
} }
@ -271,7 +277,7 @@ impl KeyTrie {
trie = match trie { trie = match trie {
KeyTrie::Node(map) => map.get(key), KeyTrie::Node(map) => map.get(key),
// leaf encountered while keys left to process // leaf encountered while keys left to process
KeyTrie::Leaf(_) => None, KeyTrie::Leaf(_) | KeyTrie::Sequence(_) => None,
}? }?
} }
Some(trie) Some(trie)
@ -283,6 +289,8 @@ pub enum KeymapResultKind {
/// Needs more keys to execute a command. Contains valid keys for next keystroke. /// Needs more keys to execute a command. Contains valid keys for next keystroke.
Pending(KeyTrieNode), Pending(KeyTrieNode),
Matched(Command), Matched(Command),
/// Matched a sequence of commands to execute.
MatchedSequence(Vec<Command>),
/// Key was not found in the root keymap /// Key was not found in the root keymap
NotFound, NotFound,
/// Key is invalid in combination with previous keys. Contains keys leading upto /// Key is invalid in combination with previous keys. Contains keys leading upto
@ -365,6 +373,12 @@ impl Keymap {
Some(&KeyTrie::Leaf(cmd)) => { Some(&KeyTrie::Leaf(cmd)) => {
return KeymapResult::new(KeymapResultKind::Matched(cmd), self.sticky()) 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()), None => return KeymapResult::new(KeymapResultKind::NotFound, self.sticky()),
Some(t) => t, Some(t) => t,
}; };
@ -382,6 +396,13 @@ impl Keymap {
self.state.clear(); self.state.clear();
return KeymapResult::new(KeymapResultKind::Matched(cmd), self.sticky()); 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( None => KeymapResult::new(
KeymapResultKind::Cancelled(self.state.drain(..).collect()), KeymapResultKind::Cancelled(self.state.drain(..).collect()),
self.sticky(), self.sticky(),

@ -695,6 +695,11 @@ impl EditorView {
match &key_result.kind { match &key_result.kind {
KeymapResultKind::Matched(command) => command.execute(cxt), KeymapResultKind::Matched(command) => command.execute(cxt),
KeymapResultKind::Pending(node) => self.autoinfo = Some(node.infobox()), KeymapResultKind::Pending(node) => self.autoinfo = Some(node.infobox()),
KeymapResultKind::MatchedSequence(commands) => {
for command in commands {
command.execute(cxt);
}
}
KeymapResultKind::NotFound | KeymapResultKind::Cancelled(_) => return Some(key_result), KeymapResultKind::NotFound | KeymapResultKind::Cancelled(_) => return Some(key_result),
} }
None None

Loading…
Cancel
Save