From c301792ff71a056b572a2e43d1e00137287af1b7 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Sat, 23 Mar 2024 06:42:24 +0000 Subject: [PATCH] Add options `editor.indent.{tab-width,unit}`. Also add `language.indent.required`. --- helix-core/src/syntax.rs | 12 ++++++++++-- helix-view/src/document.rs | 35 ++++++++++++++++++++++++----------- helix-view/src/editor.rs | 5 ++++- languages.toml | 4 ++-- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 7de6ddf44..c73412914 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -146,7 +146,7 @@ pub struct LanguageConfiguration { )] pub language_servers: Vec, #[serde(skip_serializing_if = "Option::is_none")] - pub indent: Option, + pub indent: Option, #[serde(skip)] pub(crate) indent_query: OnceCell>, @@ -537,7 +537,7 @@ pub struct DebuggerQuirks { pub absolute_paths: bool, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub struct IndentationConfiguration { #[serde(deserialize_with = "deserialize_tab_width")] @@ -545,6 +545,14 @@ pub struct IndentationConfiguration { pub unit: String, } +#[derive(Debug, Serialize, Deserialize)] +pub struct LanguageIndentationConfiguration { + #[serde(flatten)] + pub indent: IndentationConfiguration, + #[serde(default)] + pub required: bool, +} + /// How the indentation for a newly inserted line should be determined. /// If the selected heuristic is not available (e.g. because the current /// language has no tree-sitter indent queries), a simpler one will be used. diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 91ec27874..967480d3e 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -7,7 +7,7 @@ use helix_core::auto_pairs::AutoPairs; use helix_core::chars::char_is_word; use helix_core::doc_formatter::TextFormat; use helix_core::encoding::Encoding; -use helix_core::syntax::{Highlight, LanguageServerFeature}; +use helix_core::syntax::{Highlight, LanguageServerFeature, IndentationConfiguration}; use helix_core::text_annotations::{InlineAnnotation, Overlay}; use helix_lsp::util::lsp_pos_to_pos; use helix_stdx::faccess::{copy_metadata, readonly}; @@ -653,7 +653,7 @@ impl Document { let changes = ChangeSet::new(text.slice(..)); let old_state = None; - Self { + let mut doc = Self { id: DocumentId::default(), path: None, encoding, @@ -684,7 +684,9 @@ impl Document { focused_at: std::time::Instant::now(), readonly: false, jump_labels: HashMap::new(), - } + }; + doc.detect_indent_and_line_ending(); + doc } pub fn default(config: Arc>) -> Self { @@ -1065,14 +1067,13 @@ impl Document { .or_else(|| config_loader.language_config_for_shebang(self.text().slice(..))) } - /// Detect the indentation used in the file, or otherwise defaults to the language indentation - /// configured in `languages.toml`, with a fallback to tabs if it isn't specified. Line ending + /// Detect the indentation used in the file, or otherwise defaults to the global indentation + /// configured in `config.toml` and then the language indentation configured in + /// `languages.toml`, with a fallback to tabs if it isn't specified. Line ending /// 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.language_config() - .and_then(|config| config.indent.as_ref()) - .map_or(DEFAULT_INDENT, |config| IndentStyle::from_str(&config.unit)) + self.indent_config(DEFAULT_INDENT, |config| IndentStyle::from_str(&config.unit)) }); if let Some(line_ending) = auto_detect_line_ending(&self.text) { self.line_ending = line_ending; @@ -1107,6 +1108,20 @@ 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) + } + /// Reload the document from its path. pub fn reload( &mut self, @@ -1790,9 +1805,7 @@ impl Document { /// The width that the tab character is rendered at pub fn tab_width(&self) -> usize { - self.language_config() - .and_then(|config| config.indent.as_ref()) - .map_or(4, |config| config.tab_width) // fallback to 4 columns + self.indent_config(4, |config| config.tab_width) // fallback to 4 columns } // The width (in spaces) of a level of indentation. diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 26dea3a21..dbbf40f34 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -42,7 +42,7 @@ use anyhow::{anyhow, bail, Error}; pub use helix_core::diagnostic::Severity; use helix_core::{ auto_pairs::AutoPairs, - syntax::{self, AutoPairConfig, IndentationHeuristic, LanguageServerFeature, SoftWrap}, + syntax::{self, AutoPairConfig, IndentationConfiguration, IndentationHeuristic, LanguageServerFeature, SoftWrap}, Change, LineEnding, Position, Range, Selection, Uri, NATIVE_LINE_ENDING, }; use helix_dap as dap; @@ -316,6 +316,8 @@ pub struct Config { pub rulers: Vec, #[serde(default)] pub whitespace: WhitespaceConfig, + #[serde(skip_serializing_if = "Option::is_none")] + pub indent: Option, /// Persistently display open buffers along the top pub bufferline: BufferLine, /// Vertical indent width guides. @@ -964,6 +966,7 @@ impl Default for Config { terminal: get_terminal_provider(), rulers: Vec::new(), whitespace: WhitespaceConfig::default(), + indent: None, bufferline: BufferLine::default(), indent_guides: IndentGuidesConfig::default(), color_modes: false, diff --git a/languages.toml b/languages.toml index 57d220b61..3ab227039 100644 --- a/languages.toml +++ b/languages.toml @@ -863,7 +863,7 @@ roots = ["pyproject.toml", "setup.py", "poetry.lock", "pyrightconfig.json"] comment-token = "#" language-servers = ["ruff", "jedi", "pylsp"] # TODO: pyls needs utf-8 offsets -indent = { tab-width = 4, unit = " " } +indent = { tab-width = 4, unit = " ", required = true } [[grammar]] name = "python" @@ -1308,7 +1308,7 @@ name = "yaml" scope = "source.yaml" file-types = ["yml", "yaml"] comment-token = "#" -indent = { tab-width = 2, unit = " " } +indent = { tab-width = 2, unit = " ", required = true } language-servers = [ "yaml-language-server", "ansible-language-server" ] injection-regex = "yml|yaml"