diff --git a/helix-term/src/config.rs b/helix-term/src/config.rs index f37b03ec7..a7afead52 100644 --- a/helix-term/src/config.rs +++ b/helix-term/src/config.rs @@ -1,7 +1,6 @@ use crate::keymap; -use crate::keymap::{merge_keys, KeyTrie}; +use crate::keymap::{merge_keys, Domain, KeyTrie}; use helix_loader::merge_toml_values; -use helix_view::document::Mode; use serde::Deserialize; use std::collections::HashMap; use std::fmt::Display; @@ -12,7 +11,7 @@ use toml::de::Error as TomlError; #[derive(Debug, Clone, PartialEq)] pub struct Config { pub theme: Option, - pub keys: HashMap, + pub keys: HashMap, pub editor: helix_view::editor::Config, } @@ -20,7 +19,7 @@ pub struct Config { #[serde(deny_unknown_fields)] pub struct ConfigRaw { pub theme: Option, - pub keys: Option>, + pub keys: Option>, pub editor: Option, } @@ -154,11 +153,11 @@ mod tests { merge_keys( &mut keys, hashmap! { - Mode::Insert => keymap!({ "Insert mode" + Domain::Mode(Mode::Insert) => keymap!({ "Insert mode" "y" => move_line_down, "S-C-a" => delete_selection, }), - Mode::Normal => keymap!({ "Normal mode" + Domain::Mode(Mode::Normal) => keymap!({ "Normal mode" "A-F12" => move_next_word_end, }), }, diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 5a72a35a5..55c107f3a 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -272,8 +272,15 @@ pub enum KeymapResult { /// A map of command names to keybinds that will execute the command. pub type ReverseKeymap = HashMap>>; +// TODO name +#[derive(Eq, Hash, PartialEq, Clone, Debug)] +pub enum Domain { + Mode(Mode), + Component(&'static str), +} + pub struct Keymaps { - pub map: Box>>, + pub map: Box>>, /// Stores pending keys waiting for the next key. This is relative to a /// sticky node if one is in use. state: Vec, @@ -282,7 +289,7 @@ pub struct Keymaps { } impl Keymaps { - pub fn new(map: Box>>) -> Self { + pub fn new(map: Box>>) -> Self { Self { map, state: Vec::new(), @@ -290,7 +297,7 @@ impl Keymaps { } } - pub fn map(&self) -> DynGuard> { + pub fn map(&self) -> DynGuard> { self.map.load() } @@ -303,14 +310,24 @@ impl Keymaps { self.sticky.as_ref() } + pub fn get_by_mode(&mut self, mode: Mode, key: KeyEvent) -> KeymapResult { + self.get(Domain::Mode(mode), key) + } + + pub fn get_by_component_id(&mut self, id: &'static str, key: KeyEvent) -> KeymapResult { + self.get(Domain::Component(id), key) + } + /// 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, mode: Mode, key: KeyEvent) -> KeymapResult { - // TODO: remove the sticky part and look up manually + fn get(&mut self, domain: Domain, key: KeyEvent) -> KeymapResult { let keymaps = &*self.map(); - let keymap = &keymaps[&mode]; + let Some(keymap) = keymaps.get(&domain) else { + return KeymapResult::NotFound; + }; + // TODO: remove the sticky part and look up manually if key!(Esc) == key { if !self.state.is_empty() { // Note that Esc is not included here @@ -365,7 +382,7 @@ impl Default for Keymaps { } /// Merge default config keys with user overwritten keys for custom user config. -pub fn merge_keys(dst: &mut HashMap, mut delta: HashMap) { +pub fn merge_keys(dst: &mut HashMap, mut delta: HashMap) { for (mode, keys) in dst { keys.merge_nodes( delta @@ -400,7 +417,7 @@ mod tests { #[test] fn merge_partial_keys() { let keymap = hashmap! { - Mode::Normal => keymap!({ "Normal mode" + Domain::Mode(Mode::Normal) => keymap!({ "Normal mode" "i" => normal_mode, "无" => insert_mode, "z" => jump_backward, @@ -416,23 +433,23 @@ mod tests { let mut keymap = Keymaps::new(Box::new(Constant(merged_keyamp.clone()))); assert_eq!( - keymap.get(Mode::Normal, key!('i')), + keymap.get_by_mode(Mode::Normal, key!('i')), KeymapResult::Matched(MappableCommand::normal_mode), "Leaf should replace leaf" ); assert_eq!( - keymap.get(Mode::Normal, key!('无')), + keymap.get_by_mode(Mode::Normal, key!('无')), KeymapResult::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(Mode::Normal, key!('z')), + keymap.get_by_mode(Mode::Normal, key!('z')), KeymapResult::Matched(MappableCommand::jump_backward), "Leaf should replace node" ); - let keymap = merged_keyamp.get_mut(&Mode::Normal).unwrap(); + let keymap = merged_keyamp.get_mut(&Domain::Mode(Mode::Normal)).unwrap(); // Assumes that `g` is a node in default keymap assert_eq!( keymap.search(&[key!('g'), key!('$')]).unwrap(), @@ -454,7 +471,7 @@ mod tests { assert!( merged_keyamp - .get(&Mode::Normal) + .get(&Domain::Mode(Mode::Normal)) .and_then(|key_trie| key_trie.node()) .unwrap() .len() @@ -462,7 +479,7 @@ mod tests { ); assert!( merged_keyamp - .get(&Mode::Insert) + .get(&Domain::Mode(Mode::Insert)) .and_then(|key_trie| key_trie.node()) .unwrap() .len() @@ -473,7 +490,7 @@ mod tests { #[test] fn order_should_be_set() { let keymap = hashmap! { - Mode::Normal => keymap!({ "Normal mode" + Domain::Mode(Mode::Normal) => keymap!({ "Normal mode" "space" => { "" "s" => { "" "v" => vsplit, @@ -485,7 +502,7 @@ mod tests { let mut merged_keyamp = default(); merge_keys(&mut merged_keyamp, keymap.clone()); assert_ne!(keymap, merged_keyamp); - let keymap = merged_keyamp.get_mut(&Mode::Normal).unwrap(); + let keymap = merged_keyamp.get_mut(&Domain::Mode(Mode::Normal)).unwrap(); // Make sure mapping works assert_eq!( keymap.search(&[key!(' '), key!('s'), key!('v')]).unwrap(), @@ -500,7 +517,7 @@ mod tests { #[test] fn aliased_modes_are_same_in_default_keymap() { let keymaps = Keymaps::default().map(); - let root = keymaps.get(&Mode::Normal).unwrap(); + let root = keymaps.get(&Domain::Mode(Mode::Normal)).unwrap(); assert_eq!( root.search(&[key!(' '), key!('w')]).unwrap(), root.search(&["C-w".parse::().unwrap()]).unwrap(), diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index c84c616c6..fd92c3615 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -1,10 +1,10 @@ use std::collections::HashMap; use super::macros::keymap; -use super::{KeyTrie, Mode}; +use super::{Domain, KeyTrie, Mode}; use helix_core::hashmap; -pub fn default() -> HashMap { +pub fn default() -> HashMap { let normal = keymap!({ "Normal mode" "h" | "left" => move_char_left, "j" | "down" => move_visual_line_down, @@ -380,8 +380,8 @@ pub fn default() -> HashMap { "end" => goto_line_end_newline, }); hashmap!( - Mode::Normal => normal, - Mode::Select => select, - Mode::Insert => insert, + Domain::Mode(Mode::Normal) => normal, + Domain::Mode(Mode::Select) => select, + Domain::Mode(Mode::Insert) => insert, ) } diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 515297c39..d1487bcb6 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -792,7 +792,7 @@ impl EditorView { ) -> Option { let mut last_mode = mode; self.pseudo_pending.extend(cxt.keymaps.pending()); - let key_result = cxt.keymaps.get(mode, event); + let key_result = cxt.keymaps.get_by_mode(mode, event); cxt.editor.autoinfo = cxt.keymaps.sticky().map(|node| node.infobox()); let mut execute_command = |command: &commands::MappableCommand| {