aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-01-06 10:54:28 +0000
committerAleksey Kladov <[email protected]>2021-01-06 12:39:28 +0000
commitf7a15b5cd1df58e46066bbd27c90cb1ad7f9c316 (patch)
treedf2caa99c4558b9f2550420896ec9998566e1d5d
parentc3104466596e85d7fa43b8e3ac015bcabd08fcce (diff)
More maintainable config
Rather than eagerly converting JSON, we losslessly keep it as is, and change the shape of user-submitted data at the last moment. This also allows us to remove a bunch of wrong Defaults
-rw-r--r--crates/ide/src/hover.rs29
-rw-r--r--crates/ide/src/inlay_hints.rs21
-rw-r--r--crates/ide/src/runnables.rs35
-rw-r--r--crates/project_model/src/project_json.rs10
-rw-r--r--crates/rust-analyzer/src/bin/main.rs13
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs5
-rw-r--r--crates/rust-analyzer/src/config.rs535
-rw-r--r--crates/rust-analyzer/src/global_state.rs2
-rw-r--r--crates/rust-analyzer/src/handlers.rs95
-rw-r--r--crates/rust-analyzer/src/main_loop.rs11
-rw-r--r--crates/rust-analyzer/src/reload.rs26
-rw-r--r--crates/rust-analyzer/src/to_proto.rs6
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/main.rs22
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs44
-rw-r--r--docs/user/generated_config.adoc2
-rw-r--r--editors/code/package.json8
16 files changed, 422 insertions, 442 deletions
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index f2ad95cb6..72c9c66fe 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -17,7 +17,7 @@ use crate::{
17 doc_links::{remove_links, rewrite_links}, 17 doc_links::{remove_links, rewrite_links},
18 markdown_remove::remove_markdown, 18 markdown_remove::remove_markdown,
19 markup::Markup, 19 markup::Markup,
20 runnables::runnable, 20 runnables::{runnable, runnable_fn},
21 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, 21 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
22}; 22};
23 23
@@ -31,19 +31,6 @@ pub struct HoverConfig {
31 pub markdown: bool, 31 pub markdown: bool,
32} 32}
33 33
34impl Default for HoverConfig {
35 fn default() -> Self {
36 Self {
37 implementations: true,
38 run: true,
39 debug: true,
40 goto_type_def: true,
41 links_in_hover: true,
42 markdown: true,
43 }
44 }
45}
46
47impl HoverConfig { 34impl HoverConfig {
48 pub const NO_ACTIONS: Self = Self { 35 pub const NO_ACTIONS: Self = Self {
49 implementations: false, 36 implementations: false,
@@ -204,22 +191,20 @@ fn runnable_action(
204 match def { 191 match def {
205 Definition::ModuleDef(it) => match it { 192 Definition::ModuleDef(it) => match it {
206 ModuleDef::Module(it) => match it.definition_source(sema.db).value { 193 ModuleDef::Module(it) => match it.definition_source(sema.db).value {
207 ModuleSource::Module(it) => runnable(&sema, it.syntax().clone(), file_id) 194 ModuleSource::Module(it) => {
208 .map(|it| HoverAction::Runnable(it)), 195 runnable(&sema, it.syntax().clone()).map(|it| HoverAction::Runnable(it))
196 }
209 _ => None, 197 _ => None,
210 }, 198 },
211 ModuleDef::Function(it) => { 199 ModuleDef::Function(func) => {
212 #[allow(deprecated)] 200 let src = func.source(sema.db)?;
213 let src = it.source(sema.db)?;
214 if src.file_id != file_id.into() { 201 if src.file_id != file_id.into() {
215 mark::hit!(hover_macro_generated_struct_fn_doc_comment); 202 mark::hit!(hover_macro_generated_struct_fn_doc_comment);
216 mark::hit!(hover_macro_generated_struct_fn_doc_attr); 203 mark::hit!(hover_macro_generated_struct_fn_doc_attr);
217
218 return None; 204 return None;
219 } 205 }
220 206
221 runnable(&sema, src.value.syntax().clone(), file_id) 207 runnable_fn(&sema, func).map(HoverAction::Runnable)
222 .map(|it| HoverAction::Runnable(it))
223 } 208 }
224 _ => None, 209 _ => None,
225 }, 210 },
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 65df7979c..fe60abfc8 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -18,12 +18,6 @@ pub struct InlayHintsConfig {
18 pub max_length: Option<usize>, 18 pub max_length: Option<usize>,
19} 19}
20 20
21impl Default for InlayHintsConfig {
22 fn default() -> Self {
23 Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None }
24 }
25}
26
27#[derive(Clone, Debug, PartialEq, Eq)] 21#[derive(Clone, Debug, PartialEq, Eq)]
28pub enum InlayKind { 22pub enum InlayKind {
29 TypeHint, 23 TypeHint,
@@ -433,8 +427,15 @@ mod tests {
433 427
434 use crate::{fixture, inlay_hints::InlayHintsConfig}; 428 use crate::{fixture, inlay_hints::InlayHintsConfig};
435 429
430 const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
431 type_hints: true,
432 parameter_hints: true,
433 chaining_hints: true,
434 max_length: None,
435 };
436
436 fn check(ra_fixture: &str) { 437 fn check(ra_fixture: &str) {
437 check_with_config(InlayHintsConfig::default(), ra_fixture); 438 check_with_config(TEST_CONFIG, ra_fixture);
438 } 439 }
439 440
440 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { 441 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
@@ -748,7 +749,7 @@ fn main() {
748 #[test] 749 #[test]
749 fn hint_truncation() { 750 fn hint_truncation() {
750 check_with_config( 751 check_with_config(
751 InlayHintsConfig { max_length: Some(8), ..Default::default() }, 752 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
752 r#" 753 r#"
753struct Smol<T>(T); 754struct Smol<T>(T);
754 755
@@ -831,7 +832,7 @@ fn main() {
831 #[test] 832 #[test]
832 fn omitted_parameters_hints_heuristics() { 833 fn omitted_parameters_hints_heuristics() {
833 check_with_config( 834 check_with_config(
834 InlayHintsConfig { max_length: Some(8), ..Default::default() }, 835 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
835 r#" 836 r#"
836fn map(f: i32) {} 837fn map(f: i32) {}
837fn filter(predicate: i32) {} 838fn filter(predicate: i32) {}
@@ -924,7 +925,7 @@ fn main() {
924 #[test] 925 #[test]
925 fn unit_structs_have_no_type_hints() { 926 fn unit_structs_have_no_type_hints() {
926 check_with_config( 927 check_with_config(
927 InlayHintsConfig { max_length: Some(8), ..Default::default() }, 928 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
928 r#" 929 r#"
929enum Result<T, E> { Ok(T), Err(E) } 930enum Result<T, E> { Ok(T), Err(E) }
930use Result::*; 931use Result::*;
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index c893afc7c..f4030f3ef 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -2,11 +2,11 @@ use std::fmt;
2 2
3use assists::utils::test_related_attribute; 3use assists::utils::test_related_attribute;
4use cfg::CfgExpr; 4use cfg::CfgExpr;
5use hir::{AsAssocItem, HasAttrs, InFile, Semantics}; 5use hir::{AsAssocItem, HasAttrs, HasSource, Semantics};
6use ide_db::RootDatabase; 6use ide_db::RootDatabase;
7use itertools::Itertools; 7use itertools::Itertools;
8use syntax::{ 8use syntax::{
9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, 9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner},
10 match_ast, SyntaxNode, 10 match_ast, SyntaxNode,
11}; 11};
12 12
@@ -96,17 +96,16 @@ impl Runnable {
96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
97 let sema = Semantics::new(db); 97 let sema = Semantics::new(db);
98 let source_file = sema.parse(file_id); 98 let source_file = sema.parse(file_id);
99 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect() 99 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect()
100} 100}
101 101
102pub(crate) fn runnable( 102pub(crate) fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> {
103 sema: &Semantics<RootDatabase>,
104 item: SyntaxNode,
105 file_id: FileId,
106) -> Option<Runnable> {
107 let runnable_item = match_ast! { 103 let runnable_item = match_ast! {
108 match (item.clone()) { 104 match (item.clone()) {
109 ast::Fn(it) => runnable_fn(sema, it, file_id), 105 ast::Fn(func) => {
106 let def = sema.to_def(&func)?;
107 runnable_fn(sema, def)
108 },
110 ast::Module(it) => runnable_mod(sema, it), 109 ast::Module(it) => runnable_mod(sema, it),
111 _ => None, 110 _ => None,
112 } 111 }
@@ -114,23 +113,23 @@ pub(crate) fn runnable(
114 runnable_item.or_else(|| runnable_doctest(sema, item)) 113 runnable_item.or_else(|| runnable_doctest(sema, item))
115} 114}
116 115
117fn runnable_fn(sema: &Semantics<RootDatabase>, func: ast::Fn, file_id: FileId) -> Option<Runnable> { 116pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
118 let def = sema.to_def(&func)?; 117 let func = def.source(sema.db)?;
119 let name_string = func.name()?.text().to_string(); 118 let name_string = def.name(sema.db).to_string();
120 119
121 let kind = if name_string == "main" { 120 let kind = if name_string == "main" {
122 RunnableKind::Bin 121 RunnableKind::Bin
123 } else { 122 } else {
124 let canonical_path = sema.to_def(&func).and_then(|def| { 123 let canonical_path = {
125 let def: hir::ModuleDef = def.into(); 124 let def: hir::ModuleDef = def.into();
126 def.canonical_path(sema.db) 125 def.canonical_path(sema.db)
127 }); 126 };
128 let test_id = canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name_string)); 127 let test_id = canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name_string));
129 128
130 if test_related_attribute(&func).is_some() { 129 if test_related_attribute(&func.value).is_some() {
131 let attr = TestAttr::from_fn(&func); 130 let attr = TestAttr::from_fn(&func.value);
132 RunnableKind::Test { test_id, attr } 131 RunnableKind::Test { test_id, attr }
133 } else if func.has_atom_attr("bench") { 132 } else if func.value.has_atom_attr("bench") {
134 RunnableKind::Bench { test_id } 133 RunnableKind::Bench { test_id }
135 } else { 134 } else {
136 return None; 135 return None;
@@ -139,7 +138,7 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, func: ast::Fn, file_id: FileId) -
139 138
140 let nav = NavigationTarget::from_named( 139 let nav = NavigationTarget::from_named(
141 sema.db, 140 sema.db,
142 InFile::new(file_id.into(), &func), 141 func.as_ref().map(|it| it as &dyn ast::NameOwner),
143 SymbolKind::Function, 142 SymbolKind::Function,
144 ); 143 );
145 let cfg = def.attrs(sema.db).cfg(); 144 let cfg = def.attrs(sema.db).cfg();
diff --git a/crates/project_model/src/project_json.rs b/crates/project_model/src/project_json.rs
index af884eb84..41a2ac03e 100644
--- a/crates/project_model/src/project_json.rs
+++ b/crates/project_model/src/project_json.rs
@@ -110,13 +110,13 @@ impl ProjectJson {
110 } 110 }
111} 111}
112 112
113#[derive(Deserialize)] 113#[derive(Deserialize, Debug, Clone)]
114pub struct ProjectJsonData { 114pub struct ProjectJsonData {
115 sysroot_src: Option<PathBuf>, 115 sysroot_src: Option<PathBuf>,
116 crates: Vec<CrateData>, 116 crates: Vec<CrateData>,
117} 117}
118 118
119#[derive(Deserialize)] 119#[derive(Deserialize, Debug, Clone)]
120struct CrateData { 120struct CrateData {
121 display_name: Option<String>, 121 display_name: Option<String>,
122 root_module: PathBuf, 122 root_module: PathBuf,
@@ -132,7 +132,7 @@ struct CrateData {
132 source: Option<CrateSource>, 132 source: Option<CrateSource>,
133} 133}
134 134
135#[derive(Deserialize)] 135#[derive(Deserialize, Debug, Clone)]
136#[serde(rename = "edition")] 136#[serde(rename = "edition")]
137enum EditionData { 137enum EditionData {
138 #[serde(rename = "2015")] 138 #[serde(rename = "2015")]
@@ -153,7 +153,7 @@ impl From<EditionData> for Edition {
153 } 153 }
154} 154}
155 155
156#[derive(Deserialize)] 156#[derive(Deserialize, Debug, Clone)]
157struct DepData { 157struct DepData {
158 /// Identifies a crate by position in the crates array. 158 /// Identifies a crate by position in the crates array.
159 #[serde(rename = "crate")] 159 #[serde(rename = "crate")]
@@ -162,7 +162,7 @@ struct DepData {
162 name: CrateName, 162 name: CrateName,
163} 163}
164 164
165#[derive(Deserialize)] 165#[derive(Deserialize, Debug, Clone)]
166struct CrateSource { 166struct CrateSource {
167 include_dirs: Vec<PathBuf>, 167 include_dirs: Vec<PathBuf>,
168 exclude_dirs: Vec<PathBuf>, 168 exclude_dirs: Vec<PathBuf>,
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index defdcbd74..3af3c59d8 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -8,11 +8,7 @@ use std::{convert::TryFrom, env, fs, path::PathBuf, process};
8 8
9use lsp_server::Connection; 9use lsp_server::Connection;
10use project_model::ProjectManifest; 10use project_model::ProjectManifest;
11use rust_analyzer::{ 11use rust_analyzer::{cli, config::Config, from_json, Result};
12 cli,
13 config::{Config, LinkedProject},
14 from_json, Result,
15};
16use vfs::AbsPathBuf; 12use vfs::AbsPathBuf;
17 13
18#[cfg(all(feature = "mimalloc"))] 14#[cfg(all(feature = "mimalloc"))]
@@ -138,13 +134,12 @@ fn run_server() -> Result<()> {
138 } 134 }
139 }; 135 };
140 136
141 let mut config = Config::new(root_path); 137 let mut config = Config::new(root_path, initialize_params.capabilities);
142 if let Some(json) = initialize_params.initialization_options { 138 if let Some(json) = initialize_params.initialization_options {
143 config.update(json); 139 config.update(json);
144 } 140 }
145 config.update_caps(&initialize_params.capabilities);
146 141
147 if config.linked_projects.is_empty() { 142 if config.linked_projects().is_empty() {
148 let workspace_roots = initialize_params 143 let workspace_roots = initialize_params
149 .workspace_folders 144 .workspace_folders
150 .map(|workspaces| { 145 .map(|workspaces| {
@@ -163,7 +158,7 @@ fn run_server() -> Result<()> {
163 log::error!("failed to find any projects in {:?}", workspace_roots); 158 log::error!("failed to find any projects in {:?}", workspace_roots);
164 } 159 }
165 160
166 config.linked_projects = discovered.into_iter().map(LinkedProject::from).collect(); 161 config.discovered_projects = Some(discovered);
167 } 162 }
168 163
169 config 164 config
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 8a8b4a32c..5af0802a2 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -84,14 +84,15 @@ impl CargoTargetSpec {
84 } 84 }
85 } 85 }
86 86
87 if snap.config.cargo.all_features { 87 let cargo_config = snap.config.cargo();
88 if cargo_config.all_features {
88 args.push("--all-features".to_string()); 89 args.push("--all-features".to_string());
89 } else { 90 } else {
90 let mut features = Vec::new(); 91 let mut features = Vec::new();
91 if let Some(cfg) = cfg.as_ref() { 92 if let Some(cfg) = cfg.as_ref() {
92 required_features(cfg, &mut features); 93 required_features(cfg, &mut features);
93 } 94 }
94 for feature in &snap.config.cargo.features { 95 for feature in cargo_config.features {
95 features.push(feature.clone()); 96 features.push(feature.clone());
96 } 97 }
97 features.dedup(); 98 features.dedup();
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index a80652e83..24e7936fc 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -148,13 +148,19 @@ config_data! {
148 /// of projects.\n\nElements must be paths pointing to `Cargo.toml`, 148 /// of projects.\n\nElements must be paths pointing to `Cargo.toml`,
149 /// `rust-project.json`, or JSON objects in `rust-project.json` format. 149 /// `rust-project.json`, or JSON objects in `rust-project.json` format.
150 linkedProjects: Vec<ManifestOrProjectJson> = "[]", 150 linkedProjects: Vec<ManifestOrProjectJson> = "[]",
151
151 /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. 152 /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
152 lruCapacity: Option<usize> = "null", 153 lruCapacity: Option<usize> = "null",
154
153 /// Whether to show `can't find Cargo.toml` error message. 155 /// Whether to show `can't find Cargo.toml` error message.
154 notifications_cargoTomlNotFound: bool = "true", 156 notifications_cargoTomlNotFound: bool = "true",
157
155 /// Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be 158 /// Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be
156 /// enabled. 159 /// enabled.
157 procMacro_enable: bool = "false", 160 procMacro_enable: bool = "false",
161 /// Internal config, path to proc-macro server executable (typically,
162 /// this is rust-analyzer itself, but we override this in tests).
163 procMacro_server: Option<PathBuf> = "null",
158 164
159 /// Command to be executed instead of 'cargo' for runnables. 165 /// Command to be executed instead of 'cargo' for runnables.
160 runnables_overrideCargo: Option<String> = "null", 166 runnables_overrideCargo: Option<String> = "null",
@@ -163,7 +169,7 @@ config_data! {
163 runnables_cargoExtraArgs: Vec<String> = "[]", 169 runnables_cargoExtraArgs: Vec<String> = "[]",
164 170
165 /// Path to the rust compiler sources, for usage in rustc_private projects. 171 /// Path to the rust compiler sources, for usage in rustc_private projects.
166 rustcSource : Option<String> = "null", 172 rustcSource : Option<PathBuf> = "null",
167 173
168 /// Additional arguments to `rustfmt`. 174 /// Additional arguments to `rustfmt`.
169 rustfmt_extraArgs: Vec<String> = "[]", 175 rustfmt_extraArgs: Vec<String> = "[]",
@@ -173,34 +179,17 @@ config_data! {
173 } 179 }
174} 180}
175 181
182impl Default for ConfigData {
183 fn default() -> Self {
184 ConfigData::from_json(serde_json::Value::Null)
185 }
186}
187
176#[derive(Debug, Clone)] 188#[derive(Debug, Clone)]
177pub struct Config { 189pub struct Config {
178 pub caps: lsp_types::ClientCapabilities, 190 caps: lsp_types::ClientCapabilities,
179 191 data: ConfigData,
180 pub publish_diagnostics: bool, 192 pub discovered_projects: Option<Vec<ProjectManifest>>,
181 pub diagnostics: DiagnosticsConfig,
182 pub diagnostics_map: DiagnosticsMapConfig,
183 pub lru_capacity: Option<usize>,
184 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
185 pub files: FilesConfig,
186 pub notifications: NotificationsConfig,
187
188 pub cargo_autoreload: bool,
189 pub cargo: CargoConfig,
190 pub rustfmt: RustfmtConfig,
191 pub flycheck: Option<FlycheckConfig>,
192 pub runnables: RunnablesConfig,
193
194 pub inlay_hints: InlayHintsConfig,
195 pub completion: CompletionConfig,
196 pub assist: AssistConfig,
197 pub call_info_full: bool,
198 pub lens: LensConfig,
199 pub hover: HoverConfig,
200 pub semantic_tokens_refresh: bool,
201 pub code_lens_refresh: bool,
202
203 pub linked_projects: Vec<LinkedProject>,
204 pub root_path: AbsPathBuf, 193 pub root_path: AbsPathBuf,
205} 194}
206 195
@@ -230,12 +219,6 @@ pub struct LensConfig {
230 pub method_refs: bool, 219 pub method_refs: bool,
231} 220}
232 221
233impl Default for LensConfig {
234 fn default() -> Self {
235 Self { run: true, debug: true, implementations: true, method_refs: false }
236 }
237}
238
239impl LensConfig { 222impl LensConfig {
240 pub fn any(&self) -> bool { 223 pub fn any(&self) -> bool {
241 self.implementations || self.runnable() || self.references() 224 self.implementations || self.runnable() || self.references()
@@ -278,7 +261,7 @@ pub enum RustfmtConfig {
278} 261}
279 262
280/// Configuration for runnable items, such as `main` function or tests. 263/// Configuration for runnable items, such as `main` function or tests.
281#[derive(Debug, Clone, Default)] 264#[derive(Debug, Clone)]
282pub struct RunnablesConfig { 265pub struct RunnablesConfig {
283 /// Custom command to be executed instead of `cargo` for runnables. 266 /// Custom command to be executed instead of `cargo` for runnables.
284 pub override_cargo: Option<String>, 267 pub override_cargo: Option<String>,
@@ -287,250 +270,15 @@ pub struct RunnablesConfig {
287} 270}
288 271
289impl Config { 272impl Config {
290 pub fn new(root_path: AbsPathBuf) -> Self { 273 pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self {
291 // Defaults here don't matter, we'll immediately re-write them with 274 Config { caps, data: ConfigData::default(), discovered_projects: None, root_path }
292 // ConfigData.
293 let mut res = Config {
294 caps: lsp_types::ClientCapabilities::default(),
295
296 publish_diagnostics: false,
297 diagnostics: DiagnosticsConfig::default(),
298 diagnostics_map: DiagnosticsMapConfig::default(),
299 lru_capacity: None,
300 proc_macro_srv: None,
301 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
302 notifications: NotificationsConfig { cargo_toml_not_found: false },
303
304 cargo_autoreload: false,
305 cargo: CargoConfig::default(),
306 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
307 flycheck: Some(FlycheckConfig::CargoCommand {
308 command: String::new(),
309 target_triple: None,
310 no_default_features: false,
311 all_targets: false,
312 all_features: false,
313 extra_args: Vec::new(),
314 features: Vec::new(),
315 }),
316 runnables: RunnablesConfig::default(),
317
318 inlay_hints: InlayHintsConfig {
319 type_hints: false,
320 parameter_hints: false,
321 chaining_hints: false,
322 max_length: None,
323 },
324 completion: CompletionConfig::default(),
325 assist: AssistConfig::default(),
326 call_info_full: false,
327 lens: LensConfig::default(),
328 hover: HoverConfig::default(),
329 semantic_tokens_refresh: false,
330 code_lens_refresh: false,
331 linked_projects: Vec::new(),
332 root_path,
333 };
334 res.do_update(serde_json::json!({}));
335 res
336 } 275 }
337 pub fn update(&mut self, json: serde_json::Value) { 276 pub fn update(&mut self, json: serde_json::Value) {
338 log::info!("updating config from JSON: {:#}", json); 277 log::info!("updating config from JSON: {:#}", json);
339 if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) { 278 if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
340 return; 279 return;
341 } 280 }
342 self.do_update(json); 281 self.data = ConfigData::from_json(json);
343 log::info!("updated config: {:#?}", self);
344 }
345 fn do_update(&mut self, json: serde_json::Value) {
346 let data = ConfigData::from_json(json);
347
348 self.publish_diagnostics = data.diagnostics_enable;
349 self.diagnostics = DiagnosticsConfig {
350 disable_experimental: !data.diagnostics_enableExperimental,
351 disabled: data.diagnostics_disabled,
352 };
353 self.diagnostics_map = DiagnosticsMapConfig {
354 warnings_as_info: data.diagnostics_warningsAsInfo,
355 warnings_as_hint: data.diagnostics_warningsAsHint,
356 };
357 self.lru_capacity = data.lruCapacity;
358 self.files.watcher = match data.files_watcher.as_str() {
359 "notify" => FilesWatcher::Notify,
360 "client" | _ => FilesWatcher::Client,
361 };
362 self.notifications =
363 NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound };
364 self.cargo_autoreload = data.cargo_autoreload;
365
366 let rustc_source = if let Some(rustc_source) = data.rustcSource {
367 let rustpath: PathBuf = rustc_source.into();
368 AbsPathBuf::try_from(rustpath)
369 .map_err(|_| {
370 log::error!("rustc source directory must be an absolute path");
371 })
372 .ok()
373 } else {
374 None
375 };
376
377 self.cargo = CargoConfig {
378 no_default_features: data.cargo_noDefaultFeatures,
379 all_features: data.cargo_allFeatures,
380 features: data.cargo_features.clone(),
381 load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck,
382 target: data.cargo_target.clone(),
383 rustc_source: rustc_source,
384 no_sysroot: data.cargo_noSysroot,
385 };
386 self.runnables = RunnablesConfig {
387 override_cargo: data.runnables_overrideCargo,
388 cargo_extra_args: data.runnables_cargoExtraArgs,
389 };
390
391 self.proc_macro_srv = if data.procMacro_enable {
392 std::env::current_exe().ok().map(|path| (path, vec!["proc-macro".into()]))
393 } else {
394 None
395 };
396
397 self.rustfmt = match data.rustfmt_overrideCommand {
398 Some(mut args) if !args.is_empty() => {
399 let command = args.remove(0);
400 RustfmtConfig::CustomCommand { command, args }
401 }
402 Some(_) | None => RustfmtConfig::Rustfmt { extra_args: data.rustfmt_extraArgs },
403 };
404
405 self.flycheck = if data.checkOnSave_enable {
406 let flycheck_config = match data.checkOnSave_overrideCommand {
407 Some(mut args) if !args.is_empty() => {
408 let command = args.remove(0);
409 FlycheckConfig::CustomCommand { command, args }
410 }
411 Some(_) | None => FlycheckConfig::CargoCommand {
412 command: data.checkOnSave_command,
413 target_triple: data.checkOnSave_target.or(data.cargo_target),
414 all_targets: data.checkOnSave_allTargets,
415 no_default_features: data
416 .checkOnSave_noDefaultFeatures
417 .unwrap_or(data.cargo_noDefaultFeatures),
418 all_features: data.checkOnSave_allFeatures.unwrap_or(data.cargo_allFeatures),
419 features: data.checkOnSave_features.unwrap_or(data.cargo_features),
420 extra_args: data.checkOnSave_extraArgs,
421 },
422 };
423 Some(flycheck_config)
424 } else {
425 None
426 };
427
428 self.inlay_hints = InlayHintsConfig {
429 type_hints: data.inlayHints_typeHints,
430 parameter_hints: data.inlayHints_parameterHints,
431 chaining_hints: data.inlayHints_chainingHints,
432 max_length: data.inlayHints_maxLength,
433 };
434
435 self.assist.insert_use.merge = match data.assist_importMergeBehaviour {
436 MergeBehaviorDef::None => None,
437 MergeBehaviorDef::Full => Some(MergeBehavior::Full),
438 MergeBehaviorDef::Last => Some(MergeBehavior::Last),
439 };
440 self.assist.insert_use.prefix_kind = match data.assist_importPrefix {
441 ImportPrefixDef::Plain => PrefixKind::Plain,
442 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
443 ImportPrefixDef::BySelf => PrefixKind::BySelf,
444 };
445
446 self.completion.enable_postfix_completions = data.completion_postfix_enable;
447 self.completion.enable_autoimport_completions = data.completion_autoimport_enable;
448 self.completion.add_call_parenthesis = data.completion_addCallParenthesis;
449 self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets;
450 self.completion.merge = self.assist.insert_use.merge;
451
452 self.call_info_full = data.callInfo_full;
453
454 self.lens = LensConfig {
455 run: data.lens_enable && data.lens_run,
456 debug: data.lens_enable && data.lens_debug,
457 implementations: data.lens_enable && data.lens_implementations,
458 method_refs: data.lens_enable && data.lens_methodReferences,
459 };
460
461 if !data.linkedProjects.is_empty() {
462 self.linked_projects.clear();
463 for linked_project in data.linkedProjects {
464 let linked_project = match linked_project {
465 ManifestOrProjectJson::Manifest(it) => {
466 let path = self.root_path.join(it);
467 match ProjectManifest::from_manifest_file(path) {
468 Ok(it) => it.into(),
469 Err(e) => {
470 log::error!("failed to load linked project: {}", e);
471 continue;
472 }
473 }
474 }
475 ManifestOrProjectJson::ProjectJson(it) => {
476 ProjectJson::new(&self.root_path, it).into()
477 }
478 };
479 self.linked_projects.push(linked_project);
480 }
481 }
482
483 self.hover = HoverConfig {
484 implementations: data.hoverActions_enable && data.hoverActions_implementations,
485 run: data.hoverActions_enable && data.hoverActions_run,
486 debug: data.hoverActions_enable && data.hoverActions_debug,
487 goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef,
488 links_in_hover: data.hoverActions_linksInHover,
489 markdown: true,
490 };
491 }
492
493 pub fn update_caps(&mut self, caps: &ClientCapabilities) {
494 self.caps = caps.clone();
495 if let Some(doc_caps) = caps.text_document.as_ref() {
496 if let Some(value) = doc_caps.hover.as_ref().and_then(|it| it.content_format.as_ref()) {
497 self.hover.markdown = value.contains(&MarkupKind::Markdown)
498 }
499
500 self.completion.allow_snippets(false);
501 self.completion.active_resolve_capabilities =
502 enabled_completions_resolve_capabilities(caps).unwrap_or_default();
503 if let Some(completion) = &doc_caps.completion {
504 if let Some(completion_item) = &completion.completion_item {
505 if let Some(value) = completion_item.snippet_support {
506 self.completion.allow_snippets(value);
507 }
508 }
509 }
510 }
511
512 self.assist.allow_snippets(false);
513 if let Some(experimental) = &caps.experimental {
514 let get_bool =
515 |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true);
516
517 let snippet_text_edit = get_bool("snippetTextEdit");
518 self.assist.allow_snippets(snippet_text_edit);
519 }
520
521 if let Some(workspace_caps) = caps.workspace.as_ref() {
522 if let Some(refresh_support) =
523 workspace_caps.semantic_tokens.as_ref().and_then(|it| it.refresh_support)
524 {
525 self.semantic_tokens_refresh = refresh_support;
526 }
527
528 if let Some(refresh_support) =
529 workspace_caps.code_lens.as_ref().and_then(|it| it.refresh_support)
530 {
531 self.code_lens_refresh = refresh_support;
532 }
533 }
534 } 282 }
535 283
536 pub fn json_schema() -> serde_json::Value { 284 pub fn json_schema() -> serde_json::Value {
@@ -550,6 +298,38 @@ macro_rules! try_or {
550} 298}
551 299
552impl Config { 300impl Config {
301 pub fn linked_projects(&self) -> Vec<LinkedProject> {
302 if self.data.linkedProjects.is_empty() {
303 self.discovered_projects
304 .as_ref()
305 .into_iter()
306 .flatten()
307 .cloned()
308 .map(LinkedProject::from)
309 .collect()
310 } else {
311 self.data
312 .linkedProjects
313 .iter()
314 .filter_map(|linked_project| {
315 let res = match linked_project {
316 ManifestOrProjectJson::Manifest(it) => {
317 let path = self.root_path.join(it);
318 ProjectManifest::from_manifest_file(path)
319 .map_err(|e| log::error!("failed to load linked project: {}", e))
320 .ok()?
321 .into()
322 }
323 ManifestOrProjectJson::ProjectJson(it) => {
324 ProjectJson::new(&self.root_path, it.clone()).into()
325 }
326 };
327 Some(res)
328 })
329 .collect()
330 }
331 }
332
553 pub fn location_link(&self) -> bool { 333 pub fn location_link(&self) -> bool {
554 try_or!(self.caps.text_document.as_ref()?.definition?.link_support?, false) 334 try_or!(self.caps.text_document.as_ref()?.definition?.link_support?, false)
555 } 335 }
@@ -625,16 +405,217 @@ impl Config {
625 pub fn status_notification(&self) -> bool { 405 pub fn status_notification(&self) -> bool {
626 self.experimental("statusNotification") 406 self.experimental("statusNotification")
627 } 407 }
408
409 pub fn publish_diagnostics(&self) -> bool {
410 self.data.diagnostics_enable
411 }
412 pub fn diagnostics(&self) -> DiagnosticsConfig {
413 DiagnosticsConfig {
414 disable_experimental: !self.data.diagnostics_enableExperimental,
415 disabled: self.data.diagnostics_disabled.clone(),
416 }
417 }
418 pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
419 DiagnosticsMapConfig {
420 warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
421 warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
422 }
423 }
424 pub fn lru_capacity(&self) -> Option<usize> {
425 self.data.lruCapacity
426 }
427 pub fn proc_macro_srv(&self) -> Option<(PathBuf, Vec<OsString>)> {
428 if !self.data.procMacro_enable {
429 return None;
430 }
431
432 let path = self.data.procMacro_server.clone().or_else(|| std::env::current_exe().ok())?;
433 Some((path, vec!["proc-macro".into()]))
434 }
435 pub fn files(&self) -> FilesConfig {
436 FilesConfig {
437 watcher: match self.data.files_watcher.as_str() {
438 "notify" => FilesWatcher::Notify,
439 "client" | _ => FilesWatcher::Client,
440 },
441 exclude: Vec::new(),
442 }
443 }
444 pub fn notifications(&self) -> NotificationsConfig {
445 NotificationsConfig { cargo_toml_not_found: self.data.notifications_cargoTomlNotFound }
446 }
447 pub fn cargo_autoreload(&self) -> bool {
448 self.data.cargo_autoreload
449 }
450 pub fn cargo(&self) -> CargoConfig {
451 let rustc_source = self.data.rustcSource.clone().and_then(|it| {
452 AbsPathBuf::try_from(it)
453 .map_err(|_| log::error!("rustc source directory must be an absolute path"))
454 .ok()
455 });
456
457 CargoConfig {
458 no_default_features: self.data.cargo_noDefaultFeatures,
459 all_features: self.data.cargo_allFeatures,
460 features: self.data.cargo_features.clone(),
461 load_out_dirs_from_check: self.data.cargo_loadOutDirsFromCheck,
462 target: self.data.cargo_target.clone(),
463 rustc_source,
464 no_sysroot: self.data.cargo_noSysroot,
465 }
466 }
467 pub fn rustfmt(&self) -> RustfmtConfig {
468 match &self.data.rustfmt_overrideCommand {
469 Some(args) if !args.is_empty() => {
470 let mut args = args.clone();
471 let command = args.remove(0);
472 RustfmtConfig::CustomCommand { command, args }
473 }
474 Some(_) | None => {
475 RustfmtConfig::Rustfmt { extra_args: self.data.rustfmt_extraArgs.clone() }
476 }
477 }
478 }
479 pub fn flycheck(&self) -> Option<FlycheckConfig> {
480 if !self.data.checkOnSave_enable {
481 return None;
482 }
483 let flycheck_config = match &self.data.checkOnSave_overrideCommand {
484 Some(args) if !args.is_empty() => {
485 let mut args = args.clone();
486 let command = args.remove(0);
487 FlycheckConfig::CustomCommand { command, args }
488 }
489 Some(_) | None => FlycheckConfig::CargoCommand {
490 command: self.data.checkOnSave_command.clone(),
491 target_triple: self
492 .data
493 .checkOnSave_target
494 .clone()
495 .or(self.data.cargo_target.clone()),
496 all_targets: self.data.checkOnSave_allTargets,
497 no_default_features: self
498 .data
499 .checkOnSave_noDefaultFeatures
500 .unwrap_or(self.data.cargo_noDefaultFeatures),
501 all_features: self
502 .data
503 .checkOnSave_allFeatures
504 .unwrap_or(self.data.cargo_allFeatures),
505 features: self
506 .data
507 .checkOnSave_features
508 .clone()
509 .unwrap_or(self.data.cargo_features.clone()),
510 extra_args: self.data.checkOnSave_extraArgs.clone(),
511 },
512 };
513 Some(flycheck_config)
514 }
515 pub fn runnables(&self) -> RunnablesConfig {
516 RunnablesConfig {
517 override_cargo: self.data.runnables_overrideCargo.clone(),
518 cargo_extra_args: self.data.runnables_cargoExtraArgs.clone(),
519 }
520 }
521 pub fn inlay_hints(&self) -> InlayHintsConfig {
522 InlayHintsConfig {
523 type_hints: self.data.inlayHints_typeHints,
524 parameter_hints: self.data.inlayHints_parameterHints,
525 chaining_hints: self.data.inlayHints_chainingHints,
526 max_length: self.data.inlayHints_maxLength,
527 }
528 }
529 fn merge_behavior(&self) -> Option<MergeBehavior> {
530 match self.data.assist_importMergeBehaviour {
531 MergeBehaviorDef::None => None,
532 MergeBehaviorDef::Full => Some(MergeBehavior::Full),
533 MergeBehaviorDef::Last => Some(MergeBehavior::Last),
534 }
535 }
536 pub fn completion(&self) -> CompletionConfig {
537 let mut res = CompletionConfig::default();
538 res.enable_postfix_completions = self.data.completion_postfix_enable;
539 res.enable_autoimport_completions = self.data.completion_autoimport_enable;
540 res.add_call_parenthesis = self.data.completion_addCallParenthesis;
541 res.add_call_argument_snippets = self.data.completion_addCallArgumentSnippets;
542 res.merge = self.merge_behavior();
543 res.active_resolve_capabilities =
544 enabled_completions_resolve_capabilities(&self.caps).unwrap_or_default();
545
546 res.allow_snippets(try_or!(
547 self.caps
548 .text_document
549 .as_ref()?
550 .completion
551 .as_ref()?
552 .completion_item
553 .as_ref()?
554 .snippet_support?,
555 false
556 ));
557 res
558 }
559 pub fn assist(&self) -> AssistConfig {
560 let mut res = AssistConfig::default();
561 res.insert_use.merge = self.merge_behavior();
562 res.insert_use.prefix_kind = match self.data.assist_importPrefix {
563 ImportPrefixDef::Plain => PrefixKind::Plain,
564 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
565 ImportPrefixDef::BySelf => PrefixKind::BySelf,
566 };
567 res.allow_snippets(self.experimental("snippetTextEdit"));
568 res
569 }
570 pub fn call_info_full(&self) -> bool {
571 self.data.callInfo_full
572 }
573 pub fn lens(&self) -> LensConfig {
574 LensConfig {
575 run: self.data.lens_enable && self.data.lens_run,
576 debug: self.data.lens_enable && self.data.lens_debug,
577 implementations: self.data.lens_enable && self.data.lens_implementations,
578 method_refs: self.data.lens_enable && self.data.lens_methodReferences,
579 }
580 }
581 pub fn hover(&self) -> HoverConfig {
582 HoverConfig {
583 implementations: self.data.hoverActions_enable
584 && self.data.hoverActions_implementations,
585 run: self.data.hoverActions_enable && self.data.hoverActions_run,
586 debug: self.data.hoverActions_enable && self.data.hoverActions_debug,
587 goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef,
588 links_in_hover: self.data.hoverActions_linksInHover,
589 markdown: try_or!(
590 self.caps
591 .text_document
592 .as_ref()?
593 .hover
594 .as_ref()?
595 .content_format
596 .as_ref()?
597 .as_slice(),
598 &[]
599 )
600 .contains(&MarkupKind::Markdown),
601 }
602 }
603 pub fn semantic_tokens_refresh(&self) -> bool {
604 try_or!(self.caps.workspace.as_ref()?.semantic_tokens.as_ref()?.refresh_support?, false)
605 }
606 pub fn code_lens_refresh(&self) -> bool {
607 try_or!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?, false)
608 }
628} 609}
629 610
630#[derive(Deserialize)] 611#[derive(Deserialize, Debug, Clone)]
631#[serde(untagged)] 612#[serde(untagged)]
632enum ManifestOrProjectJson { 613enum ManifestOrProjectJson {
633 Manifest(PathBuf), 614 Manifest(PathBuf),
634 ProjectJson(ProjectJsonData), 615 ProjectJson(ProjectJsonData),
635} 616}
636 617
637#[derive(Deserialize)] 618#[derive(Deserialize, Debug, Clone)]
638#[serde(rename_all = "snake_case")] 619#[serde(rename_all = "snake_case")]
639enum MergeBehaviorDef { 620enum MergeBehaviorDef {
640 None, 621 None,
@@ -642,7 +623,7 @@ enum MergeBehaviorDef {
642 Last, 623 Last,
643} 624}
644 625
645#[derive(Deserialize)] 626#[derive(Deserialize, Debug, Clone)]
646#[serde(rename_all = "snake_case")] 627#[serde(rename_all = "snake_case")]
647enum ImportPrefixDef { 628enum ImportPrefixDef {
648 Plain, 629 Plain,
@@ -658,6 +639,7 @@ macro_rules! _config_data {
658 )* 639 )*
659 }) => { 640 }) => {
660 #[allow(non_snake_case)] 641 #[allow(non_snake_case)]
642 #[derive(Debug, Clone)]
661 struct $name { $($field: $ty,)* } 643 struct $name { $($field: $ty,)* }
662 impl $name { 644 impl $name {
663 fn from_json(mut json: serde_json::Value) -> $name { 645 fn from_json(mut json: serde_json::Value) -> $name {
@@ -763,6 +745,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
763 "Option<String>" => set! { 745 "Option<String>" => set! {
764 "type": ["null", "string"], 746 "type": ["null", "string"],
765 }, 747 },
748 "Option<PathBuf>" => set! {
749 "type": ["null", "string"],
750 },
766 "Option<bool>" => set! { 751 "Option<bool>" => set! {
767 "type": ["null", "boolean"], 752 "type": ["null", "boolean"],
768 }, 753 },
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 71dc56915..19ab4d596 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -109,7 +109,7 @@ impl GlobalState {
109 Handle { handle, receiver } 109 Handle { handle, receiver }
110 }; 110 };
111 111
112 let analysis_host = AnalysisHost::new(config.lru_capacity); 112 let analysis_host = AnalysisHost::new(config.lru_capacity());
113 let (flycheck_sender, flycheck_receiver) = unbounded(); 113 let (flycheck_sender, flycheck_receiver) = unbounded();
114 GlobalState { 114 GlobalState {
115 sender, 115 sender,
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 071b34cda..33661325a 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -9,9 +9,9 @@ use std::{
9}; 9};
10 10
11use ide::{ 11use ide::{
12 AssistConfig, CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, 12 CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData,
13 HoverGotoTypeData, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, 13 LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
14 SearchScope, SourceChange, SymbolKind, TextEdit, 14 SourceChange, SymbolKind, TextEdit,
15}; 15};
16use itertools::Itertools; 16use itertools::Itertools;
17use lsp_server::ErrorCode; 17use lsp_server::ErrorCode;
@@ -548,7 +548,7 @@ pub(crate) fn handle_runnables(
548 } 548 }
549 549
550 // Add `cargo check` and `cargo test` for all targets of the whole package 550 // Add `cargo check` and `cargo test` for all targets of the whole package
551 let config = &snap.config.runnables; 551 let config = snap.config.runnables();
552 match cargo_spec { 552 match cargo_spec {
553 Some(spec) => { 553 Some(spec) => {
554 for &cmd in ["check", "test"].iter() { 554 for &cmd in ["check", "test"].iter() {
@@ -579,9 +579,9 @@ pub(crate) fn handle_runnables(
579 kind: lsp_ext::RunnableKind::Cargo, 579 kind: lsp_ext::RunnableKind::Cargo,
580 args: lsp_ext::CargoRunnable { 580 args: lsp_ext::CargoRunnable {
581 workspace_root: None, 581 workspace_root: None,
582 override_cargo: config.override_cargo.clone(), 582 override_cargo: config.override_cargo,
583 cargo_args: vec!["check".to_string(), "--workspace".to_string()], 583 cargo_args: vec!["check".to_string(), "--workspace".to_string()],
584 cargo_extra_args: config.cargo_extra_args.clone(), 584 cargo_extra_args: config.cargo_extra_args,
585 executable_args: Vec::new(), 585 executable_args: Vec::new(),
586 expect_test: None, 586 expect_test: None,
587 }, 587 },
@@ -620,7 +620,8 @@ pub(crate) fn handle_completion(
620 return Ok(None); 620 return Ok(None);
621 } 621 }
622 622
623 let items = match snap.analysis.completions(&snap.config.completion, position)? { 623 let completion_config = &snap.config.completion();
624 let items = match snap.analysis.completions(completion_config, position)? {
624 None => return Ok(None), 625 None => return Ok(None),
625 Some(items) => items, 626 Some(items) => items,
626 }; 627 };
@@ -633,7 +634,7 @@ pub(crate) fn handle_completion(
633 let mut new_completion_items = 634 let mut new_completion_items =
634 to_proto::completion_item(&line_index, line_endings, item.clone()); 635 to_proto::completion_item(&line_index, line_endings, item.clone());
635 636
636 if snap.config.completion.resolve_additional_edits_lazily() { 637 if completion_config.resolve_additional_edits_lazily() {
637 for new_item in &mut new_completion_items { 638 for new_item in &mut new_completion_items {
638 let _ = fill_resolve_data(&mut new_item.data, &item, &text_document_position) 639 let _ = fill_resolve_data(&mut new_item.data, &item, &text_document_position)
639 .take(); 640 .take();
@@ -663,9 +664,8 @@ pub(crate) fn handle_completion_resolve(
663 } 664 }
664 665
665 // FIXME resolve the other capabilities also? 666 // FIXME resolve the other capabilities also?
666 if !snap 667 let completion_config = &snap.config.completion();
667 .config 668 if !completion_config
668 .completion
669 .active_resolve_capabilities 669 .active_resolve_capabilities
670 .contains(&CompletionResolveCapability::AdditionalTextEdits) 670 .contains(&CompletionResolveCapability::AdditionalTextEdits)
671 { 671 {
@@ -690,7 +690,7 @@ pub(crate) fn handle_completion_resolve(
690 let additional_edits = snap 690 let additional_edits = snap
691 .analysis 691 .analysis
692 .resolve_completion_edits( 692 .resolve_completion_edits(
693 &snap.config.completion, 693 &completion_config,
694 FilePosition { file_id, offset }, 694 FilePosition { file_id, offset },
695 &resolve_data.full_import_path, 695 &resolve_data.full_import_path,
696 resolve_data.imported_name, 696 resolve_data.imported_name,
@@ -746,7 +746,7 @@ pub(crate) fn handle_signature_help(
746 Some(it) => it, 746 Some(it) => it,
747 None => return Ok(None), 747 None => return Ok(None),
748 }; 748 };
749 let concise = !snap.config.call_info_full; 749 let concise = !snap.config.call_info_full();
750 let res = 750 let res =
751 to_proto::signature_help(call_info, concise, snap.config.signature_help_label_offsets()); 751 to_proto::signature_help(call_info, concise, snap.config.signature_help_label_offsets());
752 Ok(Some(res)) 752 Ok(Some(res))
@@ -758,14 +758,12 @@ pub(crate) fn handle_hover(
758) -> Result<Option<lsp_ext::Hover>> { 758) -> Result<Option<lsp_ext::Hover>> {
759 let _p = profile::span("handle_hover"); 759 let _p = profile::span("handle_hover");
760 let position = from_proto::file_position(&snap, params.text_document_position_params)?; 760 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
761 let info = match snap.analysis.hover( 761 let hover_config = snap.config.hover();
762 position, 762 let info =
763 snap.config.hover.links_in_hover, 763 match snap.analysis.hover(position, hover_config.links_in_hover, hover_config.markdown)? {
764 snap.config.hover.markdown, 764 None => return Ok(None),
765 )? { 765 Some(info) => info,
766 None => return Ok(None), 766 };
767 Some(info) => info,
768 };
769 let line_index = snap.analysis.file_line_index(position.file_id)?; 767 let line_index = snap.analysis.file_line_index(position.file_id)?;
770 let range = to_proto::range(&line_index, info.range); 768 let range = to_proto::range(&line_index, info.range);
771 let hover = lsp_ext::Hover { 769 let hover = lsp_ext::Hover {
@@ -851,7 +849,7 @@ pub(crate) fn handle_formatting(
851 let file_line_index = snap.analysis.file_line_index(file_id)?; 849 let file_line_index = snap.analysis.file_line_index(file_id)?;
852 let file_line_endings = snap.file_line_endings(file_id); 850 let file_line_endings = snap.file_line_endings(file_id);
853 851
854 let mut rustfmt = match &snap.config.rustfmt { 852 let mut rustfmt = match snap.config.rustfmt() {
855 RustfmtConfig::Rustfmt { extra_args } => { 853 RustfmtConfig::Rustfmt { extra_args } => {
856 let mut cmd = process::Command::new(toolchain::rustfmt()); 854 let mut cmd = process::Command::new(toolchain::rustfmt());
857 cmd.args(extra_args); 855 cmd.args(extra_args);
@@ -947,14 +945,12 @@ pub(crate) fn handle_code_action(
947 let range = from_proto::text_range(&line_index, params.range); 945 let range = from_proto::text_range(&line_index, params.range);
948 let frange = FileRange { file_id, range }; 946 let frange = FileRange { file_id, range };
949 947
950 let assists_config = AssistConfig { 948 let mut assists_config = snap.config.assist();
951 allowed: params 949 assists_config.allowed = params
952 .clone() 950 .clone()
953 .context 951 .context
954 .only 952 .only
955 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()), 953 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
956 ..snap.config.assist
957 };
958 954
959 let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); 955 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
960 956
@@ -989,7 +985,7 @@ fn add_quick_fixes(
989 line_index: &Arc<LineIndex>, 985 line_index: &Arc<LineIndex>,
990 acc: &mut Vec<lsp_ext::CodeAction>, 986 acc: &mut Vec<lsp_ext::CodeAction>,
991) -> Result<()> { 987) -> Result<()> {
992 let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics, frange.file_id)?; 988 let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics(), frange.file_id)?;
993 989
994 for fix in diagnostics 990 for fix in diagnostics
995 .into_iter() 991 .into_iter()
@@ -1018,7 +1014,7 @@ fn add_quick_fixes(
1018} 1014}
1019 1015
1020pub(crate) fn handle_code_action_resolve( 1016pub(crate) fn handle_code_action_resolve(
1021 mut snap: GlobalStateSnapshot, 1017 snap: GlobalStateSnapshot,
1022 mut code_action: lsp_ext::CodeAction, 1018 mut code_action: lsp_ext::CodeAction,
1023) -> Result<lsp_ext::CodeAction> { 1019) -> Result<lsp_ext::CodeAction> {
1024 let _p = profile::span("handle_code_action_resolve"); 1020 let _p = profile::span("handle_code_action_resolve");
@@ -1032,13 +1028,14 @@ pub(crate) fn handle_code_action_resolve(
1032 let range = from_proto::text_range(&line_index, params.code_action_params.range); 1028 let range = from_proto::text_range(&line_index, params.code_action_params.range);
1033 let frange = FileRange { file_id, range }; 1029 let frange = FileRange { file_id, range };
1034 1030
1035 snap.config.assist.allowed = params 1031 let mut assists_config = snap.config.assist();
1032 assists_config.allowed = params
1036 .code_action_params 1033 .code_action_params
1037 .context 1034 .context
1038 .only 1035 .only
1039 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); 1036 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
1040 1037
1041 let assists = snap.analysis.assists(&snap.config.assist, true, frange)?; 1038 let assists = snap.analysis.assists(&assists_config, true, frange)?;
1042 let (id, index) = split_once(&params.id, ':').unwrap(); 1039 let (id, index) = split_once(&params.id, ':').unwrap();
1043 let index = index.parse::<usize>().unwrap(); 1040 let index = index.parse::<usize>().unwrap();
1044 let assist = &assists[index]; 1041 let assist = &assists[index];
@@ -1055,7 +1052,8 @@ pub(crate) fn handle_code_lens(
1055 let _p = profile::span("handle_code_lens"); 1052 let _p = profile::span("handle_code_lens");
1056 let mut lenses: Vec<CodeLens> = Default::default(); 1053 let mut lenses: Vec<CodeLens> = Default::default();
1057 1054
1058 if snap.config.lens.none() { 1055 let lens_config = snap.config.lens();
1056 if lens_config.none() {
1059 // early return before any db query! 1057 // early return before any db query!
1060 return Ok(Some(lenses)); 1058 return Ok(Some(lenses));
1061 } 1059 }
@@ -1064,7 +1062,7 @@ pub(crate) fn handle_code_lens(
1064 let line_index = snap.analysis.file_line_index(file_id)?; 1062 let line_index = snap.analysis.file_line_index(file_id)?;
1065 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; 1063 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
1066 1064
1067 if snap.config.lens.runnable() { 1065 if lens_config.runnable() {
1068 // Gather runnables 1066 // Gather runnables
1069 for runnable in snap.analysis.runnables(file_id)? { 1067 for runnable in snap.analysis.runnables(file_id)? {
1070 if should_skip_target(&runnable, cargo_spec.as_ref()) { 1068 if should_skip_target(&runnable, cargo_spec.as_ref()) {
@@ -1074,7 +1072,7 @@ pub(crate) fn handle_code_lens(
1074 let action = runnable.action(); 1072 let action = runnable.action();
1075 let range = to_proto::range(&line_index, runnable.nav.full_range); 1073 let range = to_proto::range(&line_index, runnable.nav.full_range);
1076 let r = to_proto::runnable(&snap, file_id, runnable)?; 1074 let r = to_proto::runnable(&snap, file_id, runnable)?;
1077 if snap.config.lens.run { 1075 if lens_config.run {
1078 let lens = CodeLens { 1076 let lens = CodeLens {
1079 range, 1077 range,
1080 command: Some(run_single_command(&r, action.run_title)), 1078 command: Some(run_single_command(&r, action.run_title)),
@@ -1083,7 +1081,7 @@ pub(crate) fn handle_code_lens(
1083 lenses.push(lens); 1081 lenses.push(lens);
1084 } 1082 }
1085 1083
1086 if action.debugee && snap.config.lens.debug { 1084 if action.debugee && lens_config.debug {
1087 let debug_lens = 1085 let debug_lens =
1088 CodeLens { range, command: Some(debug_single_command(&r)), data: None }; 1086 CodeLens { range, command: Some(debug_single_command(&r)), data: None };
1089 lenses.push(debug_lens); 1087 lenses.push(debug_lens);
@@ -1091,7 +1089,7 @@ pub(crate) fn handle_code_lens(
1091 } 1089 }
1092 } 1090 }
1093 1091
1094 if snap.config.lens.implementations { 1092 if lens_config.implementations {
1095 // Handle impls 1093 // Handle impls
1096 lenses.extend( 1094 lenses.extend(
1097 snap.analysis 1095 snap.analysis
@@ -1126,7 +1124,7 @@ pub(crate) fn handle_code_lens(
1126 ); 1124 );
1127 } 1125 }
1128 1126
1129 if snap.config.lens.references() { 1127 if lens_config.references() {
1130 lenses.extend(snap.analysis.find_all_methods(file_id)?.into_iter().map(|it| { 1128 lenses.extend(snap.analysis.find_all_methods(file_id)?.into_iter().map(|it| {
1131 let range = to_proto::range(&line_index, it.range); 1129 let range = to_proto::range(&line_index, it.range);
1132 let position = to_proto::position(&line_index, it.range.start()); 1130 let position = to_proto::position(&line_index, it.range.start());
@@ -1272,7 +1270,7 @@ pub(crate) fn publish_diagnostics(
1272 1270
1273 let diagnostics: Vec<Diagnostic> = snap 1271 let diagnostics: Vec<Diagnostic> = snap
1274 .analysis 1272 .analysis
1275 .diagnostics(&snap.config.diagnostics, file_id)? 1273 .diagnostics(&snap.config.diagnostics(), file_id)?
1276 .into_iter() 1274 .into_iter()
1277 .map(|d| Diagnostic { 1275 .map(|d| Diagnostic {
1278 range: to_proto::range(&line_index, d.range), 1276 range: to_proto::range(&line_index, d.range),
@@ -1305,7 +1303,7 @@ pub(crate) fn handle_inlay_hints(
1305 let line_index = snap.analysis.file_line_index(file_id)?; 1303 let line_index = snap.analysis.file_line_index(file_id)?;
1306 Ok(snap 1304 Ok(snap
1307 .analysis 1305 .analysis
1308 .inlay_hints(file_id, &snap.config.inlay_hints)? 1306 .inlay_hints(file_id, &snap.config.inlay_hints())?
1309 .into_iter() 1307 .into_iter()
1310 .map(|it| to_proto::inlay_hint(&line_index, it)) 1308 .map(|it| to_proto::inlay_hint(&line_index, it))
1311 .collect()) 1309 .collect())
@@ -1575,7 +1573,7 @@ fn show_impl_command_link(
1575 snap: &GlobalStateSnapshot, 1573 snap: &GlobalStateSnapshot,
1576 position: &FilePosition, 1574 position: &FilePosition,
1577) -> Option<lsp_ext::CommandLinkGroup> { 1575) -> Option<lsp_ext::CommandLinkGroup> {
1578 if snap.config.hover.implementations { 1576 if snap.config.hover().implementations {
1579 if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) { 1577 if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) {
1580 let uri = to_proto::url(snap, position.file_id); 1578 let uri = to_proto::url(snap, position.file_id);
1581 let line_index = snap.analysis.file_line_index(position.file_id).ok()?; 1579 let line_index = snap.analysis.file_line_index(position.file_id).ok()?;
@@ -1603,7 +1601,8 @@ fn runnable_action_links(
1603 runnable: Runnable, 1601 runnable: Runnable,
1604) -> Option<lsp_ext::CommandLinkGroup> { 1602) -> Option<lsp_ext::CommandLinkGroup> {
1605 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?; 1603 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?;
1606 if !snap.config.hover.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) { 1604 let hover_config = snap.config.hover();
1605 if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) {
1607 return None; 1606 return None;
1608 } 1607 }
1609 1608
@@ -1611,12 +1610,12 @@ fn runnable_action_links(
1611 to_proto::runnable(snap, file_id, runnable).ok().map(|r| { 1610 to_proto::runnable(snap, file_id, runnable).ok().map(|r| {
1612 let mut group = lsp_ext::CommandLinkGroup::default(); 1611 let mut group = lsp_ext::CommandLinkGroup::default();
1613 1612
1614 if snap.config.hover.run { 1613 if hover_config.run {
1615 let run_command = run_single_command(&r, action.run_title); 1614 let run_command = run_single_command(&r, action.run_title);
1616 group.commands.push(to_command_link(run_command, r.label.clone())); 1615 group.commands.push(to_command_link(run_command, r.label.clone()));
1617 } 1616 }
1618 1617
1619 if snap.config.hover.debug { 1618 if hover_config.debug {
1620 let dbg_command = debug_single_command(&r); 1619 let dbg_command = debug_single_command(&r);
1621 group.commands.push(to_command_link(dbg_command, r.label)); 1620 group.commands.push(to_command_link(dbg_command, r.label));
1622 } 1621 }
@@ -1629,7 +1628,7 @@ fn goto_type_action_links(
1629 snap: &GlobalStateSnapshot, 1628 snap: &GlobalStateSnapshot,
1630 nav_targets: &[HoverGotoTypeData], 1629 nav_targets: &[HoverGotoTypeData],
1631) -> Option<lsp_ext::CommandLinkGroup> { 1630) -> Option<lsp_ext::CommandLinkGroup> {
1632 if !snap.config.hover.goto_type_def || nav_targets.is_empty() { 1631 if !snap.config.hover().goto_type_def || nav_targets.is_empty() {
1633 return None; 1632 return None;
1634 } 1633 }
1635 1634
@@ -1650,7 +1649,7 @@ fn prepare_hover_actions(
1650 file_id: FileId, 1649 file_id: FileId,
1651 actions: &[HoverAction], 1650 actions: &[HoverAction],
1652) -> Vec<lsp_ext::CommandLinkGroup> { 1651) -> Vec<lsp_ext::CommandLinkGroup> {
1653 if snap.config.hover.none() || !snap.config.hover_actions() { 1652 if snap.config.hover().none() || !snap.config.hover_actions() {
1654 return Vec::new(); 1653 return Vec::new();
1655 } 1654 }
1656 1655
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 8eca79f7e..53f9546b8 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -99,7 +99,8 @@ impl fmt::Debug for Event {
99 99
100impl GlobalState { 100impl GlobalState {
101 fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> { 101 fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
102 if self.config.linked_projects.is_empty() && self.config.notifications.cargo_toml_not_found 102 if self.config.linked_projects().is_empty()
103 && self.config.notifications().cargo_toml_not_found
103 { 104 {
104 self.show_message( 105 self.show_message(
105 lsp_types::MessageType::Error, 106 lsp_types::MessageType::Error,
@@ -296,7 +297,7 @@ impl GlobalState {
296 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { 297 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
297 let diagnostics = 298 let diagnostics =
298 crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( 299 crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
299 &self.config.diagnostics_map, 300 &self.config.diagnostics_map(),
300 &diagnostic, 301 &diagnostic,
301 &workspace_root, 302 &workspace_root,
302 ); 303 );
@@ -365,13 +366,13 @@ impl GlobalState {
365 self.update_file_notifications_on_threadpool(); 366 self.update_file_notifications_on_threadpool();
366 367
367 // Refresh semantic tokens if the client supports it. 368 // Refresh semantic tokens if the client supports it.
368 if self.config.semantic_tokens_refresh { 369 if self.config.semantic_tokens_refresh() {
369 self.semantic_tokens_cache.lock().clear(); 370 self.semantic_tokens_cache.lock().clear();
370 self.send_request::<lsp_types::request::SemanticTokensRefesh>((), |_, _| ()); 371 self.send_request::<lsp_types::request::SemanticTokensRefesh>((), |_, _| ());
371 } 372 }
372 373
373 // Refresh code lens if the client supports it. 374 // Refresh code lens if the client supports it.
374 if self.config.code_lens_refresh { 375 if self.config.code_lens_refresh() {
375 self.send_request::<lsp_types::request::CodeLensRefresh>((), |_, _| ()); 376 self.send_request::<lsp_types::request::CodeLensRefresh>((), |_, _| ());
376 } 377 }
377 } 378 }
@@ -658,7 +659,7 @@ impl GlobalState {
658 .collect::<Vec<_>>(); 659 .collect::<Vec<_>>();
659 660
660 log::trace!("updating notifications for {:?}", subscriptions); 661 log::trace!("updating notifications for {:?}", subscriptions);
661 if self.config.publish_diagnostics { 662 if self.config.publish_diagnostics() {
662 let snapshot = self.snapshot(); 663 let snapshot = self.snapshot();
663 self.task_pool.handle.spawn(move || { 664 self.task_pool.handle.spawn(move || {
664 let diagnostics = subscriptions 665 let diagnostics = subscriptions
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index ce5cedeb3..51c24e966 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -19,12 +19,12 @@ impl GlobalState {
19 pub(crate) fn update_configuration(&mut self, config: Config) { 19 pub(crate) fn update_configuration(&mut self, config: Config) {
20 let _p = profile::span("GlobalState::update_configuration"); 20 let _p = profile::span("GlobalState::update_configuration");
21 let old_config = mem::replace(&mut self.config, config); 21 let old_config = mem::replace(&mut self.config, config);
22 if self.config.lru_capacity != old_config.lru_capacity { 22 if self.config.lru_capacity() != old_config.lru_capacity() {
23 self.analysis_host.update_lru_capacity(old_config.lru_capacity); 23 self.analysis_host.update_lru_capacity(self.config.lru_capacity());
24 } 24 }
25 if self.config.linked_projects != old_config.linked_projects { 25 if self.config.linked_projects() != old_config.linked_projects() {
26 self.fetch_workspaces() 26 self.fetch_workspaces()
27 } else if self.config.flycheck != old_config.flycheck { 27 } else if self.config.flycheck() != old_config.flycheck() {
28 self.reload_flycheck(); 28 self.reload_flycheck();
29 } 29 }
30 } 30 }
@@ -36,7 +36,7 @@ impl GlobalState {
36 Status::Loading | Status::NeedsReload => return, 36 Status::Loading | Status::NeedsReload => return,
37 Status::Ready | Status::Invalid => (), 37 Status::Ready | Status::Invalid => (),
38 } 38 }
39 if self.config.cargo_autoreload { 39 if self.config.cargo_autoreload() {
40 self.fetch_workspaces(); 40 self.fetch_workspaces();
41 } else { 41 } else {
42 self.transition(Status::NeedsReload); 42 self.transition(Status::NeedsReload);
@@ -94,8 +94,8 @@ impl GlobalState {
94 pub(crate) fn fetch_workspaces(&mut self) { 94 pub(crate) fn fetch_workspaces(&mut self) {
95 log::info!("will fetch workspaces"); 95 log::info!("will fetch workspaces");
96 self.task_pool.handle.spawn({ 96 self.task_pool.handle.spawn({
97 let linked_projects = self.config.linked_projects.clone(); 97 let linked_projects = self.config.linked_projects();
98 let cargo_config = self.config.cargo.clone(); 98 let cargo_config = self.config.cargo();
99 move || { 99 move || {
100 let workspaces = linked_projects 100 let workspaces = linked_projects
101 .iter() 101 .iter()
@@ -143,7 +143,7 @@ impl GlobalState {
143 return; 143 return;
144 } 144 }
145 145
146 if let FilesWatcher::Client = self.config.files.watcher { 146 if let FilesWatcher::Client = self.config.files().watcher {
147 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { 147 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
148 watchers: workspaces 148 watchers: workspaces
149 .iter() 149 .iter()
@@ -170,9 +170,9 @@ impl GlobalState {
170 170
171 let project_folders = ProjectFolders::new(&workspaces); 171 let project_folders = ProjectFolders::new(&workspaces);
172 172
173 self.proc_macro_client = match &self.config.proc_macro_srv { 173 self.proc_macro_client = match self.config.proc_macro_srv() {
174 None => None, 174 None => None,
175 Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) { 175 Some((path, args)) => match ProcMacroClient::extern_process(path.clone(), args) {
176 Ok(it) => Some(it), 176 Ok(it) => Some(it),
177 Err(err) => { 177 Err(err) => {
178 log::error!( 178 log::error!(
@@ -185,7 +185,7 @@ impl GlobalState {
185 }, 185 },
186 }; 186 };
187 187
188 let watch = match self.config.files.watcher { 188 let watch = match self.config.files().watcher {
189 FilesWatcher::Client => vec![], 189 FilesWatcher::Client => vec![],
190 FilesWatcher::Notify => project_folders.watch, 190 FilesWatcher::Notify => project_folders.watch,
191 }; 191 };
@@ -211,7 +211,7 @@ impl GlobalState {
211 }; 211 };
212 for ws in workspaces.iter() { 212 for ws in workspaces.iter() {
213 crate_graph.extend(ws.to_crate_graph( 213 crate_graph.extend(ws.to_crate_graph(
214 self.config.cargo.target.as_deref(), 214 self.config.cargo().target.as_deref(),
215 self.proc_macro_client.as_ref(), 215 self.proc_macro_client.as_ref(),
216 &mut load, 216 &mut load,
217 )); 217 ));
@@ -231,7 +231,7 @@ impl GlobalState {
231 } 231 }
232 232
233 fn reload_flycheck(&mut self) { 233 fn reload_flycheck(&mut self) {
234 let config = match self.config.flycheck.clone() { 234 let config = match self.config.flycheck() {
235 Some(it) => it, 235 Some(it) => it,
236 None => { 236 None => {
237 self.flycheck = Vec::new(); 237 self.flycheck = Vec::new();
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index e0413ec06..a5f7e3af7 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -818,7 +818,7 @@ pub(crate) fn runnable(
818 file_id: FileId, 818 file_id: FileId,
819 runnable: Runnable, 819 runnable: Runnable,
820) -> Result<lsp_ext::Runnable> { 820) -> Result<lsp_ext::Runnable> {
821 let config = &snap.config.runnables; 821 let config = snap.config.runnables();
822 let spec = CargoTargetSpec::for_file(snap, file_id)?; 822 let spec = CargoTargetSpec::for_file(snap, file_id)?;
823 let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone()); 823 let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone());
824 let target = spec.as_ref().map(|s| s.target.clone()); 824 let target = spec.as_ref().map(|s| s.target.clone());
@@ -833,9 +833,9 @@ pub(crate) fn runnable(
833 kind: lsp_ext::RunnableKind::Cargo, 833 kind: lsp_ext::RunnableKind::Cargo,
834 args: lsp_ext::CargoRunnable { 834 args: lsp_ext::CargoRunnable {
835 workspace_root: workspace_root.map(|it| it.into()), 835 workspace_root: workspace_root.map(|it| it.into()),
836 override_cargo: config.override_cargo.clone(), 836 override_cargo: config.override_cargo,
837 cargo_args, 837 cargo_args,
838 cargo_extra_args: config.cargo_extra_args.clone(), 838 cargo_extra_args: config.cargo_extra_args,
839 executable_args, 839 executable_args,
840 expect_test: None, 840 expect_test: None,
841 }, 841 },
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs
index 84db0856d..38d09f3ee 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/main.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs
@@ -13,6 +13,7 @@ mod support;
13 13
14use std::{collections::HashMap, path::PathBuf, time::Instant}; 14use std::{collections::HashMap, path::PathBuf, time::Instant};
15 15
16use expect_test::expect;
16use lsp_types::{ 17use lsp_types::{
17 notification::DidOpenTextDocument, 18 notification::DidOpenTextDocument,
18 request::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest}, 19 request::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest},
@@ -569,9 +570,9 @@ fn main() {
569} 570}
570"###, 571"###,
571 ) 572 )
572 .with_config(|config| { 573 .with_config(serde_json::json!({
573 config.cargo.load_out_dirs_from_check = true; 574 "cargo": { "loadOutDirsFromCheck": true }
574 }) 575 }))
575 .server() 576 .server()
576 .wait_until_workspace_is_loaded(); 577 .wait_until_workspace_is_loaded();
577 578
@@ -712,12 +713,13 @@ pub fn foo(_input: TokenStream) -> TokenStream {
712 713
713"###, 714"###,
714 ) 715 )
715 .with_config(|config| { 716 .with_config(serde_json::json!({
716 let macro_srv_path = PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer")); 717 "cargo": { "loadOutDirsFromCheck": true },
717 718 "procMacro": {
718 config.cargo.load_out_dirs_from_check = true; 719 "enable": true,
719 config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".into()])); 720 "server": PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer")),
720 }) 721 }
722 }))
721 .root("foo") 723 .root("foo")
722 .root("bar") 724 .root("bar")
723 .server() 725 .server()
@@ -731,5 +733,5 @@ pub fn foo(_input: TokenStream) -> TokenStream {
731 work_done_progress_params: Default::default(), 733 work_done_progress_params: Default::default(),
732 }); 734 });
733 let value = res.get("contents").unwrap().get("value").unwrap().to_string(); 735 let value = res.get("contents").unwrap().get("value").unwrap().to_string();
734 assert_eq!(value, r#""\n```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#) 736 expect![[r#""\n```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#]].assert_eq(&value);
735} 737}
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index aac7dbcce..2658ee185 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -12,11 +12,8 @@ use lsp_types::{
12 notification::Exit, request::Shutdown, TextDocumentIdentifier, Url, WorkDoneProgress, 12 notification::Exit, request::Shutdown, TextDocumentIdentifier, Url, WorkDoneProgress,
13}; 13};
14use lsp_types::{ProgressParams, ProgressParamsValue}; 14use lsp_types::{ProgressParams, ProgressParamsValue};
15use project_model::{CargoConfig, ProjectManifest}; 15use project_model::ProjectManifest;
16use rust_analyzer::{ 16use rust_analyzer::{config::Config, main_loop};
17 config::{Config, FilesConfig, FilesWatcher, LinkedProject},
18 main_loop,
19};
20use serde::Serialize; 17use serde::Serialize;
21use serde_json::{to_string_pretty, Value}; 18use serde_json::{to_string_pretty, Value};
22use test_utils::{find_mismatch, Fixture}; 19use test_utils::{find_mismatch, Fixture};
@@ -29,12 +26,18 @@ pub(crate) struct Project<'a> {
29 with_sysroot: bool, 26 with_sysroot: bool,
30 tmp_dir: Option<TestDir>, 27 tmp_dir: Option<TestDir>,
31 roots: Vec<PathBuf>, 28 roots: Vec<PathBuf>,
32 config: Option<Box<dyn Fn(&mut Config)>>, 29 config: serde_json::Value,
33} 30}
34 31
35impl<'a> Project<'a> { 32impl<'a> Project<'a> {
36 pub(crate) fn with_fixture(fixture: &str) -> Project { 33 pub(crate) fn with_fixture(fixture: &str) -> Project {
37 Project { fixture, tmp_dir: None, roots: vec![], with_sysroot: false, config: None } 34 Project {
35 fixture,
36 tmp_dir: None,
37 roots: vec![],
38 with_sysroot: false,
39 config: serde_json::Value::Null,
40 }
38 } 41 }
39 42
40 pub(crate) fn tmp_dir(mut self, tmp_dir: TestDir) -> Project<'a> { 43 pub(crate) fn tmp_dir(mut self, tmp_dir: TestDir) -> Project<'a> {
@@ -52,8 +55,8 @@ impl<'a> Project<'a> {
52 self 55 self
53 } 56 }
54 57
55 pub(crate) fn with_config(mut self, config: impl Fn(&mut Config) + 'static) -> Project<'a> { 58 pub(crate) fn with_config(mut self, config: serde_json::Value) -> Project<'a> {
56 self.config = Some(Box::new(config)); 59 self.config = config;
57 self 60 self
58 } 61 }
59 62
@@ -77,14 +80,14 @@ impl<'a> Project<'a> {
77 if roots.is_empty() { 80 if roots.is_empty() {
78 roots.push(tmp_dir_path.clone()); 81 roots.push(tmp_dir_path.clone());
79 } 82 }
80 let linked_projects = roots 83 let discovered_projects = roots
81 .into_iter() 84 .into_iter()
82 .map(|it| ProjectManifest::discover_single(&it).unwrap()) 85 .map(|it| ProjectManifest::discover_single(&it).unwrap())
83 .map(LinkedProject::from)
84 .collect::<Vec<_>>(); 86 .collect::<Vec<_>>();
85 87
86 let mut config = Config { 88 let mut config = Config::new(
87 caps: lsp_types::ClientCapabilities { 89 tmp_dir_path,
90 lsp_types::ClientCapabilities {
88 text_document: Some(lsp_types::TextDocumentClientCapabilities { 91 text_document: Some(lsp_types::TextDocumentClientCapabilities {
89 definition: Some(lsp_types::GotoCapability { 92 definition: Some(lsp_types::GotoCapability {
90 link_support: Some(true), 93 link_support: Some(true),
@@ -96,6 +99,10 @@ impl<'a> Project<'a> {
96 ), 99 ),
97 ..Default::default() 100 ..Default::default()
98 }), 101 }),
102 hover: Some(lsp_types::HoverClientCapabilities {
103 content_format: Some(vec![lsp_types::MarkupKind::Markdown]),
104 ..Default::default()
105 }),
99 ..Default::default() 106 ..Default::default()
100 }), 107 }),
101 window: Some(lsp_types::WindowClientCapabilities { 108 window: Some(lsp_types::WindowClientCapabilities {
@@ -104,14 +111,9 @@ impl<'a> Project<'a> {
104 }), 111 }),
105 ..Default::default() 112 ..Default::default()
106 }, 113 },
107 cargo: CargoConfig { no_sysroot: !self.with_sysroot, ..Default::default() }, 114 );
108 linked_projects, 115 config.discovered_projects = Some(discovered_projects);
109 files: FilesConfig { watcher: FilesWatcher::Client, exclude: Vec::new() }, 116 config.update(self.config);
110 ..Config::new(tmp_dir_path)
111 };
112 if let Some(f) = &self.config {
113 f(&mut config)
114 }
115 117
116 Server::new(tmp_dir, config) 118 Server::new(tmp_dir, config)
117 } 119 }
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index e109f2b01..e45ea5c35 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -94,6 +94,8 @@
94 Whether to show `can't find Cargo.toml` error message. 94 Whether to show `can't find Cargo.toml` error message.
95[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `false`):: 95[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `false`)::
96 Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be enabled. 96 Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be enabled.
97[[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`)::
98 Internal config, path to proc-macro server executable (typically, this is rust-analyzer itself, but we override this in tests).
97[[rust-analyzer.runnables.overrideCargo]]rust-analyzer.runnables.overrideCargo (default: `null`):: 99[[rust-analyzer.runnables.overrideCargo]]rust-analyzer.runnables.overrideCargo (default: `null`)::
98 Command to be executed instead of 'cargo' for runnables. 100 Command to be executed instead of 'cargo' for runnables.
99[[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`):: 101[[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`)::
diff --git a/editors/code/package.json b/editors/code/package.json
index 63db87064..ea7f0990c 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -663,6 +663,14 @@
663 "default": false, 663 "default": false,
664 "type": "boolean" 664 "type": "boolean"
665 }, 665 },
666 "rust-analyzer.procMacro.server": {
667 "markdownDescription": "Internal config, path to proc-macro server executable (typically, this is rust-analyzer itself, but we override this in tests).",
668 "default": null,
669 "type": [
670 "null",
671 "string"
672 ]
673 },
666 "rust-analyzer.runnables.overrideCargo": { 674 "rust-analyzer.runnables.overrideCargo": {
667 "markdownDescription": "Command to be executed instead of 'cargo' for runnables.", 675 "markdownDescription": "Command to be executed instead of 'cargo' for runnables.",
668 "default": null, 676 "default": null,