mirror of https://github.com/helix-editor/helix
keymap: Store pending/sticky on the root level
parent
cfd992b151
commit
7909d6f05e
|
@ -2105,7 +2105,7 @@ pub fn command_palette(cx: &mut Context) {
|
||||||
move |compositor: &mut Compositor, cx: &mut compositor::Context| {
|
move |compositor: &mut Compositor, cx: &mut compositor::Context| {
|
||||||
let doc = doc_mut!(cx.editor);
|
let doc = doc_mut!(cx.editor);
|
||||||
let keymap =
|
let keymap =
|
||||||
compositor.find::<ui::EditorView>().unwrap().keymaps[&doc.mode].reverse_map();
|
compositor.find::<ui::EditorView>().unwrap().keymaps.map[&doc.mode].reverse_map();
|
||||||
|
|
||||||
let mut commands: Vec<MappableCommand> = MappableCommand::STATIC_COMMAND_LIST.into();
|
let mut commands: Vec<MappableCommand> = MappableCommand::STATIC_COMMAND_LIST.into();
|
||||||
commands.extend(typed::TYPABLE_COMMAND_LIST.iter().map(|cmd| {
|
commands.extend(typed::TYPABLE_COMMAND_LIST.iter().map(|cmd| {
|
||||||
|
|
|
@ -43,7 +43,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
toml::from_str::<Config>(sample_keymaps).unwrap(),
|
toml::from_str::<Config>(sample_keymaps).unwrap(),
|
||||||
Config {
|
Config {
|
||||||
keys: Keymaps(hashmap! {
|
keys: Keymaps::new(hashmap! {
|
||||||
Mode::Insert => Keymap::new(keymap!({ "Insert mode"
|
Mode::Insert => Keymap::new(keymap!({ "Insert mode"
|
||||||
"y" => move_line_down,
|
"y" => move_line_down,
|
||||||
"S-C-a" => delete_selection,
|
"S-C-a" => delete_selection,
|
||||||
|
|
|
@ -328,26 +328,15 @@ impl<'a> KeymapResult<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
pub struct Keymap {
|
pub struct Keymap {
|
||||||
/// Always a Node
|
/// Always a Node
|
||||||
#[serde(flatten)]
|
|
||||||
root: KeyTrie,
|
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<KeyEvent>,
|
|
||||||
/// Stores the sticky node if one is activated.
|
|
||||||
#[serde(skip)]
|
|
||||||
sticky: Option<KeyTrieNode>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keymap {
|
impl Keymap {
|
||||||
pub fn new(root: KeyTrie) -> Self {
|
pub fn new(root: KeyTrie) -> Self {
|
||||||
Keymap {
|
Keymap { root }
|
||||||
root,
|
|
||||||
state: Vec::new(),
|
|
||||||
sticky: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reverse_map(&self) -> HashMap<String, Vec<Vec<KeyEvent>>> {
|
pub fn reverse_map(&self) -> HashMap<String, Vec<Vec<KeyEvent>>> {
|
||||||
|
@ -387,19 +376,65 @@ impl Keymap {
|
||||||
&self.root
|
&self.root
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sticky(&self) -> Option<&KeyTrieNode> {
|
pub fn merge(&mut self, other: Self) {
|
||||||
self.sticky.as_ref()
|
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)]
|
||||||
|
pub struct Keymaps {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub map: HashMap<Mode, Keymap>,
|
||||||
|
|
||||||
|
/// Stores pending keys waiting for the next key. This is relative to a
|
||||||
|
/// sticky node if one is in use.
|
||||||
|
#[serde(skip)]
|
||||||
|
state: Vec<KeyEvent>,
|
||||||
|
|
||||||
|
/// Stores the sticky node if one is activated.
|
||||||
|
#[serde(skip)]
|
||||||
|
pub sticky: Option<KeyTrieNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Keymaps {
|
||||||
|
pub fn new(map: HashMap<Mode, Keymap>) -> Self {
|
||||||
|
Self {
|
||||||
|
map,
|
||||||
|
state: Vec::new(),
|
||||||
|
sticky: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns list of keys waiting to be disambiguated.
|
/// Returns list of keys waiting to be disambiguated in current mode.
|
||||||
pub fn pending(&self) -> &[KeyEvent] {
|
pub fn pending(&self) -> &[KeyEvent] {
|
||||||
&self.state
|
&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
|
/// 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
|
/// key cancels pending keystrokes. If there are no pending keystrokes but a
|
||||||
/// sticky node is in use, it will be cleared.
|
/// 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 key!(Esc) == key {
|
||||||
if !self.state.is_empty() {
|
if !self.state.is_empty() {
|
||||||
return KeymapResult::new(
|
return KeymapResult::new(
|
||||||
|
@ -414,7 +449,7 @@ impl Keymap {
|
||||||
let first = self.state.get(0).unwrap_or(&key);
|
let first = self.state.get(0).unwrap_or(&key);
|
||||||
let trie_node = match self.sticky {
|
let trie_node = match self.sticky {
|
||||||
Some(ref trie) => Cow::Owned(KeyTrie::Node(trie.clone())),
|
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]) {
|
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<Mode, Keymap>);
|
|
||||||
|
|
||||||
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<Mode, Keymap>;
|
|
||||||
|
|
||||||
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 {
|
impl Default for Keymaps {
|
||||||
|
@ -856,7 +842,7 @@ impl Default for Keymaps {
|
||||||
"C-x" => completion,
|
"C-x" => completion,
|
||||||
"C-r" => insert_register,
|
"C-r" => insert_register,
|
||||||
});
|
});
|
||||||
Self(hashmap!(
|
Self::new(hashmap!(
|
||||||
Mode::Normal => Keymap::new(normal),
|
Mode::Normal => Keymap::new(normal),
|
||||||
Mode::Select => Keymap::new(select),
|
Mode::Select => Keymap::new(select),
|
||||||
Mode::Insert => Keymap::new(insert),
|
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.
|
/// Merge default config keys with user overwritten keys for custom user config.
|
||||||
pub fn merge_keys(mut config: Config) -> Config {
|
pub fn merge_keys(mut config: Config) -> Config {
|
||||||
let mut delta = std::mem::take(&mut config.keys);
|
let mut delta = std::mem::take(&mut config.keys);
|
||||||
for (mode, keys) in &mut *config.keys {
|
for (mode, keys) in &mut config.keys.map {
|
||||||
keys.merge(delta.remove(mode).unwrap_or_default())
|
keys.merge(delta.map.remove(mode).unwrap_or_default())
|
||||||
}
|
}
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
@ -895,7 +881,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn merge_partial_keys() {
|
fn merge_partial_keys() {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
keys: Keymaps(hashmap! {
|
keys: Keymaps::new(hashmap! {
|
||||||
Mode::Normal => Keymap::new(
|
Mode::Normal => Keymap::new(
|
||||||
keymap!({ "Normal mode"
|
keymap!({ "Normal mode"
|
||||||
"i" => normal_mode,
|
"i" => normal_mode,
|
||||||
|
@ -913,23 +899,25 @@ mod tests {
|
||||||
let mut merged_config = merge_keys(config.clone());
|
let mut merged_config = merge_keys(config.clone());
|
||||||
assert_ne!(config, merged_config);
|
assert_ne!(config, merged_config);
|
||||||
|
|
||||||
let keymap = merged_config.keys.0.get_mut(&Mode::Normal).unwrap();
|
let keymap = &mut merged_config.keys;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
keymap.get(key!('i')).kind,
|
keymap.get(Mode::Normal, key!('i')).kind,
|
||||||
KeymapResultKind::Matched(MappableCommand::normal_mode),
|
KeymapResultKind::Matched(MappableCommand::normal_mode),
|
||||||
"Leaf should replace leaf"
|
"Leaf should replace leaf"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
keymap.get(key!('无')).kind,
|
keymap.get(Mode::Normal, key!('无')).kind,
|
||||||
KeymapResultKind::Matched(MappableCommand::insert_mode),
|
KeymapResultKind::Matched(MappableCommand::insert_mode),
|
||||||
"New leaf should be present in merged keymap"
|
"New leaf should be present in merged keymap"
|
||||||
);
|
);
|
||||||
// Assumes that z is a node in the default keymap
|
// Assumes that z is a node in the default keymap
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
keymap.get(key!('z')).kind,
|
keymap.get(Mode::Normal, key!('z')).kind,
|
||||||
KeymapResultKind::Matched(MappableCommand::jump_backward),
|
KeymapResultKind::Matched(MappableCommand::jump_backward),
|
||||||
"Leaf should replace node"
|
"Leaf should replace node"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let keymap = merged_config.keys.map.get_mut(&Mode::Normal).unwrap();
|
||||||
// Assumes that `g` is a node in default keymap
|
// Assumes that `g` is a node in default keymap
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
keymap.root().search(&[key!('g'), key!('$')]).unwrap(),
|
keymap.root().search(&[key!('g'), key!('$')]).unwrap(),
|
||||||
|
@ -949,14 +937,14 @@ mod tests {
|
||||||
"Old leaves in subnode should be present in merged node"
|
"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.map.get(&Mode::Normal).unwrap().len() > 1);
|
||||||
assert!(merged_config.keys.0.get(&Mode::Insert).unwrap().len() > 0);
|
assert!(merged_config.keys.map.get(&Mode::Insert).unwrap().len() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn order_should_be_set() {
|
fn order_should_be_set() {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
keys: Keymaps(hashmap! {
|
keys: Keymaps::new(hashmap! {
|
||||||
Mode::Normal => Keymap::new(
|
Mode::Normal => Keymap::new(
|
||||||
keymap!({ "Normal mode"
|
keymap!({ "Normal mode"
|
||||||
"space" => { ""
|
"space" => { ""
|
||||||
|
@ -972,7 +960,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
let mut merged_config = merge_keys(config.clone());
|
let mut merged_config = merge_keys(config.clone());
|
||||||
assert_ne!(config, merged_config);
|
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
|
// Make sure mapping works
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
keymap
|
keymap
|
||||||
|
@ -990,7 +978,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn aliased_modes_are_same_in_default_keymap() {
|
fn aliased_modes_are_same_in_default_keymap() {
|
||||||
let keymaps = Keymaps::default();
|
let keymaps = Keymaps::default();
|
||||||
let root = keymaps.get(&Mode::Normal).unwrap().root();
|
let root = keymaps.map.get(&Mode::Normal).unwrap().root();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
root.search(&[key!(' '), key!('w')]).unwrap(),
|
root.search(&[key!(' '), key!('w')]).unwrap(),
|
||||||
root.search(&["C-w".parse::<KeyEvent>().unwrap()]).unwrap(),
|
root.search(&["C-w".parse::<KeyEvent>().unwrap()]).unwrap(),
|
||||||
|
|
|
@ -703,7 +703,7 @@ impl EditorView {
|
||||||
event: KeyEvent,
|
event: KeyEvent,
|
||||||
) -> Option<KeymapResult> {
|
) -> Option<KeymapResult> {
|
||||||
cxt.editor.autoinfo = None;
|
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());
|
cxt.editor.autoinfo = key_result.sticky.map(|node| node.infobox());
|
||||||
|
|
||||||
match &key_result.kind {
|
match &key_result.kind {
|
||||||
|
@ -733,7 +733,7 @@ impl EditorView {
|
||||||
Some(ch) => commands::insert::insert_char(cx, ch),
|
Some(ch) => commands::insert::insert_char(cx, ch),
|
||||||
None => {
|
None => {
|
||||||
if let KeymapResultKind::Matched(command) =
|
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);
|
command.execute(cx);
|
||||||
}
|
}
|
||||||
|
@ -1182,8 +1182,7 @@ impl Component for EditorView {
|
||||||
// how we entered insert mode is important, and we should track that so
|
// how we entered insert mode is important, and we should track that so
|
||||||
// we can repeat the side effect.
|
// we can repeat the side effect.
|
||||||
|
|
||||||
self.last_insert.0 =
|
self.last_insert.0 = match self.keymaps.get(mode, key).kind {
|
||||||
match self.keymaps.get_mut(&mode).unwrap().get(key).kind {
|
|
||||||
KeymapResultKind::Matched(command) => command,
|
KeymapResultKind::Matched(command) => command,
|
||||||
// FIXME: insert mode can only be entered through single KeyCodes
|
// FIXME: insert mode can only be entered through single KeyCodes
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
|
|
Loading…
Reference in New Issue