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`
This commit is contained in:
parent
bf70cfd050
commit
d131a9dd0e
3 changed files with 30 additions and 3 deletions
|
@ -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…
Add table
Reference in a new issue