diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index c73412914..04a7de4ef 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -69,6 +69,21 @@ where }) } +fn deserialize_tab_width_option<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + usize::deserialize(deserializer).and_then(|n| { + if n > 0 && n <= 16 { + Ok(Some(n)) + } else { + Err(serde::de::Error::custom( + "tab width must be a value from 1 to 16 inclusive", + )) + } + }) +} + pub fn deserialize_auto_pairs<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>, @@ -537,18 +552,20 @@ pub struct DebuggerQuirks { pub absolute_paths: bool, } -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub struct IndentationConfiguration { - #[serde(deserialize_with = "deserialize_tab_width")] - pub tab_width: usize, - pub unit: String, + #[serde(deserialize_with = "deserialize_tab_width_option")] + pub tab_width: Option, + pub unit: Option, } #[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] pub struct LanguageIndentationConfiguration { - #[serde(flatten)] - pub indent: IndentationConfiguration, + #[serde(deserialize_with = "deserialize_tab_width")] + pub tab_width: usize, + pub unit: String, #[serde(default)] pub required: bool, } @@ -612,6 +629,15 @@ impl FromStr for AutoPairConfig { } } +impl Default for IndentationConfiguration { + fn default() -> Self { + IndentationConfiguration { + tab_width: None, + unit: None, + } + } +} + #[derive(Debug)] pub struct TextObjectQuery { pub query: Query, diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 4e953e8a1..8561288fd 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1081,7 +1081,7 @@ impl Document { /// is likewise auto-detected, and will remain unchanged if no line endings were detected. pub fn detect_indent_and_line_ending(&mut self) { self.indent_style = auto_detect_indent_style(&self.text).unwrap_or_else(|| { - self.indent_config(DEFAULT_INDENT, |config| IndentStyle::from_str(&config.unit)) + self.indent_config(DEFAULT_INDENT, |config| config.unit.as_ref().map(|unit| IndentStyle::from_str(unit))) }); if let Some(line_ending) = auto_detect_line_ending(&self.text) { self.line_ending = line_ending; @@ -1116,18 +1116,15 @@ impl Document { }; } - fn indent_config T>(&self, default: T, mapper: F) -> T { - self.language_config() - .and_then(|config| config.indent.as_ref()) - .filter(|config| config.required) - .map(|config| mapper(&config.indent)) - .or_else(|| self.config.load().indent.as_ref().map(&mapper)) - .or_else(|| { - self.language_config() - .and_then(|config| config.indent.as_ref()) - .map(|config| mapper(&config.indent)) - }) - .unwrap_or(default) + fn indent_config Option>(&self, default: T, mapper: F) -> T { + let mut indent = self.config.load().indent.clone(); + + if let Some(c) = self.language_config().and_then(|config| config.indent.as_ref()) { + indent.tab_width = indent.tab_width.filter(|_| !c.required).or(Some(c.tab_width)); + indent.unit = indent.unit.filter(|_| !c.required).or(Some(c.unit.clone())); + } + + mapper(indent).unwrap_or(default) } /// Reload the document from its path. diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index dbbf40f34..d8c1f29b1 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -316,8 +316,7 @@ pub struct Config { pub rulers: Vec, #[serde(default)] pub whitespace: WhitespaceConfig, - #[serde(skip_serializing_if = "Option::is_none")] - pub indent: Option, + pub indent: IndentationConfiguration, /// Persistently display open buffers along the top pub bufferline: BufferLine, /// Vertical indent width guides. @@ -966,7 +965,7 @@ impl Default for Config { terminal: get_terminal_provider(), rulers: Vec::new(), whitespace: WhitespaceConfig::default(), - indent: None, + indent: IndentationConfiguration::default(), bufferline: BufferLine::default(), indent_guides: IndentGuidesConfig::default(), color_modes: false,