on master help sign in
Allow keys to be mapped to sequences of commands (#589)
d131a9dOmnikar
book/src/remapping.md+1
@@ -15,6 +15,7 @@ a = "move_char_left" # Maps the 'a' key to the move_char_left command
15w = "move_line_up" # Maps the 'w' key move_line_up
15w = "move_line_up" # Maps the 'w' key move_line_up
16"C-S-esc" = "extend_line" # Maps Control-Shift-Escape to extend_line
16"C-S-esc" = "extend_line" # Maps Control-Shift-Escape to extend_line
17g = { a = "code_action" } # Maps `ga` to show possible code actions
17g = { a = "code_action" } # Maps `ga` to show possible code actions
18+"ret" = ["open_below", "normal_mode"] # Maps the enter key to open_below then re-enter normal mode
18
19
19[keys.insert]
20[keys.insert]
20"A-x" = "normal_mode" # Maps Alt-X to enter normal mode
21"A-x" = "normal_mode" # Maps Alt-X to enter normal mode
helix-term/src/keymap.rs+24−3
@@ -85,6 +85,10 @@ macro_rules! keymap {
85 keymap!({ $label $(sticky=$sticky)? $($($key)|+ => $value,)+ })
85 keymap!({ $label $(sticky=$sticky)? $($($key)|+ => $value,)+ })
86 };
86 };
87
87
88+ (@trie [$($cmd:ident),* $(,)?]) => {
89+ $crate::keymap::KeyTrie::Sequence(vec![$($crate::commands::Command::$cmd),*])
90+ };
91+
88 (
92 (
89 { $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
93 { $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
90 ) => {
94 ) => {
@@ -180,6 +184,7 @@ impl KeyTrieNode {
180 cmd.doc()
184 cmd.doc()
181 }
185 }
182 KeyTrie::Node(n) => n.name(),
186 KeyTrie::Node(n) => n.name(),
187+ KeyTrie::Sequence(_) => "[Multiple commands]",
183 };
188 };
184 match body.iter().position(|(d, _)| d == &desc) {
189 match body.iter().position(|(d, _)| d == &desc) {
185 Some(pos) => {
190 Some(pos) => {
@@ -240,6 +245,7 @@ impl DerefMut for KeyTrieNode {
240#[serde(untagged)]
245#[serde(untagged)]
241pub enum KeyTrie {
246pub enum KeyTrie {
242 Leaf(Command),
247 Leaf(Command),
248+ Sequence(Vec<Command>),
243 Node(KeyTrieNode),
249 Node(KeyTrieNode),
244}
250}
245
251
@@ -247,14 +253,14 @@ impl KeyTrie {
247 pub fn node(&self) -> Option<&KeyTrieNode> {
253 pub fn node(&self) -> Option<&KeyTrieNode> {
248 match *self {
254 match *self {
249 KeyTrie::Node(ref node) => Some(node),
255 KeyTrie::Node(ref node) => Some(node),
250− KeyTrie::Leaf(_) => None,
256+ KeyTrie::Leaf(_) | KeyTrie::Sequence(_) => None,
251 }
257 }
252 }
258 }
253
259
254 pub fn node_mut(&mut self) -> Option<&mut KeyTrieNode> {
260 pub fn node_mut(&mut self) -> Option<&mut KeyTrieNode> {
255 match *self {
261 match *self {
256 KeyTrie::Node(ref mut node) => Some(node),
262 KeyTrie::Node(ref mut node) => Some(node),
257− KeyTrie::Leaf(_) => None,
263+ KeyTrie::Leaf(_) | KeyTrie::Sequence(_) => None,
258 }
264 }
259 }
265 }
260
266
@@ -271,7 +277,7 @@ impl KeyTrie {
271 trie = match trie {
277 trie = match trie {
272 KeyTrie::Node(map) => map.get(key),
278 KeyTrie::Node(map) => map.get(key),
273 // leaf encountered while keys left to process
279 // leaf encountered while keys left to process
274− KeyTrie::Leaf(_) => None,
280+ KeyTrie::Leaf(_) | KeyTrie::Sequence(_) => None,
275 }?
281 }?
276 }
282 }
277 Some(trie)
283 Some(trie)
@@ -283,6 +289,8 @@ pub enum KeymapResultKind {
283 /// Needs more keys to execute a command. Contains valid keys for next keystroke.
289 /// Needs more keys to execute a command. Contains valid keys for next keystroke.
284 Pending(KeyTrieNode),
290 Pending(KeyTrieNode),
285 Matched(Command),
291 Matched(Command),
292+ /// Matched a sequence of commands to execute.
293+ MatchedSequence(Vec<Command>),
286 /// Key was not found in the root keymap
294 /// Key was not found in the root keymap
287 NotFound,
295 NotFound,
288 /// Key is invalid in combination with previous keys. Contains keys leading upto
296 /// Key is invalid in combination with previous keys. Contains keys leading upto
@@ -365,6 +373,12 @@ impl Keymap {
365 Some(&KeyTrie::Leaf(cmd)) => {
373 Some(&KeyTrie::Leaf(cmd)) => {
366 return KeymapResult::new(KeymapResultKind::Matched(cmd), self.sticky())
374 return KeymapResult::new(KeymapResultKind::Matched(cmd), self.sticky())
367 }
375 }
376+ Some(&KeyTrie::Sequence(ref cmds)) => {
377+ return KeymapResult::new(
378+ KeymapResultKind::MatchedSequence(cmds.clone()),
379+ self.sticky(),
380+ )
381+ }
368 None => return KeymapResult::new(KeymapResultKind::NotFound, self.sticky()),
382 None => return KeymapResult::new(KeymapResultKind::NotFound, self.sticky()),
369 Some(t) => t,
383 Some(t) => t,
370 };
384 };
@@ -382,6 +396,13 @@ impl Keymap {
382 self.state.clear();
396 self.state.clear();
383 return KeymapResult::new(KeymapResultKind::Matched(cmd), self.sticky());
397 return KeymapResult::new(KeymapResultKind::Matched(cmd), self.sticky());
384 }
398 }
399+ Some(&KeyTrie::Sequence(ref cmds)) => {
400+ self.state.clear();
401+ KeymapResult::new(
402+ KeymapResultKind::MatchedSequence(cmds.clone()),
403+ self.sticky(),
404+ )
405+ }
385 None => KeymapResult::new(
406 None => KeymapResult::new(
386 KeymapResultKind::Cancelled(self.state.drain(..).collect()),
407 KeymapResultKind::Cancelled(self.state.drain(..).collect()),
387 self.sticky(),
408 self.sticky(),
helix-term/src/ui/editor.rs+5
@@ -695,6 +695,11 @@ impl EditorView {
695 match &key_result.kind {
695 match &key_result.kind {
696 KeymapResultKind::Matched(command) => command.execute(cxt),
696 KeymapResultKind::Matched(command) => command.execute(cxt),
697 KeymapResultKind::Pending(node) => self.autoinfo = Some(node.infobox()),
697 KeymapResultKind::Pending(node) => self.autoinfo = Some(node.infobox()),
698+ KeymapResultKind::MatchedSequence(commands) => {
699+ for command in commands {
700+ command.execute(cxt);
701+ }
702+ }
698 KeymapResultKind::NotFound | KeymapResultKind::Cancelled(_) => return Some(key_result),
703 KeymapResultKind::NotFound | KeymapResultKind::Cancelled(_) => return Some(key_result),
699 }
704 }
700 None
705 None