From f375f7fc493bc5171c638460fe1aaa99713cd103 Mon Sep 17 00:00:00 2001 From: rrakea Date: Fri, 29 Aug 2025 16:08:51 +0200 Subject: [PATCH 1/7] First draft --- helix-term/src/config.rs | 23 +++++++++++++++-------- helix-term/src/keymap.rs | 5 ++++- helix-term/src/keymap/default.rs | 5 +++++ helix-view/src/editor.rs | 4 ++++ 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/helix-term/src/config.rs b/helix-term/src/config.rs index bcba8d8e1d45..bcc4eb8e3e68 100644 --- a/helix-term/src/config.rs +++ b/helix-term/src/config.rs @@ -19,6 +19,7 @@ pub struct Config { #[derive(Debug, Clone, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] pub struct ConfigRaw { + // MARK RRAKEA pub theme: Option, pub keys: Option>, pub editor: Option, @@ -66,14 +67,6 @@ impl Config { local.and_then(|file| toml::from_str(&file).map_err(ConfigLoadError::BadConfig)); let res = match (global_config, local_config) { (Ok(global), Ok(local)) => { - let mut keys = keymap::default(); - if let Some(global_keys) = global.keys { - merge_keys(&mut keys, global_keys) - } - if let Some(local_keys) = local.keys { - merge_keys(&mut keys, local_keys) - } - let editor = match (global.editor, local.editor) { (None, None) => helix_view::editor::Config::default(), (None, Some(val)) | (Some(val), None) => { @@ -84,6 +77,20 @@ impl Config { .map_err(ConfigLoadError::BadConfig)?, }; + let mut keys = if editor.load_default_keymap { + keymap::default() + } else { + keymap::empty() + }; + + // MARK RRAKEA + if let Some(global_keys) = global.keys { + merge_keys(&mut keys, global_keys) + } + if let Some(local_keys) = local.keys { + merge_keys(&mut keys, local_keys) + } + Config { theme: local.theme.or(global.theme), keys, diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index d8227b500ee7..368d6a609e87 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -15,7 +15,7 @@ use std::{ sync::Arc, }; -pub use default::default; +pub use default::{default, empty}; use macros::key; #[derive(Debug, Clone, Default)] @@ -43,6 +43,7 @@ impl<'de> Deserialize<'de> for KeyTrieNode { } impl KeyTrieNode { + // MARK RRAKEA pub fn new(name: &str, map: HashMap, order: Vec) -> Self { Self { name: name.to_string(), @@ -130,6 +131,7 @@ impl DerefMut for KeyTrieNode { } } +// MARK RRAKEA #[derive(Debug, Clone, PartialEq)] pub enum KeyTrie { MappableCommand(MappableCommand), @@ -208,6 +210,7 @@ impl<'de> serde::de::Visitor<'de> for KeyTrieVisitor { } impl KeyTrie { + // MARK RRAKEA pub fn reverse_map(&self) -> ReverseKeymap { // recursively visit all nodes in keymap fn map_node(cmd_map: &mut ReverseKeymap, node: &KeyTrie, keys: &mut Vec) { diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index 5bbbd3f40429..862ba559063f 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -410,3 +410,8 @@ pub fn default() -> HashMap { Mode::Insert => insert, ) } + +// MARK RRAKEA +pub fn empty() -> HashMap { + hashmap!() +} diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 34854054b38f..f7a79bc6f244 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -248,6 +248,7 @@ where #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] +// MARK RRAKEA pub struct Config { /// Padding to keep between the edge of the screen and the cursor when scrolling. Defaults to 5. pub scrolloff: usize, @@ -381,6 +382,8 @@ pub struct Config { pub editor_config: bool, /// Whether to render rainbow colors for matching brackets. Defaults to `false`. pub rainbow_brackets: bool, + // Whether to load the default keymap + pub load_default_keymap: bool, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq, PartialOrd, Ord)] @@ -1061,6 +1064,7 @@ impl Default for Config { clipboard_provider: ClipboardProvider::default(), editor_config: true, rainbow_brackets: false, + load_default_keymap: true, } } } From b7ef2b305d84ebc0c8d73bf60ab8104ef0d41dd4 Mon Sep 17 00:00:00 2001 From: rrakea Date: Fri, 29 Aug 2025 16:12:19 +0200 Subject: [PATCH 2/7] Fixed on error branch --- helix-term/src/config.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/helix-term/src/config.rs b/helix-term/src/config.rs index bcc4eb8e3e68..e843ca1afa54 100644 --- a/helix-term/src/config.rs +++ b/helix-term/src/config.rs @@ -103,17 +103,25 @@ impl Config { return Err(ConfigLoadError::BadConfig(err)) } (Ok(config), Err(_)) | (Err(_), Ok(config)) => { - let mut keys = keymap::default(); + let editor = config.editor.map_or_else( + || Ok(helix_view::editor::Config::default()), + |val| val.try_into().map_err(ConfigLoadError::BadConfig), + )?; + + let mut keys = if editor.load_default_keymap { + keymap::default() + } else { + keymap::empty() + }; + if let Some(keymap) = config.keys { merge_keys(&mut keys, keymap); } + Config { theme: config.theme, keys, - editor: config.editor.map_or_else( - || Ok(helix_view::editor::Config::default()), - |val| val.try_into().map_err(ConfigLoadError::BadConfig), - )?, + editor, } } From a6892485466e89bdfc8a58834d2a0cb5a5e79adb Mon Sep 17 00:00:00 2001 From: rrakea Date: Fri, 29 Aug 2025 16:26:42 +0200 Subject: [PATCH 3/7] Fixed it crashing because it cant find the mode in keymap.get() --- helix-term/src/keymap.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 368d6a609e87..1f37fcf76eb3 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -322,7 +322,11 @@ impl Keymaps { pub fn contains_key(&self, mode: Mode, key: KeyEvent) -> bool { let keymaps = &*self.map(); - let keymap = &keymaps[&mode]; + let keymap = match keymaps.get(&mode) { + Some(k) => k, + None => return false, + }; + keymap .search(self.pending()) .and_then(KeyTrie::node) @@ -335,7 +339,10 @@ impl Keymaps { pub fn get(&mut self, mode: Mode, key: KeyEvent) -> KeymapResult { // TODO: remove the sticky part and look up manually let keymaps = &*self.map(); - let keymap = &keymaps[&mode]; + let keymap = match keymaps.get(&mode) { + Some(k) => k, + None => return KeymapResult::NotFound, + }; if key!(Esc) == key { if !self.state.is_empty() { From 6810657e7a4ae91c7c43ca0164a9bf79c89ff9ce Mon Sep 17 00:00:00 2001 From: rrakea Date: Fri, 29 Aug 2025 18:51:30 +0200 Subject: [PATCH 4/7] Added the modes into empty() since other functions rely on the map already having them --- helix-term/src/keymap.rs | 2 ++ helix-term/src/keymap/default.rs | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 1f37fcf76eb3..4e1da0b90251 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -66,6 +66,7 @@ impl KeyTrieNode { } self.map.insert(key, trie); } + // MARK RRAKEA for &key in self.map.keys() { if !self.order.contains(&key) { self.order.push(key); @@ -397,6 +398,7 @@ impl Default for Keymaps { } } +// MARK RRAKEA /// Merge default config keys with user overwritten keys for custom user config. pub fn merge_keys(dst: &mut HashMap, mut delta: HashMap) { for (mode, keys) in dst { diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index 862ba559063f..6389fc58fc47 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; +use crate::keymap::KeyTrieNode; + use super::macros::keymap; use super::{KeyTrie, Mode}; use helix_core::hashmap; @@ -413,5 +415,9 @@ pub fn default() -> HashMap { // MARK RRAKEA pub fn empty() -> HashMap { - hashmap!() + hashmap!( + Mode::Normal => KeyTrie::Node(KeyTrieNode::new("", HashMap::new(), Vec::new())), + Mode::Select => KeyTrie::Node(KeyTrieNode::new("", HashMap::new(), Vec::new())), + Mode::Insert => KeyTrie::Node(KeyTrieNode::new("", HashMap::new(), Vec::new())), + ) } From 30b8695405d76ac2c1555b2340258eaf15b5469b Mon Sep 17 00:00:00 2001 From: rrakea Date: Fri, 29 Aug 2025 18:57:41 +0200 Subject: [PATCH 5/7] Removed comments --- helix-term/src/config.rs | 2 -- helix-term/src/keymap.rs | 5 ----- helix-term/src/keymap/default.rs | 1 - helix-view/src/editor.rs | 1 - 4 files changed, 9 deletions(-) diff --git a/helix-term/src/config.rs b/helix-term/src/config.rs index e843ca1afa54..5190784cee50 100644 --- a/helix-term/src/config.rs +++ b/helix-term/src/config.rs @@ -19,7 +19,6 @@ pub struct Config { #[derive(Debug, Clone, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] pub struct ConfigRaw { - // MARK RRAKEA pub theme: Option, pub keys: Option>, pub editor: Option, @@ -83,7 +82,6 @@ impl Config { keymap::empty() }; - // MARK RRAKEA if let Some(global_keys) = global.keys { merge_keys(&mut keys, global_keys) } diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 4e1da0b90251..3c676caf353e 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -43,7 +43,6 @@ impl<'de> Deserialize<'de> for KeyTrieNode { } impl KeyTrieNode { - // MARK RRAKEA pub fn new(name: &str, map: HashMap, order: Vec) -> Self { Self { name: name.to_string(), @@ -66,7 +65,6 @@ impl KeyTrieNode { } self.map.insert(key, trie); } - // MARK RRAKEA for &key in self.map.keys() { if !self.order.contains(&key) { self.order.push(key); @@ -132,7 +130,6 @@ impl DerefMut for KeyTrieNode { } } -// MARK RRAKEA #[derive(Debug, Clone, PartialEq)] pub enum KeyTrie { MappableCommand(MappableCommand), @@ -211,7 +208,6 @@ impl<'de> serde::de::Visitor<'de> for KeyTrieVisitor { } impl KeyTrie { - // MARK RRAKEA pub fn reverse_map(&self) -> ReverseKeymap { // recursively visit all nodes in keymap fn map_node(cmd_map: &mut ReverseKeymap, node: &KeyTrie, keys: &mut Vec) { @@ -398,7 +394,6 @@ impl Default for Keymaps { } } -// MARK RRAKEA /// Merge default config keys with user overwritten keys for custom user config. pub fn merge_keys(dst: &mut HashMap, mut delta: HashMap) { for (mode, keys) in dst { diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index 6389fc58fc47..3e742f6f87bc 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -413,7 +413,6 @@ pub fn default() -> HashMap { ) } -// MARK RRAKEA pub fn empty() -> HashMap { hashmap!( Mode::Normal => KeyTrie::Node(KeyTrieNode::new("", HashMap::new(), Vec::new())), diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index f7a79bc6f244..9bebe3efa1a2 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -248,7 +248,6 @@ where #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] -// MARK RRAKEA pub struct Config { /// Padding to keep between the edge of the screen and the cursor when scrolling. Defaults to 5. pub scrolloff: usize, From 83e70eb5b14456ab8a49c7e7ee01652c0388a72d Mon Sep 17 00:00:00 2001 From: rrakea Date: Fri, 29 Aug 2025 18:59:57 +0200 Subject: [PATCH 6/7] Reverted the changes to panicing if a mode is missing --- helix-term/src/keymap.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 3c676caf353e..4736a7c393db 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -319,11 +319,7 @@ impl Keymaps { pub fn contains_key(&self, mode: Mode, key: KeyEvent) -> bool { let keymaps = &*self.map(); - let keymap = match keymaps.get(&mode) { - Some(k) => k, - None => return false, - }; - + let keymap = &keymaps[&mode]; keymap .search(self.pending()) .and_then(KeyTrie::node) @@ -336,10 +332,7 @@ impl Keymaps { pub fn get(&mut self, mode: Mode, key: KeyEvent) -> KeymapResult { // TODO: remove the sticky part and look up manually let keymaps = &*self.map(); - let keymap = match keymaps.get(&mode) { - Some(k) => k, - None => return KeymapResult::NotFound, - }; + let keymap = &keymaps[&mode]; if key!(Esc) == key { if !self.state.is_empty() { From 001481cc2585e3c255a6e097d59151b5c1eade7a Mon Sep 17 00:00:00 2001 From: rrakea Date: Fri, 29 Aug 2025 19:18:37 +0200 Subject: [PATCH 7/7] Added documentation --- book/src/editor.md | 1 + 1 file changed, 1 insertion(+) diff --git a/book/src/editor.md b/book/src/editor.md index b5544a06297a..b9516dd546e5 100644 --- a/book/src/editor.md +++ b/book/src/editor.md @@ -64,6 +64,7 @@ | `clipboard-provider` | Which API to use for clipboard interaction. One of `pasteboard` (MacOS), `wayland`, `x-clip`, `x-sel`, `win-32-yank`, `termux`, `tmux`, `windows`, `termcode`, `none`, or a custom command set. | Platform and environment specific. | | `editor-config` | Whether to read settings from [EditorConfig](https://editorconfig.org) files | `true` | | `rainbow-brackets` | Whether to render rainbow colors for matching brackets. Requires tree-sitter `rainbows.scm` queries for the language. | `false` | +| `load-default-keymap` | Whether to load the default before applying the keymap from your config file. Useful for using non qwerty keyboard layouts. | `true` | ### `[editor.clipboard-provider]` Section