@@ -4,6 +4,7 @@ use helix_core::hashmap;
4use helix_view::{document::Mode, info::Info, input::KeyEvent};
4use helix_view::{document::Mode, info::Info, input::KeyEvent};
5use serde::Deserialize;
5use serde::Deserialize;
7 collections::HashMap,
8 collections::HashMap,
8 ops::{Deref, DerefMut},
9 ops::{Deref, DerefMut},
@@ -47,13 +48,13 @@ macro_rules! keymap {
50− { $label:literal $($($key:literal)|+ => $value:tt,)+ }
51+ { $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
52− keymap!({ $label $($($key)|+ => $value,)+ })
53+ keymap!({ $label $(sticky=$sticky)? $($($key)|+ => $value,)+ })
56− { $label:literal $($($key:literal)|+ => $value:tt,)+ }
57+ { $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
58 // modified from the hashmap! macro
59 // modified from the hashmap! macro
@@ -70,7 +71,9 @@ macro_rules! keymap {
70 _order.push(_key);
71 _order.push(_key);
73− $crate::keymap::KeyTrie::Node($crate::keymap::KeyTrieNode::new($label, _map, _order))
74+ let mut _node = $crate::keymap::KeyTrieNode::new($label, _map, _order);
75+ $( _node.is_sticky = $sticky; )?
76+ $crate::keymap::KeyTrie::Node(_node)
@@ -84,6 +87,8 @@ pub struct KeyTrieNode {
84 map: HashMap<KeyEvent, KeyTrie>,
87 map: HashMap<KeyEvent, KeyTrie>,
85 #[serde(skip)]
88 #[serde(skip)]
86 order: Vec<KeyEvent>,
89 order: Vec<KeyEvent>,
89impl KeyTrieNode {
94impl KeyTrieNode {
@@ -92,6 +97,7 @@ impl KeyTrieNode {
92 name: name.to_string(),
97 name: name.to_string(),
@@ -119,12 +125,10 @@ impl KeyTrieNode {
124−impl From<KeyTrieNode> for Info {
129+ pub fn infobox(&self) -> Info {
125− fn from(node: KeyTrieNode) -> Self {
126− let mut body: Vec<(&str, Vec<KeyEvent>)> = Vec::with_capacity(node.len());
130+ let mut body: Vec<(&str, Vec<KeyEvent>)> = Vec::with_capacity(self.len());
127− for (&key, trie) in node.iter() {
131+ for (&key, trie) in self.iter() {
128 let desc = match trie {
132 let desc = match trie {
129 KeyTrie::Leaf(cmd) => cmd.doc(),
133 KeyTrie::Leaf(cmd) => cmd.doc(),
130 KeyTrie::Node(n) => n.name(),
134 KeyTrie::Node(n) => n.name(),
@@ -136,16 +140,16 @@ impl From<KeyTrieNode> for Info {
138 body.sort_unstable_by_key(|(_, keys)| {
142 body.sort_unstable_by_key(|(_, keys)| {
139− node.order.iter().position(|&k| k == keys[0]).unwrap()
143+ self.order.iter().position(|&k| k == keys[0]).unwrap()
141− let prefix = format!("{} ", node.name());
145+ let prefix = format!("{} ", self.name());
142 if body.iter().all(|(desc, _)| desc.starts_with(&prefix)) {
146 if body.iter().all(|(desc, _)| desc.starts_with(&prefix)) {
143 body = body
147 body = body
144 .into_iter()
148 .into_iter()
145 .map(|(desc, keys)| (desc.strip_prefix(&prefix).unwrap(), keys))
149 .map(|(desc, keys)| (desc.strip_prefix(&prefix).unwrap(), keys))
146 .collect();
150 .collect();
148− Info::new(node.name(), body)
152+ Info::new(self.name(), body)
@@ -218,7 +222,7 @@ impl KeyTrie {
220#[derive(Debug, Clone, PartialEq)]
224#[derive(Debug, Clone, PartialEq)]
221−pub enum KeymapResult {
225+pub enum KeymapResultKind {
222 /// Needs more keys to execute a command. Contains valid keys for next keystroke.
226 /// Needs more keys to execute a command. Contains valid keys for next keystroke.
223 Pending(KeyTrieNode),
227 Pending(KeyTrieNode),
224 Matched(Command),
228 Matched(Command),
@@ -229,14 +233,31 @@ pub enum KeymapResult {
229 Cancelled(Vec<KeyEvent>),
233 Cancelled(Vec<KeyEvent>),
236+/// Returned after looking up a key in [`Keymap`]. The `sticky` field has a
237+/// reference to the sticky node if one is currently active.
238+pub struct KeymapResult<'a> {
239+ pub kind: KeymapResultKind,
240+ pub sticky: Option<&'a KeyTrieNode>,
243+impl<'a> KeymapResult<'a> {
244+ pub fn new(kind: KeymapResultKind, sticky: Option<&'a KeyTrieNode>) -> Self {
245+ Self { kind, sticky }
232#[derive(Debug, Clone, PartialEq, Deserialize)]
249#[derive(Debug, Clone, PartialEq, Deserialize)]
233pub struct Keymap {
250pub struct Keymap {
234 /// Always a Node
251 /// Always a Node
235 #[serde(flatten)]
252 #[serde(flatten)]
236 root: KeyTrie,
253 root: KeyTrie,
237− /// Stores pending keys waiting for the next key
254+ /// Stores pending keys waiting for the next key. This is relative to a
255+ /// sticky node if one is in use.
238 #[serde(skip)]
256 #[serde(skip)]
239 state: Vec<KeyEvent>,
257 state: Vec<KeyEvent>,
258+ /// Stores the sticky node if one is activated.
260+ sticky: Option<KeyTrieNode>,
242impl Keymap {
263impl Keymap {
@@ -244,6 +265,7 @@ impl Keymap {
246 state: Vec::new(),
267 state: Vec::new(),
@@ -251,27 +273,60 @@ impl Keymap {
251 &self.root
273 &self.root
276+ pub fn sticky(&self) -> Option<&KeyTrieNode> {
277+ self.sticky.as_ref()
254 /// Returns list of keys waiting to be disambiguated.
280 /// Returns list of keys waiting to be disambiguated.
255 pub fn pending(&self) -> &[KeyEvent] {
281 pub fn pending(&self) -> &[KeyEvent] {
256 &self.state
282 &self.state
259− /// Lookup `key` in the keymap to try and find a command to execute
285+ /// Lookup `key` in the keymap to try and find a command to execute. Escape
286+ /// key cancels pending keystrokes. If there are no pending keystrokes but a
287+ /// sticky node is in use, it will be cleared.
260 pub fn get(&mut self, key: KeyEvent) -> KeymapResult {
288 pub fn get(&mut self, key: KeyEvent) -> KeymapResult {
289+ if let key!(Esc) = key {
290+ if self.state.is_empty() {
293+ return KeymapResult::new(
294+ KeymapResultKind::Cancelled(self.state.drain(..).collect()),
261− let &first = self.state.get(0).unwrap_or(&key);
299+ let first = self.state.get(0).unwrap_or(&key);
262− let trie = match self.root.search(&[first]) {
300+ let trie_node = match self.sticky {
301+ Some(ref trie) => Cow::Owned(KeyTrie::Node(trie.clone())),
302+ None => Cow::Borrowed(&self.root),
305+ let trie = match trie_node.search(&[*first]) {
263− Some(&KeyTrie::Leaf(cmd)) => return KeymapResult::Matched(cmd),
306+ Some(&KeyTrie::Leaf(cmd)) => {
307+ return KeymapResult::new(KeymapResultKind::Matched(cmd), self.sticky())
264− None => return KeymapResult::NotFound,
309+ None => return KeymapResult::new(KeymapResultKind::NotFound, self.sticky()),
265 Some(t) => t,
310 Some(t) => t,
267 self.state.push(key);
313 self.state.push(key);
268 match trie.search(&self.state[1..]) {
314 match trie.search(&self.state[1..]) {
269− Some(&KeyTrie::Node(ref map)) => KeymapResult::Pending(map.clone()),
315+ Some(&KeyTrie::Node(ref map)) => {
318+ self.sticky = Some(map.clone());
320+ KeymapResult::new(KeymapResultKind::Pending(map.clone()), self.sticky())
270− Some(&KeyTrie::Leaf(command)) => {
322+ Some(&KeyTrie::Leaf(cmd)) => {
271 self.state.clear();
323 self.state.clear();
272− KeymapResult::Matched(command)
324+ return KeymapResult::new(KeymapResultKind::Matched(cmd), self.sticky());
326+ None => KeymapResult::new(
274− None => KeymapResult::Cancelled(self.state.drain(..).collect()),
327+ KeymapResultKind::Cancelled(self.state.drain(..).collect()),
@@ -602,19 +657,19 @@ fn merge_partial_keys() {
603 let keymap = merged_config.keys.0.get_mut(&Mode::Normal).unwrap();
658 let keymap = merged_config.keys.0.get_mut(&Mode::Normal).unwrap();
604 assert_eq!(
659 assert_eq!(
605− keymap.get(key!('i')),
660+ keymap.get(key!('i')).kind,
606− KeymapResult::Matched(Command::normal_mode),
661+ KeymapResultKind::Matched(Command::normal_mode),
607 "Leaf should replace leaf"
662 "Leaf should replace leaf"
609 assert_eq!(
664 assert_eq!(
610− keymap.get(key!('无')),
665+ keymap.get(key!('无')).kind,
611− KeymapResult::Matched(Command::insert_mode),
666+ KeymapResultKind::Matched(Command::insert_mode),
612 "New leaf should be present in merged keymap"
667 "New leaf should be present in merged keymap"
614 // Assumes that z is a node in the default keymap
669 // Assumes that z is a node in the default keymap
615 assert_eq!(
670 assert_eq!(
616− keymap.get(key!('z')),
671+ keymap.get(key!('z')).kind,
617− KeymapResult::Matched(Command::jump_backward),
672+ KeymapResultKind::Matched(Command::jump_backward),
618 "Leaf should replace node"
673 "Leaf should replace node"
620 // Assumes that `g` is a node in default keymap
675 // Assumes that `g` is a node in default keymap