@@ -1,27 +1,34 @@
1−use crate::keymap::{default::default, merge_keys, Keymap};
1+use crate::keymap;
2+use crate::keymap::{merge_keys, Keymap};
3+use helix_loader::merge_toml_values;
24use helix_view::document::Mode;
35use serde::Deserialize;
46use std::collections::HashMap;
57use std::fmt::Display;
8+use std::fs;
69use std::io::Error as IOError;
7−use std::path::PathBuf;
810use toml::de::Error as TomlError;
911
10−#[derive(Debug, Clone, PartialEq, Deserialize)]
11−#[serde(deny_unknown_fields)]
12+#[derive(Debug, Clone, PartialEq)]
1213pub struct Config {
1314 pub theme: Option<String>,
14− #[serde(default = "default")]
1515 pub keys: HashMap<Mode, Keymap>,
16− #[serde(default)]
1716 pub editor: helix_view::editor::Config,
1817}
1918
19+#[derive(Debug, Clone, PartialEq, Deserialize)]
20+#[serde(deny_unknown_fields)]
21+pub struct ConfigRaw {
22+ pub theme: Option<String>,
23+ pub keys: Option<HashMap<Mode, Keymap>>,
24+ pub editor: Option<toml::Value>,
25+}
26+
2027impl Default for Config {
2128 fn default() -> Config {
2229 Config {
2330 theme: None,
24− keys: default(),
31+ keys: keymap::default(),
2532 editor: helix_view::editor::Config::default(),
2633 }
2734 }
@@ -33,6 +40,12 @@ pub enum ConfigLoadError {
3340 Error(IOError),
3441}
3542
43+impl Default for ConfigLoadError {
44+ fn default() -> Self {
45+ ConfigLoadError::Error(IOError::new(std::io::ErrorKind::NotFound, "place holder"))
46+ }
47+}
48+
3649impl Display for ConfigLoadError {
3750 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3851 match self {
@@ -43,17 +56,72 @@ impl Display for ConfigLoadError {
4356}
4457
4558impl Config {
46− pub fn load(config_path: PathBuf) -> Result<Config, ConfigLoadError> {
47− match std::fs::read_to_string(config_path) {
48− Ok(config) => toml::from_str(&config)
49− .map(merge_keys)
50− .map_err(ConfigLoadError::BadConfig),
51− Err(err) => Err(ConfigLoadError::Error(err)),
52− }
59+ pub fn load(
60+ global: Result<String, ConfigLoadError>,
61+ local: Result<String, ConfigLoadError>,
62+ ) -> Result<Config, ConfigLoadError> {
63+ let global_config: Result<ConfigRaw, ConfigLoadError> =
64+ global.and_then(|file| toml::from_str(&file).map_err(ConfigLoadError::BadConfig));
65+ let local_config: Result<ConfigRaw, ConfigLoadError> =
66+ local.and_then(|file| toml::from_str(&file).map_err(ConfigLoadError::BadConfig));
67+ let res = match (global_config, local_config) {
68+ (Ok(global), Ok(local)) => {
69+ let mut keys = keymap::default();
70+ if let Some(global_keys) = global.keys {
71+ merge_keys(&mut keys, global_keys)
72+ }
73+ if let Some(local_keys) = local.keys {
74+ merge_keys(&mut keys, local_keys)
75+ }
76+
77+ let editor = match (global.editor, local.editor) {
78+ (None, None) => helix_view::editor::Config::default(),
79+ (None, Some(val)) | (Some(val), None) => {
80+ val.try_into().map_err(ConfigLoadError::BadConfig)?
81+ }
82+ (Some(global), Some(local)) => merge_toml_values(global, local, 3)
83+ .try_into()
84+ .map_err(ConfigLoadError::BadConfig)?,
85+ };
86+
87+ Config {
88+ theme: local.theme.or(global.theme),
89+ keys,
90+ editor,
91+ }
92+ }
93+ // if any configs are invalid return that first
94+ (_, Err(ConfigLoadError::BadConfig(err)))
95+ | (Err(ConfigLoadError::BadConfig(err)), _) => {
96+ return Err(ConfigLoadError::BadConfig(err))
97+ }
98+ (Ok(config), Err(_)) | (Err(_), Ok(config)) => {
99+ let mut keys = keymap::default();
100+ if let Some(keymap) = config.keys {
101+ merge_keys(&mut keys, keymap);
102+ }
103+ Config {
104+ theme: config.theme,
105+ keys,
106+ editor: config.editor.map_or_else(
107+ || Ok(helix_view::editor::Config::default()),
108+ |val| val.try_into().map_err(ConfigLoadError::BadConfig),
109+ )?,
110+ }
111+ }
112+ // these are just two io errors return the one for the global config
113+ (Err(err), Err(_)) => return Err(err),
114+ };
115+
116+ Ok(res)
53117 }
54118
55119 pub fn load_default() -> Result<Config, ConfigLoadError> {
56− Config::load(helix_loader::config_file())
120+ let global_config =
121+ fs::read_to_string(helix_loader::config_file()).map_err(ConfigLoadError::Error);
122+ let local_config = fs::read_to_string(helix_loader::workspace_config_file())
123+ .map_err(ConfigLoadError::Error);
124+ Config::load(global_config, local_config)
57125 }
58126}
59127
@@ -61,6 +129,12 @@ impl Config {
61129mod tests {
62130 use super::*;
63131
132+ impl Config {
133+ fn load_test(config: &str) -> Config {
134+ Config::load(Ok(config.to_owned()), Err(ConfigLoadError::default())).unwrap()
135+ }
136+ }
137+
64138 #[test]
65139 fn parsing_keymaps_config_file() {
66140 use crate::keymap;
@@ -77,18 +151,24 @@ mod tests {
77151 A-F12 = "move_next_word_end"
78152 "#;
79153
154+ let mut keys = keymap::default();
155+ merge_keys(
156+ &mut keys,
157+ hashmap! {
158+ Mode::Insert => Keymap::new(keymap!({ "Insert mode"
159+ "y" => move_line_down,
160+ "S-C-a" => delete_selection,
161+ })),
162+ Mode::Normal => Keymap::new(keymap!({ "Normal mode"
163+ "A-F12" => move_next_word_end,
164+ })),
165+ },
166+ );
167+
80168 assert_eq!(
81− toml::from_str::<Config>(sample_keymaps).unwrap(),
169+ Config::load_test(sample_keymaps),
82170 Config {
83− keys: hashmap! {
84− Mode::Insert => Keymap::new(keymap!({ "Insert mode"
85− "y" => move_line_down,
86− "S-C-a" => delete_selection,
87− })),
88− Mode::Normal => Keymap::new(keymap!({ "Normal mode"
89− "A-F12" => move_next_word_end,
90− })),
91− },
171+ keys,
92172 ..Default::default()
93173 }
94174 );
@@ -97,11 +177,11 @@ mod tests {
97177 #[test]
98178 fn keys_resolve_to_correct_defaults() {
99179 // From serde default
100− let default_keys = toml::from_str::<Config>("").unwrap().keys;
101− assert_eq!(default_keys, default());
180+ let default_keys = Config::load_test("").keys;
181+ assert_eq!(default_keys, keymap::default());
102182
103183 // From the Default trait
104184 let default_keys = Config::default().keys;
105− assert_eq!(default_keys, default());
185+ assert_eq!(default_keys, keymap::default());
106186 }
107187}