From d51fd9f1969865354e937117b8b0db0d17ce1625 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sat, 17 Aug 2024 00:57:01 +0200 Subject: [PATCH 1/3] Define workspace level configs. --- crates/rust-analyzer/src/config.rs | 234 +++++++++++-------- crates/rust-analyzer/src/handlers/request.rs | 6 +- crates/rust-analyzer/src/lsp/capabilities.rs | 2 +- crates/rust-analyzer/src/reload.rs | 2 +- 4 files changed, 148 insertions(+), 96 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index d7f24e6ce1..ab27a65de5 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -436,6 +436,13 @@ config_data! { } } +config_data! { + workspace: struct WorkspaceDefaultConfigData <- WorkspaceConfigInput -> { + + + } +} + config_data! { /// Configs that only make sense when they are set by a client. As such they can only be defined /// by setting them using client's settings (e.g `settings.json` on VS Code). @@ -738,7 +745,7 @@ pub enum RatomlFileKind { // FIXME @alibektas : Seems like a clippy warning of this sort should tell that combining different ConfigInputs into one enum was not a good idea. #[allow(clippy::large_enum_variant)] enum RatomlFile { - Workspace(GlobalLocalConfigInput), + Workspace(WorkspaceLocalConfigInput), Crate(LocalConfigInput), } @@ -962,7 +969,7 @@ impl Config { source_root_id, ( RatomlFile::Workspace( - GlobalLocalConfigInput::from_toml( + WorkspaceLocalConfigInput::from_toml( table, &mut toml_errors, ), @@ -1000,7 +1007,7 @@ impl Config { config.source_root_parent_map = source_root_map; } - if config.check_command(None).is_empty() { + if config.check_command().is_empty() { config.validation_errors.0.push(Arc::new(ConfigErrorInner::Json { config_key: "/check/command".to_owned(), error: serde_json::Error::custom("expected a non-empty string"), @@ -1444,11 +1451,11 @@ impl Config { pub fn diagnostics(&self, source_root: Option) -> DiagnosticsConfig { DiagnosticsConfig { - enabled: *self.diagnostics_enable(source_root), + enabled: *self.diagnostics_enable(), proc_attr_macros_enabled: self.expand_proc_attr_macros(), proc_macros_enabled: *self.procMacro_enable(), - disable_experimental: !self.diagnostics_experimental_enable(source_root), - disabled: self.diagnostics_disabled(source_root).clone(), + disable_experimental: !self.diagnostics_experimental_enable(), + disabled: self.diagnostics_disabled().clone(), expr_fill_default: match self.assist_expressionFillDefault(source_root) { ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo, ExprFillDefaultDef::Default => ExprFillDefaultMode::Default, @@ -1458,7 +1465,7 @@ impl Config { prefer_no_std: self.imports_preferNoStd(source_root).to_owned(), prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(), - style_lints: self.diagnostics_styleLints_enable(source_root).to_owned(), + style_lints: self.diagnostics_styleLints_enable().to_owned(), term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64, term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(), } @@ -1651,11 +1658,11 @@ impl Config { } pub fn has_linked_projects(&self) -> bool { - !self.linkedProjects(None).is_empty() + !self.linkedProjects().is_empty() } pub fn linked_manifests(&self) -> impl Iterator + '_ { - self.linkedProjects(None).iter().filter_map(|it| match it { + self.linkedProjects().iter().filter_map(|it| match it { ManifestOrProjectJson::Manifest(p) => Some(&**p), // despite having a buildfile, using this variant as a manifest // will fail. @@ -1665,20 +1672,20 @@ impl Config { } pub fn has_linked_project_jsons(&self) -> bool { - self.linkedProjects(None) + self.linkedProjects() .iter() .any(|it| matches!(it, ManifestOrProjectJson::ProjectJson { .. })) } pub fn discover_workspace_config(&self) -> Option<&DiscoverWorkspaceConfig> { - self.workspace_discoverConfig(None).as_ref() + self.workspace_discoverConfig().as_ref() } pub fn linked_or_discovered_projects(&self) -> Vec { - match self.linkedProjects(None).as_slice() { + match self.linkedProjects().as_slice() { [] => { let exclude_dirs: Vec<_> = - self.files_excludeDirs(None).iter().map(|p| self.root_path.join(p)).collect(); + self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect(); self.discovered_projects .iter() .filter(|project| { @@ -1713,48 +1720,48 @@ impl Config { } pub fn prefill_caches(&self) -> bool { - self.cachePriming_enable(None).to_owned() + self.cachePriming_enable().to_owned() } pub fn publish_diagnostics(&self) -> bool { - self.diagnostics_enable(None).to_owned() + self.diagnostics_enable().to_owned() } pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { DiagnosticsMapConfig { - remap_prefix: self.diagnostics_remapPrefix(None).clone(), - warnings_as_info: self.diagnostics_warningsAsInfo(None).clone(), - warnings_as_hint: self.diagnostics_warningsAsHint(None).clone(), - check_ignore: self.check_ignore(None).clone(), + remap_prefix: self.diagnostics_remapPrefix().clone(), + warnings_as_info: self.diagnostics_warningsAsInfo().clone(), + warnings_as_hint: self.diagnostics_warningsAsHint().clone(), + check_ignore: self.check_ignore().clone(), } } pub fn extra_args(&self) -> &Vec { - self.cargo_extraArgs(None) + self.cargo_extraArgs() } pub fn extra_env(&self) -> &FxHashMap { - self.cargo_extraEnv(None) + self.cargo_extraEnv() } pub fn check_extra_args(&self) -> Vec { let mut extra_args = self.extra_args().clone(); - extra_args.extend_from_slice(self.check_extraArgs(None)); + extra_args.extend_from_slice(self.check_extraArgs()); extra_args } pub fn check_extra_env(&self) -> FxHashMap { - let mut extra_env = self.cargo_extraEnv(None).clone(); - extra_env.extend(self.check_extraEnv(None).clone()); + let mut extra_env = self.cargo_extraEnv().clone(); + extra_env.extend(self.check_extraEnv().clone()); extra_env } pub fn lru_parse_query_capacity(&self) -> Option { - self.lru_capacity(None).to_owned() + self.lru_capacity().to_owned() } pub fn lru_query_capacities_config(&self) -> Option<&FxHashMap, u16>> { - self.lru_query_capacities(None).is_empty().not().then(|| self.lru_query_capacities(None)) + self.lru_query_capacities().is_empty().not().then(|| self.lru_query_capacities()) } pub fn proc_macro_srv(&self) -> Option { @@ -1763,7 +1770,7 @@ impl Config { } pub fn ignored_proc_macros(&self) -> &FxHashMap, Box<[Box]>> { - self.procMacro_ignored(None) + self.procMacro_ignored() } pub fn expand_proc_macros(&self) -> bool { @@ -1778,11 +1785,7 @@ impl Config { } _ => FilesWatcher::Server, }, - exclude: self - .files_excludeDirs(None) - .iter() - .map(|it| self.root_path.join(it)) - .collect(), + exclude: self.files_excludeDirs().iter().map(|it| self.root_path.join(it)).collect(), } } @@ -1793,22 +1796,22 @@ impl Config { } pub fn cargo_autoreload_config(&self) -> bool { - self.cargo_autoreload(None).to_owned() + self.cargo_autoreload().to_owned() } pub fn run_build_scripts(&self) -> bool { - self.cargo_buildScripts_enable(None).to_owned() || self.procMacro_enable().to_owned() + self.cargo_buildScripts_enable().to_owned() || self.procMacro_enable().to_owned() } pub fn cargo(&self) -> CargoConfig { - let rustc_source = self.rustc_source(None).as_ref().map(|rustc_src| { + let rustc_source = self.rustc_source().as_ref().map(|rustc_src| { if rustc_src == "discover" { RustLibSource::Discover } else { RustLibSource::Path(self.root_path.join(rustc_src)) } }); - let sysroot = self.cargo_sysroot(None).as_ref().map(|sysroot| { + let sysroot = self.cargo_sysroot().as_ref().map(|sysroot| { if sysroot == "discover" { RustLibSource::Discover } else { @@ -1816,24 +1819,24 @@ impl Config { } }); let sysroot_src = - self.cargo_sysrootSrc(None).as_ref().map(|sysroot| self.root_path.join(sysroot)); + self.cargo_sysrootSrc().as_ref().map(|sysroot| self.root_path.join(sysroot)); CargoConfig { - all_targets: *self.cargo_allTargets(None), - features: match &self.cargo_features(None) { + all_targets: *self.cargo_allTargets(), + features: match &self.cargo_features() { CargoFeaturesDef::All => CargoFeatures::All, CargoFeaturesDef::Selected(features) => CargoFeatures::Selected { features: features.clone(), - no_default_features: self.cargo_noDefaultFeatures(None).to_owned(), + no_default_features: self.cargo_noDefaultFeatures().to_owned(), }, }, - target: self.cargo_target(None).clone(), + target: self.cargo_target().clone(), sysroot, sysroot_src, rustc_source, cfg_overrides: project_model::CfgOverrides { global: CfgDiff::new( - self.cargo_cfgs(None) + self.cargo_cfgs() .iter() .map(|(key, val)| match val { Some(val) => CfgAtom::KeyValue { @@ -1848,43 +1851,43 @@ impl Config { .unwrap(), selective: Default::default(), }, - wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(None), - invocation_strategy: match self.cargo_buildScripts_invocationStrategy(None) { + wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(), + invocation_strategy: match self.cargo_buildScripts_invocationStrategy() { InvocationStrategy::Once => project_model::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace, }, - run_build_script_command: self.cargo_buildScripts_overrideCommand(None).clone(), - extra_args: self.cargo_extraArgs(None).clone(), - extra_env: self.cargo_extraEnv(None).clone(), + run_build_script_command: self.cargo_buildScripts_overrideCommand().clone(), + extra_args: self.cargo_extraArgs().clone(), + extra_env: self.cargo_extraEnv().clone(), target_dir: self.target_dir_from_config(), } } - pub fn rustfmt(&self, source_root_id: Option) -> RustfmtConfig { - match &self.rustfmt_overrideCommand(source_root_id) { + pub fn rustfmt(&self) -> RustfmtConfig { + match &self.rustfmt_overrideCommand() { Some(args) if !args.is_empty() => { let mut args = args.clone(); let command = args.remove(0); RustfmtConfig::CustomCommand { command, args } } Some(_) | None => RustfmtConfig::Rustfmt { - extra_args: self.rustfmt_extraArgs(source_root_id).clone(), - enable_range_formatting: *self.rustfmt_rangeFormatting_enable(source_root_id), + extra_args: self.rustfmt_extraArgs().clone(), + enable_range_formatting: *self.rustfmt_rangeFormatting_enable(), }, } } pub fn flycheck_workspace(&self) -> bool { - *self.check_workspace(None) + *self.check_workspace() } pub(crate) fn cargo_test_options(&self) -> CargoOptions { CargoOptions { - target_triples: self.cargo_target(None).clone().into_iter().collect(), + target_triples: self.cargo_target().clone().into_iter().collect(), all_targets: false, - no_default_features: *self.cargo_noDefaultFeatures(None), - all_features: matches!(self.cargo_features(None), CargoFeaturesDef::All), - features: match self.cargo_features(None).clone() { + no_default_features: *self.cargo_noDefaultFeatures(), + all_features: matches!(self.cargo_features(), CargoFeaturesDef::All), + features: match self.cargo_features().clone() { CargoFeaturesDef::All => vec![], CargoFeaturesDef::Selected(it) => it, }, @@ -1895,7 +1898,7 @@ impl Config { } pub(crate) fn flycheck(&self) -> FlycheckConfig { - match &self.check_overrideCommand(None) { + match &self.check_overrideCommand() { Some(args) if !args.is_empty() => { let mut args = args.clone(); let command = args.remove(0); @@ -1903,7 +1906,7 @@ impl Config { command, args, extra_env: self.check_extra_env(), - invocation_strategy: match self.check_invocationStrategy(None) { + invocation_strategy: match self.check_invocationStrategy() { InvocationStrategy::Once => crate::flycheck::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => { crate::flycheck::InvocationStrategy::PerWorkspace @@ -1912,30 +1915,28 @@ impl Config { } } Some(_) | None => FlycheckConfig::CargoCommand { - command: self.check_command(None).clone(), + command: self.check_command().clone(), options: CargoOptions { target_triples: self - .check_targets(None) + .check_targets() .clone() .and_then(|targets| match &targets.0[..] { [] => None, targets => Some(targets.into()), }) - .unwrap_or_else(|| self.cargo_target(None).clone().into_iter().collect()), - all_targets: self - .check_allTargets(None) - .unwrap_or(*self.cargo_allTargets(None)), + .unwrap_or_else(|| self.cargo_target().clone().into_iter().collect()), + all_targets: self.check_allTargets().unwrap_or(*self.cargo_allTargets()), no_default_features: self - .check_noDefaultFeatures(None) - .unwrap_or(*self.cargo_noDefaultFeatures(None)), + .check_noDefaultFeatures() + .unwrap_or(*self.cargo_noDefaultFeatures()), all_features: matches!( - self.check_features(None).as_ref().unwrap_or(self.cargo_features(None)), + self.check_features().as_ref().unwrap_or(self.cargo_features()), CargoFeaturesDef::All ), features: match self - .check_features(None) + .check_features() .clone() - .unwrap_or_else(|| self.cargo_features(None).clone()) + .unwrap_or_else(|| self.cargo_features().clone()) { CargoFeaturesDef::All => vec![], CargoFeaturesDef::Selected(it) => it, @@ -1950,7 +1951,7 @@ impl Config { } fn target_dir_from_config(&self) -> Option { - self.cargo_targetDir(None).as_ref().and_then(|target_dir| match target_dir { + self.cargo_targetDir().as_ref().and_then(|target_dir| match target_dir { TargetDirectory::UseSubdirectory(true) => { Some(Utf8PathBuf::from("target/rust-analyzer")) } @@ -1961,18 +1962,18 @@ impl Config { } pub fn check_on_save(&self) -> bool { - *self.checkOnSave(None) + *self.checkOnSave() } pub fn script_rebuild_on_save(&self) -> bool { - *self.cargo_buildScripts_rebuildOnSave(None) + *self.cargo_buildScripts_rebuildOnSave() } pub fn runnables(&self) -> RunnablesConfig { RunnablesConfig { - override_cargo: self.runnables_command(None).clone(), - cargo_extra_args: self.runnables_extraArgs(None).clone(), - extra_test_binary_args: self.runnables_extraTestBinaryArgs(None).clone(), + override_cargo: self.runnables_command().clone(), + cargo_extra_args: self.runnables_extraArgs().clone(), + extra_test_binary_args: self.runnables_extraTestBinaryArgs().clone(), } } @@ -2043,7 +2044,7 @@ impl Config { } pub fn prime_caches_num_threads(&self) -> usize { - match self.cachePriming_numThreads(None) { + match self.cachePriming_numThreads() { NumThreads::Concrete(0) | NumThreads::Physical => num_cpus::get_physical(), &NumThreads::Concrete(n) => n, NumThreads::Logical => num_cpus::get(), @@ -2542,6 +2543,45 @@ macro_rules! _impl_for_config_data { )* } }; + (workspace, $( + $(#[doc=$doc:literal])* + $vis:vis $field:ident : $ty:ty = $default:expr, + )* + ) => { + impl Config { + $( + $($doc)* + #[allow(non_snake_case)] + $vis fn $field(&self, source_root: Option) -> &$ty { + let mut source_root = source_root.as_ref(); + while let Some(sr) = source_root { + if let Some((file, _)) = self.ratoml_file.get(&sr) { + match file { + RatomlFile::Workspace(config) => { + if let Some(v) = config.workspace.$field.as_ref() { + return &v; + } + }, + } + } + source_root = self.source_root_parent_map.get(&sr); + } + + if let Some(v) = self.client_config.0.local.$field.as_ref() { + return &v; + } + + if let Some((user_config, _)) = self.user_config.as_ref() { + if let Some(v) = user_config.local.$field.as_ref() { + return &v; + } + } + + &self.default_config.workspace.$field + } + )* + } + }; (global, $( $(#[doc=$doc:literal])* $vis:vis $field:ident : $ty:ty = $default:expr, @@ -2551,18 +2591,8 @@ macro_rules! _impl_for_config_data { $( $($doc)* #[allow(non_snake_case)] - $vis fn $field(&self, source_root : Option) -> &$ty { - let mut source_root = source_root.as_ref(); - while let Some(sr) = source_root { - if let Some((RatomlFile::Workspace(config), _)) = self.ratoml_file.get(&sr) { - if let Some(v) = config.global.$field.as_ref() { - return &v; - } - } - - source_root = self.source_root_parent_map.get(&sr); - } - + // TODO Remove source_root + $vis fn $field(&self) -> &$ty { if let Some(v) = self.client_config.0.global.$field.as_ref() { return &v; } @@ -2772,6 +2802,28 @@ impl GlobalLocalConfigInput { } } +/// All of the config levels, all fields `Option`, to describe fields that are actually set by +/// some rust-analyzer.toml file or JSON blob. An empty rust-analyzer.toml corresponds to +/// all fields being None. +#[derive(Debug, Clone, Default)] +#[allow(dead_code)] +struct WorkspaceLocalConfigInput { + workspace: WorkspaceConfigInput, + local: LocalConfigInput, +} + +impl WorkspaceLocalConfigInput { + #[allow(dead_code)] + const FIELDS: &'static [&'static [&'static str]] = + &[WorkspaceConfigInput::FIELDS, LocalConfigInput::FIELDS]; + fn from_toml(toml: toml::Table, error_sink: &mut Vec<(String, toml::de::Error)>) -> Self { + Self { + workspace: WorkspaceConfigInput::from_toml(&toml, error_sink), + local: LocalConfigInput::from_toml(&toml, error_sink), + } + } +} + fn get_field_json( json: &mut serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>, @@ -3475,7 +3527,7 @@ mod tests { })); (config, _, _) = config.apply_change(change); - assert_eq!(config.cargo_targetDir(None), &None); + assert_eq!(config.cargo_targetDir(), &None); assert!( matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none()) ); @@ -3493,7 +3545,7 @@ mod tests { (config, _, _) = config.apply_change(change); - assert_eq!(config.cargo_targetDir(None), &Some(TargetDirectory::UseSubdirectory(true))); + assert_eq!(config.cargo_targetDir(), &Some(TargetDirectory::UseSubdirectory(true))); assert!( matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer"))) ); @@ -3512,7 +3564,7 @@ mod tests { (config, _, _) = config.apply_change(change); assert_eq!( - config.cargo_targetDir(None), + config.cargo_targetDir(), &Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder"))) ); assert!( diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 34325ac7a9..1475d03ca6 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -2113,9 +2113,8 @@ fn run_rustfmt( let edition = editions.iter().copied().max(); let line_index = snap.file_line_index(file_id)?; - let sr = snap.analysis.source_root_id(file_id)?; - let mut command = match snap.config.rustfmt(Some(sr)) { + let mut command = match snap.config.rustfmt() { RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => { // FIXME: Set RUSTUP_TOOLCHAIN let mut cmd = process::Command::new(toolchain::Tool::Rustfmt.path()); @@ -2303,8 +2302,9 @@ pub(crate) fn internal_testing_fetch_config( .transpose()?; serde_json::to_value(match &*params.config { "local" => state.config.assist(source_root).assist_emit_must_use, + // TODO Most probably will fail because it was not renamed to workspace "global" => matches!( - state.config.rustfmt(source_root), + state.config.rustfmt(), RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } ), _ => return Err(anyhow::anyhow!("Unknown test config key: {}", params.config)), diff --git a/crates/rust-analyzer/src/lsp/capabilities.rs b/crates/rust-analyzer/src/lsp/capabilities.rs index 9610808c27..212294b5d3 100644 --- a/crates/rust-analyzer/src/lsp/capabilities.rs +++ b/crates/rust-analyzer/src/lsp/capabilities.rs @@ -67,7 +67,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities { code_action_provider: Some(config.caps().code_action_capabilities()), code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), document_formatting_provider: Some(OneOf::Left(true)), - document_range_formatting_provider: match config.rustfmt(None) { + document_range_formatting_provider: match config.rustfmt() { RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)), _ => Some(OneOf::Left(false)), }, diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 884a8e8347..4a3e8ae050 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -122,7 +122,7 @@ impl GlobalState { }; let mut message = String::new(); - if !self.config.cargo_autoreload(None) + if !self.config.cargo_autoreload() && self.is_quiescent() && self.fetch_workspaces_queue.op_requested() && self.config.discover_workspace_config().is_none() From 94ed6217ddad1cac94918064b0c2b0aba7bbabc4 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sat, 17 Aug 2024 01:48:23 +0200 Subject: [PATCH 2/3] Next up : generating configs for workspace level configs --- crates/rust-analyzer/src/config.rs | 70 +++++++++---------- crates/rust-analyzer/src/handlers/request.rs | 8 +-- crates/rust-analyzer/src/lsp/capabilities.rs | 2 +- .../rust-analyzer/tests/slow-tests/ratoml.rs | 18 +++-- docs/user/generated_config.adoc | 21 ------ editors/code/package.json | 39 ----------- 6 files changed, 48 insertions(+), 110 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index ab27a65de5..437f7c33ce 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -294,18 +294,6 @@ config_data! { /// This option does not take effect until rust-analyzer is restarted. rustc_source: Option = None, - /// Additional arguments to `rustfmt`. - rustfmt_extraArgs: Vec = vec![], - /// Advanced option, fully override the command rust-analyzer uses for - /// formatting. This should be the equivalent of `rustfmt` here, and - /// not that of `cargo fmt`. The file contents will be passed on the - /// standard input and the formatted result will be read from the - /// standard output. - rustfmt_overrideCommand: Option> = None, - /// Enables the use of rustfmt's unstable range formatting command for the - /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only - /// available on a nightly build. - rustfmt_rangeFormatting_enable: bool = false, /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. /// @@ -439,6 +427,18 @@ config_data! { config_data! { workspace: struct WorkspaceDefaultConfigData <- WorkspaceConfigInput -> { + /// Additional arguments to `rustfmt`. + rustfmt_extraArgs: Vec = vec![], + /// Advanced option, fully override the command rust-analyzer uses for + /// formatting. This should be the equivalent of `rustfmt` here, and + /// not that of `cargo fmt`. The file contents will be passed on the + /// standard input and the formatted result will be read from the + /// standard output. + rustfmt_overrideCommand: Option> = None, + /// Enables the use of rustfmt's unstable range formatting command for the + /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only + /// available on a nightly build. + rustfmt_rangeFormatting_enable: bool = false, } } @@ -775,7 +775,7 @@ pub struct Config { user_config_path: VfsPath, /// Config node whose values apply to **every** Rust project. - user_config: Option<(GlobalLocalConfigInput, ConfigErrors)>, + user_config: Option<(GlobalWorkspaceLocalConfigInput, ConfigErrors)>, ratoml_file: FxHashMap, @@ -825,13 +825,13 @@ impl Config { if let Ok(table) = toml::from_str(&change) { let mut toml_errors = vec![]; validate_toml_table( - GlobalLocalConfigInput::FIELDS, + GlobalWorkspaceLocalConfigInput::FIELDS, &table, &mut String::new(), &mut toml_errors, ); config.user_config = Some(( - GlobalLocalConfigInput::from_toml(table, &mut toml_errors), + GlobalWorkspaceLocalConfigInput::from_toml(table, &mut toml_errors), ConfigErrors( toml_errors .into_iter() @@ -960,7 +960,7 @@ impl Config { match toml::from_str(&text) { Ok(table) => { validate_toml_table( - GlobalLocalConfigInput::FIELDS, + WorkspaceLocalConfigInput::FIELDS, &table, &mut String::new(), &mut toml_errors, @@ -1863,16 +1863,16 @@ impl Config { } } - pub fn rustfmt(&self) -> RustfmtConfig { - match &self.rustfmt_overrideCommand() { + pub fn rustfmt(&self, source_root_id: Option) -> RustfmtConfig { + match &self.rustfmt_overrideCommand(source_root_id) { Some(args) if !args.is_empty() => { let mut args = args.clone(); let command = args.remove(0); RustfmtConfig::CustomCommand { command, args } } Some(_) | None => RustfmtConfig::Rustfmt { - extra_args: self.rustfmt_extraArgs().clone(), - enable_range_formatting: *self.rustfmt_rangeFormatting_enable(), + extra_args: self.rustfmt_extraArgs(source_root_id).clone(), + enable_range_formatting: *self.rustfmt_rangeFormatting_enable(source_root_id), }, } } @@ -2555,24 +2555,20 @@ macro_rules! _impl_for_config_data { $vis fn $field(&self, source_root: Option) -> &$ty { let mut source_root = source_root.as_ref(); while let Some(sr) = source_root { - if let Some((file, _)) = self.ratoml_file.get(&sr) { - match file { - RatomlFile::Workspace(config) => { - if let Some(v) = config.workspace.$field.as_ref() { - return &v; - } - }, + if let Some((RatomlFile::Workspace(config), _)) = self.ratoml_file.get(&sr) { + if let Some(v) = config.workspace.$field.as_ref() { + return &v; } } source_root = self.source_root_parent_map.get(&sr); } - if let Some(v) = self.client_config.0.local.$field.as_ref() { + if let Some(v) = self.client_config.0.workspace.$field.as_ref() { return &v; } if let Some((user_config, _)) = self.user_config.as_ref() { - if let Some(v) = user_config.local.$field.as_ref() { + if let Some(v) = user_config.workspace.$field.as_ref() { return &v; } } @@ -2591,7 +2587,6 @@ macro_rules! _impl_for_config_data { $( $($doc)* #[allow(non_snake_case)] - // TODO Remove source_root $vis fn $field(&self) -> &$ty { if let Some(v) = self.client_config.0.global.$field.as_ref() { return &v; @@ -2730,6 +2725,7 @@ use _config_data as config_data; #[derive(Default, Debug, Clone)] struct DefaultConfigData { global: GlobalDefaultConfigData, + workspace: WorkspaceDefaultConfigData, local: LocalDefaultConfigData, client: ClientDefaultConfigData, } @@ -2740,6 +2736,7 @@ struct DefaultConfigData { #[derive(Debug, Clone, Default)] struct FullConfigInput { global: GlobalConfigInput, + workspace: WorkspaceConfigInput, local: LocalConfigInput, client: ClientConfigInput, } @@ -2753,6 +2750,7 @@ impl FullConfigInput { global: GlobalConfigInput::from_json(&mut json, error_sink), local: LocalConfigInput::from_json(&mut json, error_sink), client: ClientConfigInput::from_json(&mut json, error_sink), + workspace: WorkspaceConfigInput::from_json(&mut json, error_sink), } } @@ -2783,26 +2781,28 @@ impl FullConfigInput { /// some rust-analyzer.toml file or JSON blob. An empty rust-analyzer.toml corresponds to /// all fields being None. #[derive(Debug, Clone, Default)] -struct GlobalLocalConfigInput { +struct GlobalWorkspaceLocalConfigInput { global: GlobalConfigInput, local: LocalConfigInput, + workspace: WorkspaceConfigInput, } -impl GlobalLocalConfigInput { +impl GlobalWorkspaceLocalConfigInput { const FIELDS: &'static [&'static [&'static str]] = &[GlobalConfigInput::FIELDS, LocalConfigInput::FIELDS]; fn from_toml( toml: toml::Table, error_sink: &mut Vec<(String, toml::de::Error)>, - ) -> GlobalLocalConfigInput { - GlobalLocalConfigInput { + ) -> GlobalWorkspaceLocalConfigInput { + GlobalWorkspaceLocalConfigInput { global: GlobalConfigInput::from_toml(&toml, error_sink), local: LocalConfigInput::from_toml(&toml, error_sink), + workspace: WorkspaceConfigInput::from_toml(&toml, error_sink), } } } -/// All of the config levels, all fields `Option`, to describe fields that are actually set by +/// Workspace and local config levels, all fields `Option`, to describe fields that are actually set by /// some rust-analyzer.toml file or JSON blob. An empty rust-analyzer.toml corresponds to /// all fields being None. #[derive(Debug, Clone, Default)] diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 1475d03ca6..d78bd3b44d 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -2113,8 +2113,9 @@ fn run_rustfmt( let edition = editions.iter().copied().max(); let line_index = snap.file_line_index(file_id)?; + let source_root_id = snap.analysis.source_root_id(file_id).ok(); - let mut command = match snap.config.rustfmt() { + let mut command = match snap.config.rustfmt(source_root_id) { RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => { // FIXME: Set RUSTUP_TOOLCHAIN let mut cmd = process::Command::new(toolchain::Tool::Rustfmt.path()); @@ -2302,9 +2303,8 @@ pub(crate) fn internal_testing_fetch_config( .transpose()?; serde_json::to_value(match &*params.config { "local" => state.config.assist(source_root).assist_emit_must_use, - // TODO Most probably will fail because it was not renamed to workspace - "global" => matches!( - state.config.rustfmt(), + "workspace" => matches!( + state.config.rustfmt(source_root), RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } ), _ => return Err(anyhow::anyhow!("Unknown test config key: {}", params.config)), diff --git a/crates/rust-analyzer/src/lsp/capabilities.rs b/crates/rust-analyzer/src/lsp/capabilities.rs index 212294b5d3..9610808c27 100644 --- a/crates/rust-analyzer/src/lsp/capabilities.rs +++ b/crates/rust-analyzer/src/lsp/capabilities.rs @@ -67,7 +67,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities { code_action_provider: Some(config.caps().code_action_capabilities()), code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), document_formatting_provider: Some(OneOf::Left(true)), - document_range_formatting_provider: match config.rustfmt() { + document_range_formatting_provider: match config.rustfmt(None) { RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)), _ => Some(OneOf::Left(false)), }, diff --git a/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/crates/rust-analyzer/tests/slow-tests/ratoml.rs index c06ba9eee1..8cb6409a84 100644 --- a/crates/rust-analyzer/tests/slow-tests/ratoml.rs +++ b/crates/rust-analyzer/tests/slow-tests/ratoml.rs @@ -17,7 +17,7 @@ enum QueryType { /// A query whose config key is a part of the global configs, so that /// testing for changes to this config means testing if global changes /// take affect. - Global, + Workspace, } struct RatomlTest { @@ -165,7 +165,7 @@ impl RatomlTest { fn query(&self, query: QueryType, source_file_idx: usize) -> bool { let config = match query { QueryType::Local => "local".to_owned(), - QueryType::Global => "global".to_owned(), + QueryType::Workspace => "workspace".to_owned(), }; let res = self.server.send_request::( InternalTestingFetchConfigParams { @@ -823,10 +823,8 @@ fn ratoml_multiple_ratoml_in_single_source_root() { // assert!(!server.query(QueryType::AssistEmitMustUse, 5)); // } -/// Having a ratoml file at the root of a project enables -/// configuring global level configurations as well. #[test] -fn ratoml_in_root_is_global() { +fn ratoml_in_root_is_workspace() { if skip_slow_tests() { return; } @@ -854,7 +852,7 @@ fn main() { None, ); - assert!(server.query(QueryType::Global, 2)); + assert!(server.query(QueryType::Workspace, 2)); } #[test] @@ -886,9 +884,9 @@ fn main() { None, ); - assert!(server.query(QueryType::Global, 2)); + assert!(server.query(QueryType::Workspace, 2)); server.edit(1, "rustfmt.rangeFormatting.enable = false".to_owned()); - assert!(!server.query(QueryType::Global, 2)); + assert!(!server.query(QueryType::Workspace, 2)); } #[test] @@ -920,7 +918,7 @@ fn main() { None, ); - assert!(server.query(QueryType::Global, 2)); + assert!(server.query(QueryType::Workspace, 2)); server.delete(1); - assert!(!server.query(QueryType::Global, 2)); + assert!(!server.query(QueryType::Workspace, 2)); } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index e4a8c6493a..1ea679d2a5 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -878,27 +878,6 @@ crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. This option does not take effect until rust-analyzer is restarted. -- -[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: -+ --- -Additional arguments to `rustfmt`. --- -[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: -+ --- -Advanced option, fully override the command rust-analyzer uses for -formatting. This should be the equivalent of `rustfmt` here, and -not that of `cargo fmt`. The file contents will be passed on the -standard input and the formatted result will be read from the -standard output. --- -[[rust-analyzer.rustfmt.rangeFormatting.enable]]rust-analyzer.rustfmt.rangeFormatting.enable (default: `false`):: -+ --- -Enables the use of rustfmt's unstable range formatting command for the -`textDocument/rangeFormatting` request. The rustfmt option is unstable and only -available on a nightly build. --- [[rust-analyzer.semanticHighlighting.doc.comment.inject.enable]]rust-analyzer.semanticHighlighting.doc.comment.inject.enable (default: `true`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index 98e8bbf02a..15e08222ad 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -2358,45 +2358,6 @@ } } }, - { - "title": "rustfmt", - "properties": { - "rust-analyzer.rustfmt.extraArgs": { - "markdownDescription": "Additional arguments to `rustfmt`.", - "default": [], - "type": "array", - "items": { - "type": "string" - } - } - } - }, - { - "title": "rustfmt", - "properties": { - "rust-analyzer.rustfmt.overrideCommand": { - "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting. This should be the equivalent of `rustfmt` here, and\nnot that of `cargo fmt`. The file contents will be passed on the\nstandard input and the formatted result will be read from the\nstandard output.", - "default": null, - "type": [ - "null", - "array" - ], - "items": { - "type": "string" - } - } - } - }, - { - "title": "rustfmt", - "properties": { - "rust-analyzer.rustfmt.rangeFormatting.enable": { - "markdownDescription": "Enables the use of rustfmt's unstable range formatting command for the\n`textDocument/rangeFormatting` request. The rustfmt option is unstable and only\navailable on a nightly build.", - "default": false, - "type": "boolean" - } - } - }, { "title": "semanticHighlighting", "properties": { From 2559ddf63167aa02b1bf7fcb7492de38c323601b Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sat, 17 Aug 2024 02:32:38 +0200 Subject: [PATCH 3/3] Old configs are back --- crates/rust-analyzer/src/config.rs | 1 + docs/user/generated_config.adoc | 21 ++++++++++++++++ editors/code/package.json | 39 ++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 437f7c33ce..fd2979be27 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -2759,6 +2759,7 @@ impl FullConfigInput { GlobalConfigInput::schema_fields(&mut fields); LocalConfigInput::schema_fields(&mut fields); ClientConfigInput::schema_fields(&mut fields); + WorkspaceConfigInput::schema_fields(&mut fields); fields.sort_by_key(|&(x, ..)| x); fields .iter() diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 1ea679d2a5..e4a8c6493a 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -878,6 +878,27 @@ crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. This option does not take effect until rust-analyzer is restarted. -- +[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: ++ +-- +Additional arguments to `rustfmt`. +-- +[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: ++ +-- +Advanced option, fully override the command rust-analyzer uses for +formatting. This should be the equivalent of `rustfmt` here, and +not that of `cargo fmt`. The file contents will be passed on the +standard input and the formatted result will be read from the +standard output. +-- +[[rust-analyzer.rustfmt.rangeFormatting.enable]]rust-analyzer.rustfmt.rangeFormatting.enable (default: `false`):: ++ +-- +Enables the use of rustfmt's unstable range formatting command for the +`textDocument/rangeFormatting` request. The rustfmt option is unstable and only +available on a nightly build. +-- [[rust-analyzer.semanticHighlighting.doc.comment.inject.enable]]rust-analyzer.semanticHighlighting.doc.comment.inject.enable (default: `true`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index 15e08222ad..98e8bbf02a 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -2358,6 +2358,45 @@ } } }, + { + "title": "rustfmt", + "properties": { + "rust-analyzer.rustfmt.extraArgs": { + "markdownDescription": "Additional arguments to `rustfmt`.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + { + "title": "rustfmt", + "properties": { + "rust-analyzer.rustfmt.overrideCommand": { + "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting. This should be the equivalent of `rustfmt` here, and\nnot that of `cargo fmt`. The file contents will be passed on the\nstandard input and the formatted result will be read from the\nstandard output.", + "default": null, + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + } + } + } + }, + { + "title": "rustfmt", + "properties": { + "rust-analyzer.rustfmt.rangeFormatting.enable": { + "markdownDescription": "Enables the use of rustfmt's unstable range formatting command for the\n`textDocument/rangeFormatting` request. The rustfmt option is unstable and only\navailable on a nightly build.", + "default": false, + "type": "boolean" + } + } + }, { "title": "semanticHighlighting", "properties": {