on master help sign in
Add a sticky mode for keymaps (#635)
commit183dcce992d7c5b2065a93c5835d61e8ee4e9f05 authorGokul Soumya <gokulps15@gmail.com> dateSun 5 Sep 2021, 09:25 (5 years ago) parent99a753a
2 files changed+104−45
helix-term/src/keymap.rs+84−29 helix-term/src/ui/editor.rs+20−16
helix-term/src/keymap.rs+84−29
@@ -4,6 +4,7 @@ use helix_core::hashmap;
44use helix_view::{document::Mode, info::Info, input::KeyEvent};
55use serde::Deserialize;
66use std::{
7+ borrow::Cow,
78 collections::HashMap,
89 ops::{Deref, DerefMut},
910};
@@ -47,13 +48,13 @@ macro_rules! keymap {
4748 };
4849
4950 (@trie
50− { $label:literal $($($key:literal)|+ => $value:tt,)+ }
51+ { $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
5152 ) => {
52− keymap!({ $label $($($key)|+ => $value,)+ })
53+ keymap!({ $label $(sticky=$sticky)? $($($key)|+ => $value,)+ })
5354 };
5455
5556 (
56− { $label:literal $($($key:literal)|+ => $value:tt,)+ }
57+ { $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
5758 ) => {
5859 // modified from the hashmap! macro
5960 {
@@ -70,7 +71,9 @@ macro_rules! keymap {
7071 _order.push(_key);
7172 )+
7273 )*
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)
7477 }
7578 };
7679}
@@ -84,6 +87,8 @@ pub struct KeyTrieNode {
8487 map: HashMap<KeyEvent, KeyTrie>,
8588 #[serde(skip)]
8689 order: Vec<KeyEvent>,
90+ #[serde(skip)]
91+ pub is_sticky: bool,
8792}
8893
8994impl KeyTrieNode {
@@ -92,6 +97,7 @@ impl KeyTrieNode {
9297 name: name.to_string(),
9398 map,
9499 order,
100+ is_sticky: false,
95101 }
96102 }
97103
@@ -119,12 +125,10 @@ impl KeyTrieNode {
119125 }
120126 }
121127 }
122−}
123128
124−impl From<KeyTrieNode> for Info {
125− fn from(node: KeyTrieNode) -> Self {
126− let mut body: Vec<(&str, Vec<KeyEvent>)> = Vec::with_capacity(node.len());
127− for (&key, trie) in node.iter() {
129+ pub fn infobox(&self) -> Info {
130+ let mut body: Vec<(&str, Vec<KeyEvent>)> = Vec::with_capacity(self.len());
131+ for (&key, trie) in self.iter() {
128132 let desc = match trie {
129133 KeyTrie::Leaf(cmd) => cmd.doc(),
130134 KeyTrie::Node(n) => n.name(),
@@ -136,16 +140,16 @@ impl From<KeyTrieNode> for Info {
136140 }
137141 }
138142 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()
140144 });
141− let prefix = format!("{} ", node.name());
145+ let prefix = format!("{} ", self.name());
142146 if body.iter().all(|(desc, _)| desc.starts_with(&prefix)) {
143147 body = body
144148 .into_iter()
145149 .map(|(desc, keys)| (desc.strip_prefix(&prefix).unwrap(), keys))
146150 .collect();
147151 }
148− Info::new(node.name(), body)
152+ Info::new(self.name(), body)
149153 }
150154}
151155
@@ -218,7 +222,7 @@ impl KeyTrie {
218222}
219223
220224#[derive(Debug, Clone, PartialEq)]
221−pub enum KeymapResult {
225+pub enum KeymapResultKind {
222226 /// Needs more keys to execute a command. Contains valid keys for next keystroke.
223227 Pending(KeyTrieNode),
224228 Matched(Command),
@@ -229,14 +233,31 @@ pub enum KeymapResult {
229233 Cancelled(Vec<KeyEvent>),
230234}
231235
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>,
241+}
242+
243+impl<'a> KeymapResult<'a> {
244+ pub fn new(kind: KeymapResultKind, sticky: Option<&'a KeyTrieNode>) -> Self {
245+ Self { kind, sticky }
246+ }
247+}
248+
232249#[derive(Debug, Clone, PartialEq, Deserialize)]
233250pub struct Keymap {
234251 /// Always a Node
235252 #[serde(flatten)]
236253 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.
238256 #[serde(skip)]
239257 state: Vec<KeyEvent>,
258+ /// Stores the sticky node if one is activated.
259+ #[serde(skip)]
260+ sticky: Option<KeyTrieNode>,
240261}
241262
242263impl Keymap {
@@ -244,6 +265,7 @@ impl Keymap {
244265 Keymap {
245266 root,
246267 state: Vec::new(),
268+ sticky: None,
247269 }
248270 }
249271
@@ -251,27 +273,60 @@ impl Keymap {
251273 &self.root
252274 }
253275
276+ pub fn sticky(&self) -> Option<&KeyTrieNode> {
277+ self.sticky.as_ref()
278+ }
279+
254280 /// Returns list of keys waiting to be disambiguated.
255281 pub fn pending(&self) -> &[KeyEvent] {
256282 &self.state
257283 }
258284
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.
260288 pub fn get(&mut self, key: KeyEvent) -> KeymapResult {
261− let &first = self.state.get(0).unwrap_or(&key);
262− let trie = match self.root.search(&[first]) {
263− Some(&KeyTrie::Leaf(cmd)) => return KeymapResult::Matched(cmd),
264− None => return KeymapResult::NotFound,
289+ if let key!(Esc) = key {
290+ if self.state.is_empty() {
291+ self.sticky = None;
292+ }
293+ return KeymapResult::new(
294+ KeymapResultKind::Cancelled(self.state.drain(..).collect()),
295+ self.sticky(),
296+ );
297+ }
298+
299+ let first = self.state.get(0).unwrap_or(&key);
300+ let trie_node = match self.sticky {
301+ Some(ref trie) => Cow::Owned(KeyTrie::Node(trie.clone())),
302+ None => Cow::Borrowed(&self.root),
303+ };
304+
305+ let trie = match trie_node.search(&[*first]) {
306+ Some(&KeyTrie::Leaf(cmd)) => {
307+ return KeymapResult::new(KeymapResultKind::Matched(cmd), self.sticky())
308+ }
309+ None => return KeymapResult::new(KeymapResultKind::NotFound, self.sticky()),
265310 Some(t) => t,
266311 };
312+
267313 self.state.push(key);
268314 match trie.search(&self.state[1..]) {
269− Some(&KeyTrie::Node(ref map)) => KeymapResult::Pending(map.clone()),
270− Some(&KeyTrie::Leaf(command)) => {
315+ Some(&KeyTrie::Node(ref map)) => {
316+ if map.is_sticky {
317+ self.state.clear();
318+ self.sticky = Some(map.clone());
319+ }
320+ KeymapResult::new(KeymapResultKind::Pending(map.clone()), self.sticky())
321+ }
322+ Some(&KeyTrie::Leaf(cmd)) => {
271323 self.state.clear();
272− KeymapResult::Matched(command)
324+ return KeymapResult::new(KeymapResultKind::Matched(cmd), self.sticky());
273325 }
274− None => KeymapResult::Cancelled(self.state.drain(..).collect()),
326+ None => KeymapResult::new(
327+ KeymapResultKind::Cancelled(self.state.drain(..).collect()),
328+ self.sticky(),
329+ ),
275330 }
276331 }
277332
@@ -602,19 +657,19 @@ fn merge_partial_keys() {
602657
603658 let keymap = merged_config.keys.0.get_mut(&Mode::Normal).unwrap();
604659 assert_eq!(
605− keymap.get(key!('i')),
606− KeymapResult::Matched(Command::normal_mode),
660+ keymap.get(key!('i')).kind,
661+ KeymapResultKind::Matched(Command::normal_mode),
607662 "Leaf should replace leaf"
608663 );
609664 assert_eq!(
610− keymap.get(key!('无')),
611− KeymapResult::Matched(Command::insert_mode),
665+ keymap.get(key!('无')).kind,
666+ KeymapResultKind::Matched(Command::insert_mode),
612667 "New leaf should be present in merged keymap"
613668 );
614669 // Assumes that z is a node in the default keymap
615670 assert_eq!(
616− keymap.get(key!('z')),
617− KeymapResult::Matched(Command::jump_backward),
671+ keymap.get(key!('z')).kind,
672+ KeymapResultKind::Matched(Command::jump_backward),
618673 "Leaf should replace node"
619674 );
620675 // Assumes that `g` is a node in default keymap
helix-term/src/ui/editor.rs+20−16
@@ -2,7 +2,7 @@ use crate::{
22 commands,
33 compositor::{Component, Context, EventResult},
44 key,
5− keymap::{KeymapResult, Keymaps},
5+ keymap::{KeymapResult, KeymapResultKind, Keymaps},
66 ui::{Completion, ProgressSpinners},
77};
88
@@ -638,7 +638,7 @@ impl EditorView {
638638
639639 /// Handle events by looking them up in `self.keymaps`. Returns None
640640 /// if event was handled (a command was executed or a subkeymap was
641− /// activated). Only KeymapResult::{NotFound, Cancelled} is returned
641+ /// activated). Only KeymapResultKind::{NotFound, Cancelled} is returned
642642 /// otherwise.
643643 fn handle_keymap_event(
644644 &mut self,
@@ -647,29 +647,32 @@ impl EditorView {
647647 event: KeyEvent,
648648 ) -> Option<KeymapResult> {
649649 self.autoinfo = None;
650− match self.keymaps.get_mut(&mode).unwrap().get(event) {
651− KeymapResult::Matched(command) => command.execute(cxt),
652− KeymapResult::Pending(node) => self.autoinfo = Some(node.into()),
653− k @ KeymapResult::NotFound | k @ KeymapResult::Cancelled(_) => return Some(k),
650+ let key_result = self.keymaps.get_mut(&mode).unwrap().get(event);
651+ self.autoinfo = key_result.sticky.map(|node| node.infobox());
652+
653+ match &key_result.kind {
654+ KeymapResultKind::Matched(command) => command.execute(cxt),
655+ KeymapResultKind::Pending(node) => self.autoinfo = Some(node.infobox()),
656+ KeymapResultKind::NotFound | KeymapResultKind::Cancelled(_) => return Some(key_result),
654657 }
655658 None
656659 }
657660
658661 fn insert_mode(&mut self, cx: &mut commands::Context, event: KeyEvent) {
659662 if let Some(keyresult) = self.handle_keymap_event(Mode::Insert, cx, event) {
660− match keyresult {
661− KeymapResult::NotFound => {
663+ match keyresult.kind {
664+ KeymapResultKind::NotFound => {
662665 if let Some(ch) = event.char() {
663666 commands::insert::insert_char(cx, ch)
664667 }
665668 }
666− KeymapResult::Cancelled(pending) => {
669+ KeymapResultKind::Cancelled(pending) => {
667670 for ev in pending {
668671 match ev.char() {
669672 Some(ch) => commands::insert::insert_char(cx, ch),
670673 None => {
671− if let KeymapResult::Matched(command) =
672− self.keymaps.get_mut(&Mode::Insert).unwrap().get(ev)
674+ if let KeymapResultKind::Matched(command) =
675+ self.keymaps.get_mut(&Mode::Insert).unwrap().get(ev).kind
673676 {
674677 command.execute(cx);
675678 }
@@ -976,11 +979,12 @@ impl Component for EditorView {
976979 // how we entered insert mode is important, and we should track that so
977980 // we can repeat the side effect.
978981
979− self.last_insert.0 = match self.keymaps.get_mut(&mode).unwrap().get(key) {
980− KeymapResult::Matched(command) => command,
981− // FIXME: insert mode can only be entered through single KeyCodes
982− _ => unimplemented!(),
983− };
982+ self.last_insert.0 =
983+ match self.keymaps.get_mut(&mode).unwrap().get(key).kind {
984+ KeymapResultKind::Matched(command) => command,
985+ // FIXME: insert mode can only be entered through single KeyCodes
986+ _ => unimplemented!(),
987+ };
984988 self.last_insert.1.clear();
985989 }
986990 (Mode::Insert, Mode::Normal) => {