From 7909d6f05e4d651d904886aa53ec4bd250294e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Sun, 20 Mar 2022 15:50:48 +0900 Subject: [PATCH] keymap: Store pending/sticky on the root level --- helix-term/src/commands.rs | 2 +- helix-term/src/config.rs | 2 +- helix-term/src/keymap.rs | 148 +++++++++++++++++------------------- helix-term/src/ui/editor.rs | 15 ++-- 4 files changed, 77 insertions(+), 90 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index f8a888e7a..c74898106 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2105,7 +2105,7 @@ pub fn command_palette(cx: &mut Context) { move |compositor: &mut Compositor, cx: &mut compositor::Context| { let doc = doc_mut!(cx.editor); let keymap = - compositor.find::().unwrap().keymaps[&doc.mode].reverse_map(); + compositor.find::().unwrap().keymaps.map[&doc.mode].reverse_map(); let mut commands: Vec = MappableCommand::STATIC_COMMAND_LIST.into(); commands.extend(typed::TYPABLE_COMMAND_LIST.iter().map(|cmd| { diff --git a/helix-term/src/config.rs b/helix-term/src/config.rs index 6b8bbc1b8..1c6289ec8 100644 --- a/helix-term/src/config.rs +++ b/helix-term/src/config.rs @@ -43,7 +43,7 @@ mod tests { assert_eq!( toml::from_str::(sample_keymaps).unwrap(), Config { - keys: Keymaps(hashmap! { + keys: Keymaps::new(hashmap! { Mode::Insert => Keymap::new(keymap!({ "Insert mode" "y" => move_line_down, "S-C-a" => delete_selection, diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 1059feaea..e3cbcaad7 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -328,26 +328,15 @@ impl<'a> KeymapResult<'a> { } #[derive(Debug, Clone, PartialEq, Deserialize)] +#[serde(transparent)] pub struct Keymap { /// Always a Node - #[serde(flatten)] root: KeyTrie, - /// Stores pending keys waiting for the next key. This is relative to a - /// sticky node if one is in use. - #[serde(skip)] - state: Vec, - /// Stores the sticky node if one is activated. - #[serde(skip)] - sticky: Option, } impl Keymap { pub fn new(root: KeyTrie) -> Self { - Keymap { - root, - state: Vec::new(), - sticky: None, - } + Keymap { root } } pub fn reverse_map(&self) -> HashMap>> { @@ -387,19 +376,65 @@ impl Keymap { &self.root } - pub fn sticky(&self) -> Option<&KeyTrieNode> { - self.sticky.as_ref() + pub fn merge(&mut self, other: Self) { + self.root.merge_nodes(other.root); + } +} + +impl Deref for Keymap { + type Target = KeyTrieNode; + + fn deref(&self) -> &Self::Target { + self.root.node().unwrap() } +} - /// Returns list of keys waiting to be disambiguated. +impl Default for Keymap { + fn default() -> Self { + Self::new(KeyTrie::Node(KeyTrieNode::default())) + } +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Keymaps { + #[serde(flatten)] + pub map: HashMap, + + /// Stores pending keys waiting for the next key. This is relative to a + /// sticky node if one is in use. + #[serde(skip)] + state: Vec, + + /// Stores the sticky node if one is activated. + #[serde(skip)] + pub sticky: Option, +} + +impl Keymaps { + pub fn new(map: HashMap) -> Self { + Self { + map, + state: Vec::new(), + sticky: None, + } + } + + /// Returns list of keys waiting to be disambiguated in current mode. pub fn pending(&self) -> &[KeyEvent] { &self.state } + pub fn sticky(&self) -> Option<&KeyTrieNode> { + self.sticky.as_ref() + } + /// Lookup `key` in the keymap to try and find a command to execute. Escape /// key cancels pending keystrokes. If there are no pending keystrokes but a /// sticky node is in use, it will be cleared. - pub fn get(&mut self, key: KeyEvent) -> KeymapResult { + pub fn get(&mut self, mode: Mode, key: KeyEvent) -> KeymapResult { + // TODO: remove the sticky part and look up manually + let keymap = &self.map[&mode]; + if key!(Esc) == key { if !self.state.is_empty() { return KeymapResult::new( @@ -414,7 +449,7 @@ impl Keymap { let first = self.state.get(0).unwrap_or(&key); let trie_node = match self.sticky { Some(ref trie) => Cow::Owned(KeyTrie::Node(trie.clone())), - None => Cow::Borrowed(&self.root), + None => Cow::Borrowed(&keymap.root), }; let trie = match trie_node.search(&[*first]) { @@ -457,55 +492,6 @@ impl Keymap { ), } } - - pub fn merge(&mut self, other: Self) { - self.root.merge_nodes(other.root); - } -} - -impl Deref for Keymap { - type Target = KeyTrieNode; - - fn deref(&self) -> &Self::Target { - self.root.node().unwrap() - } -} - -impl Default for Keymap { - fn default() -> Self { - Self::new(KeyTrie::Node(KeyTrieNode::default())) - } -} - -#[derive(Debug, Clone, PartialEq, Deserialize)] -#[serde(transparent)] -pub struct Keymaps(pub HashMap); - -impl Keymaps { - /// Returns list of keys waiting to be disambiguated in current mode. - pub fn pending(&self) -> &[KeyEvent] { - self.0 - .values() - .find_map(|keymap| match keymap.pending().is_empty() { - true => None, - false => Some(keymap.pending()), - }) - .unwrap_or_default() - } -} - -impl Deref for Keymaps { - type Target = HashMap; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Keymaps { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } } impl Default for Keymaps { @@ -856,7 +842,7 @@ impl Default for Keymaps { "C-x" => completion, "C-r" => insert_register, }); - Self(hashmap!( + Self::new(hashmap!( Mode::Normal => Keymap::new(normal), Mode::Select => Keymap::new(select), Mode::Insert => Keymap::new(insert), @@ -867,8 +853,8 @@ impl Default for Keymaps { /// Merge default config keys with user overwritten keys for custom user config. pub fn merge_keys(mut config: Config) -> Config { let mut delta = std::mem::take(&mut config.keys); - for (mode, keys) in &mut *config.keys { - keys.merge(delta.remove(mode).unwrap_or_default()) + for (mode, keys) in &mut config.keys.map { + keys.merge(delta.map.remove(mode).unwrap_or_default()) } config } @@ -895,7 +881,7 @@ mod tests { #[test] fn merge_partial_keys() { let config = Config { - keys: Keymaps(hashmap! { + keys: Keymaps::new(hashmap! { Mode::Normal => Keymap::new( keymap!({ "Normal mode" "i" => normal_mode, @@ -913,23 +899,25 @@ mod tests { let mut merged_config = merge_keys(config.clone()); assert_ne!(config, merged_config); - let keymap = merged_config.keys.0.get_mut(&Mode::Normal).unwrap(); + let keymap = &mut merged_config.keys; assert_eq!( - keymap.get(key!('i')).kind, + keymap.get(Mode::Normal, key!('i')).kind, KeymapResultKind::Matched(MappableCommand::normal_mode), "Leaf should replace leaf" ); assert_eq!( - keymap.get(key!('无')).kind, + keymap.get(Mode::Normal, key!('无')).kind, KeymapResultKind::Matched(MappableCommand::insert_mode), "New leaf should be present in merged keymap" ); // Assumes that z is a node in the default keymap assert_eq!( - keymap.get(key!('z')).kind, + keymap.get(Mode::Normal, key!('z')).kind, KeymapResultKind::Matched(MappableCommand::jump_backward), "Leaf should replace node" ); + + let keymap = merged_config.keys.map.get_mut(&Mode::Normal).unwrap(); // Assumes that `g` is a node in default keymap assert_eq!( keymap.root().search(&[key!('g'), key!('$')]).unwrap(), @@ -949,14 +937,14 @@ mod tests { "Old leaves in subnode should be present in merged node" ); - assert!(merged_config.keys.0.get(&Mode::Normal).unwrap().len() > 1); - assert!(merged_config.keys.0.get(&Mode::Insert).unwrap().len() > 0); + assert!(merged_config.keys.map.get(&Mode::Normal).unwrap().len() > 1); + assert!(merged_config.keys.map.get(&Mode::Insert).unwrap().len() > 0); } #[test] fn order_should_be_set() { let config = Config { - keys: Keymaps(hashmap! { + keys: Keymaps::new(hashmap! { Mode::Normal => Keymap::new( keymap!({ "Normal mode" "space" => { "" @@ -972,7 +960,7 @@ mod tests { }; let mut merged_config = merge_keys(config.clone()); assert_ne!(config, merged_config); - let keymap = merged_config.keys.0.get_mut(&Mode::Normal).unwrap(); + let keymap = merged_config.keys.map.get_mut(&Mode::Normal).unwrap(); // Make sure mapping works assert_eq!( keymap @@ -990,7 +978,7 @@ mod tests { #[test] fn aliased_modes_are_same_in_default_keymap() { let keymaps = Keymaps::default(); - let root = keymaps.get(&Mode::Normal).unwrap().root(); + let root = keymaps.map.get(&Mode::Normal).unwrap().root(); assert_eq!( root.search(&[key!(' '), key!('w')]).unwrap(), root.search(&["C-w".parse::().unwrap()]).unwrap(), diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 2b9de5d62..a0c0e7730 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -703,7 +703,7 @@ impl EditorView { event: KeyEvent, ) -> Option { cxt.editor.autoinfo = None; - let key_result = self.keymaps.get_mut(&mode).unwrap().get(event); + let key_result = self.keymaps.get(mode, event); cxt.editor.autoinfo = key_result.sticky.map(|node| node.infobox()); match &key_result.kind { @@ -733,7 +733,7 @@ impl EditorView { Some(ch) => commands::insert::insert_char(cx, ch), None => { if let KeymapResultKind::Matched(command) = - self.keymaps.get_mut(&Mode::Insert).unwrap().get(ev).kind + self.keymaps.get(Mode::Insert, ev).kind { command.execute(cx); } @@ -1182,12 +1182,11 @@ impl Component for EditorView { // how we entered insert mode is important, and we should track that so // we can repeat the side effect. - self.last_insert.0 = - match self.keymaps.get_mut(&mode).unwrap().get(key).kind { - KeymapResultKind::Matched(command) => command, - // FIXME: insert mode can only be entered through single KeyCodes - _ => unimplemented!(), - }; + self.last_insert.0 = match self.keymaps.get(mode, key).kind { + KeymapResultKind::Matched(command) => command, + // FIXME: insert mode can only be entered through single KeyCodes + _ => unimplemented!(), + }; self.last_insert.1.clear(); } (Mode::Insert, Mode::Normal) => {