diff --git a/CHANGELOG.md b/CHANGELOG.md index e8aec848df12..07f5b9aa7e06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,11 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b ### Editors +#### Bug fixes + +- Fix [#3923](https://github.com/biomejs/biome/issues/3923). Now the `.editorconfig` is correctly parsed by the LSP, and the options are correctly applied to files when formatting is triggered. + Plus, the Biome LSP now watches for any change to the `.editorconfig`, and updates the formatting settings. + ### Formatter ### JavaScript APIs diff --git a/Cargo.lock b/Cargo.lock index 47541f804ab7..45c559419b29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -932,6 +932,7 @@ dependencies = [ "biome_analyze", "biome_configuration", "biome_console", + "biome_deserialize", "biome_diagnostics", "biome_fs", "biome_rowan", diff --git a/crates/biome_cli/src/commands/check.rs b/crates/biome_cli/src/commands/check.rs index 4dd499c761f0..d0584dd2e26f 100644 --- a/crates/biome_cli/src/commands/check.rs +++ b/crates/biome_cli/src/commands/check.rs @@ -96,15 +96,8 @@ pub(crate) fn check( let should_use_editorconfig = configuration .as_ref() - .and_then(|f| f.formatter.as_ref()) - .and_then(|f| f.use_editorconfig) - .unwrap_or( - biome_configuration - .formatter - .as_ref() - .and_then(|f| f.use_editorconfig) - .unwrap_or_default(), - ); + .and_then(|c| c.use_editorconfig()) + .unwrap_or(biome_configuration.use_editorconfig().unwrap_or_default()); let mut fs_configuration = if should_use_editorconfig { let (editorconfig, editorconfig_diagnostics) = { let search_path = editorconfig_search_path.unwrap_or_else(|| { diff --git a/crates/biome_cli/src/commands/ci.rs b/crates/biome_cli/src/commands/ci.rs index 82ee9db9a2aa..29b909276813 100644 --- a/crates/biome_cli/src/commands/ci.rs +++ b/crates/biome_cli/src/commands/ci.rs @@ -51,7 +51,6 @@ pub(crate) fn ci(session: CliSession, payload: CiCommandPayload) -> Result<(), C cli_options.verbose, )?; - let editorconfig_search_path = loaded_configuration.directory_path.clone(); let LoadedConfiguration { configuration: biome_configuration, directory_path: configuration_path, @@ -60,18 +59,11 @@ pub(crate) fn ci(session: CliSession, payload: CiCommandPayload) -> Result<(), C let should_use_editorconfig = configuration .as_ref() - .and_then(|c| c.formatter.as_ref()) - .and_then(|f| f.use_editorconfig) - .unwrap_or( - biome_configuration - .formatter - .as_ref() - .and_then(|f| f.use_editorconfig) - .unwrap_or_default(), - ); + .and_then(|c| c.use_editorconfig()) + .unwrap_or(biome_configuration.use_editorconfig().unwrap_or_default()); let mut fs_configuration = if should_use_editorconfig { let (editorconfig, editorconfig_diagnostics) = { - let search_path = editorconfig_search_path.unwrap_or_else(|| { + let search_path = configuration_path.clone().unwrap_or_else(|| { let fs = &session.app.fs; fs.working_directory().unwrap_or_default() }); diff --git a/crates/biome_cli/src/commands/format.rs b/crates/biome_cli/src/commands/format.rs index 61d69dd6e470..0438e60f5706 100644 --- a/crates/biome_cli/src/commands/format.rs +++ b/crates/biome_cli/src/commands/format.rs @@ -90,14 +90,8 @@ pub(crate) fn format( let should_use_editorconfig = formatter_configuration .as_ref() - .and_then(|f| f.use_editorconfig) - .unwrap_or( - biome_configuration - .formatter - .as_ref() - .and_then(|f| f.use_editorconfig) - .unwrap_or_default(), - ); + .and_then(|c| c.use_editorconfig) + .unwrap_or(biome_configuration.use_editorconfig().unwrap_or_default()); let mut fs_configuration = if should_use_editorconfig { let (editorconfig, editorconfig_diagnostics) = { let search_path = editorconfig_search_path.unwrap_or_else(|| { diff --git a/crates/biome_configuration/src/lib.rs b/crates/biome_configuration/src/lib.rs index 2f3b23fc1bd3..e24d2c37dd12 100644 --- a/crates/biome_configuration/src/lib.rs +++ b/crates/biome_configuration/src/lib.rs @@ -311,6 +311,11 @@ impl PartialConfiguration { pub fn is_vcs_enabled(&self) -> bool { !self.is_vcs_disabled() } + + /// Whether Biome should check for `.editorconfig` file + pub fn use_editorconfig(&self) -> Option { + self.formatter.as_ref().and_then(|f| f.use_editorconfig) + } } /// The configuration of the filesystem diff --git a/crates/biome_lsp/Cargo.toml b/crates/biome_lsp/Cargo.toml index c9ddec595669..5a4f375705f1 100644 --- a/crates/biome_lsp/Cargo.toml +++ b/crates/biome_lsp/Cargo.toml @@ -18,6 +18,7 @@ anyhow = { workspace = true } biome_analyze = { workspace = true } biome_configuration = { workspace = true } biome_console = { workspace = true } +biome_deserialize = { workspace = true } biome_diagnostics = { workspace = true } biome_fs = { workspace = true } biome_rowan = { workspace = true } diff --git a/crates/biome_lsp/src/server.rs b/crates/biome_lsp/src/server.rs index 9303e03a6319..1d6ebdb4fee8 100644 --- a/crates/biome_lsp/src/server.rs +++ b/crates/biome_lsp/src/server.rs @@ -150,6 +150,13 @@ impl LSPServer { )), kind: Some(WatchKind::all()), }, + FileSystemWatcher { + glob_pattern: GlobPattern::String(format!( + "{}/.editorconfig", + base_path.display() + )), + kind: Some(WatchKind::all()), + }, // TODO: Biome 2.0 remove it FileSystemWatcher { glob_pattern: GlobPattern::String(format!( @@ -325,10 +332,11 @@ impl LanguageServer for LSPServer { let base_path = self.session.base_path(); if let Some(base_path) = base_path { let possible_rome_json = file_path.strip_prefix(&base_path); - if let Ok(possible_rome_json) = possible_rome_json { - if possible_rome_json.display().to_string() == ROME_JSON + if let Ok(watched_file) = possible_rome_json { + if watched_file.display().to_string() == ROME_JSON || ConfigName::file_names() - .contains(&&*possible_rome_json.display().to_string()) + .contains(&&*watched_file.display().to_string()) + || watched_file.ends_with(".editorconfig") { self.session.load_workspace_settings().await; self.session.load_manifest().await; diff --git a/crates/biome_lsp/src/session.rs b/crates/biome_lsp/src/session.rs index a68b93dd6780..fb73b00b18a6 100644 --- a/crates/biome_lsp/src/session.rs +++ b/crates/biome_lsp/src/session.rs @@ -8,10 +8,11 @@ use anyhow::Result; use biome_analyze::RuleCategoriesBuilder; use biome_configuration::ConfigurationPathHint; use biome_console::markup; +use biome_deserialize::Merge; use biome_diagnostics::{DiagnosticExt, Error, PrintDescription}; use biome_fs::{BiomePath, FileSystem}; use biome_service::configuration::{ - load_configuration, LoadedConfiguration, PartialConfigurationExt, + load_configuration, load_editorconfig, LoadedConfiguration, PartialConfigurationExt, }; use biome_service::file_handlers::{AstroFileHandler, SvelteFileHandler, VueFileHandler}; use biome_service::workspace::{ @@ -499,13 +500,43 @@ impl Session { ConfigurationStatus::Error } else { let LoadedConfiguration { - configuration, + configuration: fs_configuration, directory_path: configuration_path, .. } = loaded_configuration; info!("Configuration loaded successfully from disk."); info!("Update workspace settings."); + let fs = &self.fs; + let should_use_editorconfig = + fs_configuration.use_editorconfig().unwrap_or_default(); + let mut configuration = if should_use_editorconfig { + let (editorconfig, editorconfig_diagnostics) = { + let search_path = configuration_path + .clone() + .unwrap_or_else(|| fs.working_directory().unwrap_or_default()); + match load_editorconfig(fs, search_path) { + Ok(result) => result, + Err(error) => { + error!( + "Failed load the `.editorconfig` file. Reason: {}", + error + ); + self.client.log_message(MessageType::ERROR, &error).await; + return ConfigurationStatus::Error; + } + } + }; + for diagnostic in editorconfig_diagnostics { + let message = PrintDescription(&diagnostic).to_string(); + self.client.log_message(MessageType::ERROR, message).await; + } + editorconfig.unwrap_or_default() + } else { + Default::default() + }; + + configuration.merge_with(fs_configuration); let result = configuration.retrieve_gitignore_matches(fs, configuration_path.as_deref());