From 5be7c2696c38aedba15a8ff9348f0ca6afe49eb4 Mon Sep 17 00:00:00 2001 From: Joao Pedro Castelli Date: Tue, 18 Nov 2025 17:40:25 -0300 Subject: [PATCH 1/3] add ConfigMode enum --- helix-view/src/document.rs | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 04b7703c59a5..a6b73ec1a6b2 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -110,6 +110,54 @@ impl Serialize for Mode { serializer.collect_str(self) } } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ConfigMode { + Mode(Mode), + All, +} + +impl Display for ConfigMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::All => f.write_str("all_modes"), + Self::Mode(mode) => mode.fmt(f), + } + } +} + +impl FromStr for ConfigMode { + type Err = Error; + fn from_str(s: &str) -> Result { + match s { + "all_modes" => Ok(Self::All), + _ => match Mode::from_str(s) { + Ok(mode) => Ok(ConfigMode::Mode(mode)), + Err(e) => Err(e), + }, + } + } +} + +impl<'de> Deserialize<'de> for ConfigMode { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + s.parse().map_err(de::Error::custom) + } +} + +impl Serialize for ConfigMode { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_str(self) + } +} + /// A snapshot of the text of a document that we want to write out to disk #[derive(Debug, Clone)] pub struct DocumentSavedEvent { From 269792d64fc410339782c6d01db5a570aa7bdec0 Mon Sep 17 00:00:00 2001 From: Joao Pedro Castelli Date: Tue, 18 Nov 2025 17:40:29 -0300 Subject: [PATCH 2/3] use ConfigMode to read key-remmaping in config.toml --- helix-term/src/config.rs | 88 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 6 deletions(-) diff --git a/helix-term/src/config.rs b/helix-term/src/config.rs index bcba8d8e1d45..edd6c5c3aedd 100644 --- a/helix-term/src/config.rs +++ b/helix-term/src/config.rs @@ -1,9 +1,9 @@ use crate::keymap; use crate::keymap::{merge_keys, KeyTrie}; use helix_loader::merge_toml_values; -use helix_view::document::Mode; +use helix_view::document::{ConfigMode, Mode}; use serde::Deserialize; -use std::collections::HashMap; +use std::collections::{hash_map::Entry, HashMap}; use std::fmt::Display; use std::fs; use std::io::Error as IOError; @@ -20,10 +20,37 @@ pub struct Config { #[serde(deny_unknown_fields)] pub struct ConfigRaw { pub theme: Option, - pub keys: Option>, + pub keys: Option>, pub editor: Option, } +fn convert_config_mode(mut conf_m: HashMap) -> HashMap { + let mut keys: HashMap = HashMap::new(); + if conf_m.contains_key(&ConfigMode::All) { + let trie = conf_m.remove(&ConfigMode::All).unwrap(); + keys.insert(Mode::Normal, trie.clone()); + keys.insert(Mode::Select, trie.clone()); + keys.insert(Mode::Insert, trie); + } + + for (key, trie) in conf_m { + if let ConfigMode::Mode(mode) = key { + match keys.entry(mode) { + Entry::Occupied(entry) => { + let mut node = entry.remove(); + node.merge_nodes(trie); + keys.insert(mode, node); + } + Entry::Vacant(entry) => { + entry.insert(trie); + } + } + } + } + + keys +} + impl Default for Config { fn default() -> Config { Config { @@ -68,10 +95,10 @@ impl Config { (Ok(global), Ok(local)) => { let mut keys = keymap::default(); if let Some(global_keys) = global.keys { - merge_keys(&mut keys, global_keys) + merge_keys(&mut keys, convert_config_mode(global_keys)); } if let Some(local_keys) = local.keys { - merge_keys(&mut keys, local_keys) + merge_keys(&mut keys, convert_config_mode(local_keys)); } let editor = match (global.editor, local.editor) { @@ -98,7 +125,7 @@ impl Config { (Ok(config), Err(_)) | (Err(_), Ok(config)) => { let mut keys = keymap::default(); if let Some(keymap) = config.keys { - merge_keys(&mut keys, keymap); + merge_keys(&mut keys, convert_config_mode(keymap)); } Config { theme: config.theme, @@ -174,6 +201,55 @@ mod tests { ); } + #[test] + fn parsing_keymaps_all_modes() { + use crate::keymap; + use helix_core::hashmap; + use helix_view::document::Mode; + + let sample_keymaps = r#" + [keys.normal] + A-F12 = "move_next_word_end" + + [keys.all_modes] + y = "move_line_up" + + [keys.select] + y = "move_line_down" + S-C-a = "delete_selection" + + [keys.insert] + C-s = "goto_line_start" + "#; + + let mut keys = keymap::default(); + merge_keys( + &mut keys, + hashmap! { + Mode::Insert => keymap!({ "Insert mode" + "y" => move_line_up, + "C-s" => goto_line_start, + }), + Mode::Normal => keymap!({ "Normal mode" + "A-F12" => move_next_word_end, + "y" => move_line_up, + }), + Mode::Select => keymap!({ "Select mode" + "S-C-a" => delete_selection, + "y" => move_line_down, + }), + }, + ); + + assert_eq!( + Config::load_test(sample_keymaps), + Config { + keys, + ..Default::default() + } + ); + } + #[test] fn keys_resolve_to_correct_defaults() { // From serde default From 3e1e9241a96386dffee52514f95c2bf06700ff7b Mon Sep 17 00:00:00 2001 From: Joao Pedro Castelli Date: Thu, 20 Nov 2025 17:10:28 -0300 Subject: [PATCH 3/3] add mention of all_modes in doc --- book/src/remapping.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/remapping.md b/book/src/remapping.md index 9a9a611aea20..a6ee5f20e47c 100644 --- a/book/src/remapping.md +++ b/book/src/remapping.md @@ -30,7 +30,8 @@ this: > Within macros, wrap them in `<>`, e.g. `` and `` to distinguish from the `A` or `C` keys. ```toml -# At most one section each of 'keys.normal', 'keys.insert' and 'keys.select' +# At most one section each of 'keys.normal', 'keys.insert', 'keys.select' and 'keys.all_modes' +# Use 'keys.all_modes' for bindings that apply to all three modes. [keys.normal] C-s = ":w" # Maps Ctrl-s to the typable command :w which is an alias for :write (save file) C-o = ":open ~/.config/helix/config.toml" # Maps Ctrl-o to opening of the helix config file