aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock8
-rw-r--r--crates/ra_flycheck/src/lib.rs2
-rw-r--r--crates/ra_ide/src/inlay_hints.rs33
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs19
-rw-r--r--crates/ra_project_model/src/lib.rs6
-rw-r--r--crates/rust-analyzer/src/bin/main.rs31
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs4
-rw-r--r--crates/rust-analyzer/src/config.rs232
-rw-r--r--crates/rust-analyzer/src/conv.rs2
-rw-r--r--crates/rust-analyzer/src/feature_flags.rs77
-rw-r--r--crates/rust-analyzer/src/lib.rs4
-rw-r--r--crates/rust-analyzer/src/main_loop.rs119
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs44
-rw-r--r--crates/rust-analyzer/src/world.rs46
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs2
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs42
16 files changed, 270 insertions, 401 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9a1a05683..c07a9614a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -331,9 +331,9 @@ dependencies = [
331 331
332[[package]] 332[[package]]
333name = "filetime" 333name = "filetime"
334version = "0.2.8" 334version = "0.2.9"
335source = "registry+https://github.com/rust-lang/crates.io-index" 335source = "registry+https://github.com/rust-lang/crates.io-index"
336checksum = "1ff6d4dab0aa0c8e6346d46052e93b13a16cf847b54ed357087c35011048cc7d" 336checksum = "f59efc38004c988e4201d11d263b8171f49a2e7ec0bdbb71773433f271504a5e"
337dependencies = [ 337dependencies = [
338 "cfg-if", 338 "cfg-if",
339 "libc", 339 "libc",
@@ -842,9 +842,9 @@ checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
842 842
843[[package]] 843[[package]]
844name = "proc-macro-hack" 844name = "proc-macro-hack"
845version = "0.5.14" 845version = "0.5.15"
846source = "registry+https://github.com/rust-lang/crates.io-index" 846source = "registry+https://github.com/rust-lang/crates.io-index"
847checksum = "fcfdefadc3d57ca21cf17990a28ef4c0f7c61383a28cb7604cf4a18e6ede1420" 847checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
848 848
849[[package]] 849[[package]]
850name = "proc-macro2" 850name = "proc-macro2"
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs
index 13494a731..b54a30ab8 100644
--- a/crates/ra_flycheck/src/lib.rs
+++ b/crates/ra_flycheck/src/lib.rs
@@ -22,7 +22,7 @@ use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};
22 22
23pub use crate::conv::url_from_path_with_drive_lowercasing; 23pub use crate::conv::url_from_path_with_drive_lowercasing;
24 24
25#[derive(Clone, Debug)] 25#[derive(Clone, Debug, PartialEq, Eq)]
26pub enum FlycheckConfig { 26pub enum FlycheckConfig {
27 CargoCommand { command: String, all_targets: bool, extra_args: Vec<String> }, 27 CargoCommand { command: String, all_targets: bool, extra_args: Vec<String> },
28 CustomCommand { command: String, args: Vec<String> }, 28 CustomCommand { command: String, args: Vec<String> },
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index d06fc03d3..4b133b19b 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -75,8 +75,7 @@ fn get_chaining_hints(
75 return None; 75 return None;
76 } 76 }
77 77
78 let ty = sema.type_of_expr(&expr)?; 78 if matches!(expr, ast::Expr::RecordLit(_)) {
79 if ty.is_unknown() {
80 return None; 79 return None;
81 } 80 }
82 81
@@ -95,6 +94,17 @@ fn get_chaining_hints(
95 let next = tokens.next()?.kind(); 94 let next = tokens.next()?.kind();
96 let next_next = tokens.next()?.kind(); 95 let next_next = tokens.next()?.kind();
97 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { 96 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT {
97 let ty = sema.type_of_expr(&expr)?;
98 if ty.is_unknown() {
99 return None;
100 }
101 if matches!(expr, ast::Expr::PathExpr(_)) {
102 if let Some(Adt::Struct(st)) = ty.as_adt() {
103 if st.fields(sema.db).is_empty() {
104 return None;
105 }
106 }
107 }
98 let label = ty.display_truncated(sema.db, config.max_length).to_string(); 108 let label = ty.display_truncated(sema.db, config.max_length).to_string();
99 acc.push(InlayHint { 109 acc.push(InlayHint {
100 range: expr.syntax().text_range(), 110 range: expr.syntax().text_range(),
@@ -1154,32 +1164,35 @@ fn main() {
1154 struct A { pub b: B } 1164 struct A { pub b: B }
1155 struct B { pub c: C } 1165 struct B { pub c: C }
1156 struct C(pub bool); 1166 struct C(pub bool);
1167 struct D;
1168
1169 impl D {
1170 fn foo(&self) -> i32 { 42 }
1171 }
1157 1172
1158 fn main() { 1173 fn main() {
1159 let x = A { b: B { c: C(true) } } 1174 let x = A { b: B { c: C(true) } }
1160 .b 1175 .b
1161 .c 1176 .c
1162 .0; 1177 .0;
1178 let x = D
1179 .foo();
1163 }"#, 1180 }"#,
1164 ); 1181 );
1165 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1182 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1166 [ 1183 [
1167 InlayHint { 1184 InlayHint {
1168 range: [150; 221), 1185 range: [252; 323),
1169 kind: ChainingHint, 1186 kind: ChainingHint,
1170 label: "C", 1187 label: "C",
1171 }, 1188 },
1172 InlayHint { 1189 InlayHint {
1173 range: [150; 198), 1190 range: [252; 300),
1174 kind: ChainingHint, 1191 kind: ChainingHint,
1175 label: "B", 1192 label: "B",
1176 }, 1193 },
1177 InlayHint { 1194 ]
1178 range: [150; 175), 1195 "###);
1179 kind: ChainingHint,
1180 label: "A",
1181 },
1182 ]"###);
1183 } 1196 }
1184 1197
1185 #[test] 1198 #[test]
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index f4fd6ad28..c1b6e1ddc 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -13,7 +13,6 @@ use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}
13use ra_arena::{Arena, Idx}; 13use ra_arena::{Arena, Idx};
14use ra_db::Edition; 14use ra_db::Edition;
15use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
16use serde::Deserialize;
17 16
18/// `CargoWorkspace` represents the logical structure of, well, a Cargo 17/// `CargoWorkspace` represents the logical structure of, well, a Cargo
19/// workspace. It pretty closely mirrors `cargo metadata` output. 18/// workspace. It pretty closely mirrors `cargo metadata` output.
@@ -43,9 +42,8 @@ impl ops::Index<Target> for CargoWorkspace {
43 } 42 }
44} 43}
45 44
46#[derive(Deserialize, Clone, Debug, PartialEq, Eq)] 45#[derive(Clone, Debug, PartialEq, Eq)]
47#[serde(rename_all = "camelCase", default)] 46pub struct CargoConfig {
48pub struct CargoFeatures {
49 /// Do not activate the `default` feature. 47 /// Do not activate the `default` feature.
50 pub no_default_features: bool, 48 pub no_default_features: bool,
51 49
@@ -60,9 +58,9 @@ pub struct CargoFeatures {
60 pub load_out_dirs_from_check: bool, 58 pub load_out_dirs_from_check: bool,
61} 59}
62 60
63impl Default for CargoFeatures { 61impl Default for CargoConfig {
64 fn default() -> Self { 62 fn default() -> Self {
65 CargoFeatures { 63 CargoConfig {
66 no_default_features: false, 64 no_default_features: false,
67 all_features: true, 65 all_features: true,
68 features: Vec::new(), 66 features: Vec::new(),
@@ -141,7 +139,7 @@ impl PackageData {
141impl CargoWorkspace { 139impl CargoWorkspace {
142 pub fn from_cargo_metadata( 140 pub fn from_cargo_metadata(
143 cargo_toml: &Path, 141 cargo_toml: &Path,
144 cargo_features: &CargoFeatures, 142 cargo_features: &CargoConfig,
145 ) -> Result<CargoWorkspace> { 143 ) -> Result<CargoWorkspace> {
146 let mut meta = MetadataCommand::new(); 144 let mut meta = MetadataCommand::new();
147 meta.manifest_path(cargo_toml); 145 meta.manifest_path(cargo_toml);
@@ -275,7 +273,7 @@ pub struct ExternResources {
275 273
276pub fn load_extern_resources( 274pub fn load_extern_resources(
277 cargo_toml: &Path, 275 cargo_toml: &Path,
278 cargo_features: &CargoFeatures, 276 cargo_features: &CargoConfig,
279) -> Result<ExternResources> { 277) -> Result<ExternResources> {
280 let mut cmd = Command::new(cargo_binary()); 278 let mut cmd = Command::new(cargo_binary());
281 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); 279 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
@@ -293,9 +291,8 @@ pub fn load_extern_resources(
293 291
294 let mut res = ExternResources::default(); 292 let mut res = ExternResources::default();
295 293
296 let stdout = String::from_utf8(output.stdout)?; 294 for message in cargo_metadata::parse_messages(output.stdout.as_slice()) {
297 for line in stdout.lines() { 295 if let Ok(message) = message {
298 if let Ok(message) = serde_json::from_str::<cargo_metadata::Message>(&line) {
299 match message { 296 match message {
300 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => { 297 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => {
301 res.out_dirs.insert(package_id, out_dir); 298 res.out_dirs.insert(package_id, out_dir);
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 444d3bb3f..dd9c80691 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -19,7 +19,7 @@ use rustc_hash::FxHashMap;
19use serde_json::from_reader; 19use serde_json::from_reader;
20 20
21pub use crate::{ 21pub use crate::{
22 cargo_workspace::{CargoFeatures, CargoWorkspace, Package, Target, TargetKind}, 22 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind},
23 json_project::JsonProject, 23 json_project::JsonProject,
24 sysroot::Sysroot, 24 sysroot::Sysroot,
25}; 25};
@@ -78,14 +78,14 @@ impl PackageRoot {
78} 78}
79 79
80impl ProjectWorkspace { 80impl ProjectWorkspace {
81 pub fn discover(path: &Path, cargo_features: &CargoFeatures) -> Result<ProjectWorkspace> { 81 pub fn discover(path: &Path, cargo_features: &CargoConfig) -> Result<ProjectWorkspace> {
82 ProjectWorkspace::discover_with_sysroot(path, true, cargo_features) 82 ProjectWorkspace::discover_with_sysroot(path, true, cargo_features)
83 } 83 }
84 84
85 pub fn discover_with_sysroot( 85 pub fn discover_with_sysroot(
86 path: &Path, 86 path: &Path,
87 with_sysroot: bool, 87 with_sysroot: bool,
88 cargo_features: &CargoFeatures, 88 cargo_features: &CargoConfig,
89 ) -> Result<ProjectWorkspace> { 89 ) -> Result<ProjectWorkspace> {
90 match find_rust_project_json(path) { 90 match find_rust_project_json(path) {
91 Some(json_path) => { 91 Some(json_path) => {
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index a744a6695..608f4f67b 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -4,8 +4,7 @@
4mod args; 4mod args;
5 5
6use lsp_server::Connection; 6use lsp_server::Connection;
7 7use rust_analyzer::{cli, config::Config, from_json, Result};
8use rust_analyzer::{cli, from_json, show_message, Result, ServerConfig};
9 8
10use crate::args::HelpPrinted; 9use crate::args::HelpPrinted;
11 10
@@ -78,24 +77,18 @@ fn run_server() -> Result<()> {
78 .filter(|workspaces| !workspaces.is_empty()) 77 .filter(|workspaces| !workspaces.is_empty())
79 .unwrap_or_else(|| vec![root]); 78 .unwrap_or_else(|| vec![root]);
80 79
81 let server_config = initialize_params 80 let config = {
82 .initialization_options 81 let mut config = Config::default();
83 .and_then(|v| { 82 if let Some(value) = &initialize_params.initialization_options {
84 from_json::<ServerConfig>("config", v) 83 config.update(value);
85 .map_err(|e| { 84 }
86 log::error!("{}", e); 85 if let Some(caps) = &initialize_params.capabilities.text_document {
87 show_message(lsp_types::MessageType::Error, e.to_string(), &connection.sender); 86 config.update_caps(caps);
88 }) 87 }
89 .ok() 88 config
90 }) 89 };
91 .unwrap_or_default();
92 90
93 rust_analyzer::main_loop( 91 rust_analyzer::main_loop(workspace_roots, config, connection)?;
94 workspace_roots,
95 initialize_params.capabilities,
96 server_config,
97 connection,
98 )?;
99 92
100 log::info!("shutting down IO..."); 93 log::info!("shutting down IO...");
101 io_threads.join()?; 94 io_threads.join()?;
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 832f04226..2c0bde920 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -8,7 +8,7 @@ use crossbeam_channel::{unbounded, Receiver};
8use ra_db::{ExternSourceId, FileId, SourceRootId}; 8use ra_db::{ExternSourceId, FileId, SourceRootId};
9use ra_ide::{AnalysisChange, AnalysisHost}; 9use ra_ide::{AnalysisChange, AnalysisHost};
10use ra_project_model::{ 10use ra_project_model::{
11 get_rustc_cfg_options, CargoFeatures, PackageRoot, ProcMacroClient, ProjectWorkspace, 11 get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectWorkspace,
12}; 12};
13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; 13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
14use rustc_hash::{FxHashMap, FxHashSet}; 14use rustc_hash::{FxHashMap, FxHashSet};
@@ -29,7 +29,7 @@ pub(crate) fn load_cargo(
29 let root = std::env::current_dir()?.join(root); 29 let root = std::env::current_dir()?.join(root);
30 let ws = ProjectWorkspace::discover( 30 let ws = ProjectWorkspace::discover(
31 root.as_ref(), 31 root.as_ref(),
32 &CargoFeatures { load_out_dirs_from_check, ..Default::default() }, 32 &CargoConfig { load_out_dirs_from_check, ..Default::default() },
33 )?; 33 )?;
34 34
35 let mut extern_dirs = FxHashSet::default(); 35 let mut extern_dirs = FxHashSet::default();
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 628ed107e..3c8f55f1e 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -7,112 +7,164 @@
7//! configure the server itself, feature flags are passed into analysis, and 7//! configure the server itself, feature flags are passed into analysis, and
8//! tweak things like automatic insertion of `()` in completions. 8//! tweak things like automatic insertion of `()` in completions.
9 9
10use rustc_hash::FxHashMap; 10use lsp_types::TextDocumentClientCapabilities;
11 11use ra_flycheck::FlycheckConfig;
12use ra_project_model::CargoFeatures; 12use ra_ide::{CompletionConfig, InlayHintsConfig};
13use serde::{Deserialize, Deserializer}; 13use ra_project_model::CargoConfig;
14 14use serde::Deserialize;
15/// Client provided initialization options 15
16#[derive(Deserialize, Clone, Debug, PartialEq, Eq)] 16#[derive(Debug, Clone)]
17#[serde(rename_all = "camelCase", default)] 17pub struct Config {
18pub struct ServerConfig { 18 pub client_caps: ClientCapsConfig,
19 /// Whether the client supports our custom highlighting publishing decorations.
20 /// This is different to the highlightingOn setting, which is whether the user
21 /// wants our custom highlighting to be used.
22 ///
23 /// Defaults to `false`
24 #[serde(deserialize_with = "nullable_bool_false")]
25 pub publish_decorations: bool, 19 pub publish_decorations: bool,
26 20 pub publish_diagnostics: bool,
27 pub exclude_globs: Vec<String>, 21 pub notifications: NotificationsConfig,
28 #[serde(deserialize_with = "nullable_bool_false")] 22 pub inlay_hints: InlayHintsConfig,
29 pub use_client_watching: bool, 23 pub completion: CompletionConfig,
30 24 pub call_info_full: bool,
25 pub rustfmt: RustfmtConfig,
26 pub check: Option<FlycheckConfig>,
27 pub vscode_lldb: bool,
28 pub proc_macro_srv: Option<String>,
31 pub lru_capacity: Option<usize>, 29 pub lru_capacity: Option<usize>,
32 30 pub use_client_watching: bool,
33 #[serde(deserialize_with = "nullable_bool_true")] 31 pub exclude_globs: Vec<String>,
34 pub inlay_hints_type: bool, 32 pub cargo: CargoConfig,
35 #[serde(deserialize_with = "nullable_bool_true")]
36 pub inlay_hints_parameter: bool,
37 #[serde(deserialize_with = "nullable_bool_true")]
38 pub inlay_hints_chaining: bool,
39 pub inlay_hints_max_length: Option<usize>,
40
41 pub cargo_watch_enable: bool,
42 pub cargo_watch_args: Vec<String>,
43 pub cargo_watch_command: String,
44 pub cargo_watch_all_targets: bool,
45
46 /// For internal usage to make integrated tests faster.
47 #[serde(deserialize_with = "nullable_bool_true")]
48 pub with_sysroot: bool, 33 pub with_sysroot: bool,
34}
49 35
50 /// Fine grained feature flags to disable specific features. 36#[derive(Debug, Clone)]
51 pub feature_flags: FxHashMap<String, bool>, 37pub struct NotificationsConfig {
52 38 pub workspace_loaded: bool,
53 pub rustfmt_args: Vec<String>, 39 pub cargo_toml_not_found: bool,
40}
54 41
55 /// Cargo feature configurations. 42#[derive(Debug, Clone)]
56 pub cargo_features: CargoFeatures, 43pub enum RustfmtConfig {
44 Rustfmt {
45 extra_args: Vec<String>,
46 },
47 #[allow(unused)]
48 CustomCommand {
49 command: String,
50 args: Vec<String>,
51 },
52}
57 53
58 /// Enabled if the vscode_lldb extension is available. 54#[derive(Debug, Clone, Default)]
59 pub vscode_lldb: bool, 55pub struct ClientCapsConfig {
56 pub location_link: bool,
57 pub line_folding_only: bool,
60} 58}
61 59
62impl Default for ServerConfig { 60impl Default for Config {
63 fn default() -> ServerConfig { 61 fn default() -> Self {
64 ServerConfig { 62 Config {
65 publish_decorations: false, 63 publish_decorations: false,
66 exclude_globs: Vec::new(), 64 publish_diagnostics: true,
67 use_client_watching: false, 65 notifications: NotificationsConfig {
66 workspace_loaded: true,
67 cargo_toml_not_found: true,
68 },
69 client_caps: ClientCapsConfig::default(),
70 inlay_hints: InlayHintsConfig {
71 type_hints: true,
72 parameter_hints: true,
73 chaining_hints: true,
74 max_length: None,
75 },
76 completion: CompletionConfig {
77 enable_postfix_completions: true,
78 add_call_parenthesis: true,
79 add_call_argument_snippets: true,
80 },
81 call_info_full: true,
82 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
83 check: Some(FlycheckConfig::CargoCommand {
84 command: "check".to_string(),
85 all_targets: true,
86 extra_args: Vec::new(),
87 }),
88 vscode_lldb: false,
89 proc_macro_srv: None,
68 lru_capacity: None, 90 lru_capacity: None,
69 inlay_hints_type: true, 91 use_client_watching: false,
70 inlay_hints_parameter: true, 92 exclude_globs: Vec::new(),
71 inlay_hints_chaining: true, 93 cargo: CargoConfig::default(),
72 inlay_hints_max_length: None,
73 cargo_watch_enable: true,
74 cargo_watch_args: Vec::new(),
75 cargo_watch_command: "check".to_string(),
76 cargo_watch_all_targets: true,
77 with_sysroot: true, 94 with_sysroot: true,
78 feature_flags: FxHashMap::default(),
79 cargo_features: Default::default(),
80 rustfmt_args: Vec::new(),
81 vscode_lldb: false,
82 } 95 }
83 } 96 }
84} 97}
85 98
86/// Deserializes a null value to a bool false by default 99impl Config {
87fn nullable_bool_false<'de, D>(deserializer: D) -> Result<bool, D::Error> 100 #[rustfmt::skip]
88where 101 pub fn update(&mut self, value: &serde_json::Value) {
89 D: Deserializer<'de>, 102 log::info!("Config::update({:#})", value);
90{ 103
91 let opt = Option::deserialize(deserializer)?; 104 let client_caps = self.client_caps.clone();
92 Ok(opt.unwrap_or(false)) 105 *self = Default::default();
93} 106 self.client_caps = client_caps;
107
108 set(value, "/publishDecorations", &mut self.publish_decorations);
109 set(value, "/excludeGlobs", &mut self.exclude_globs);
110 set(value, "/useClientWatching", &mut self.use_client_watching);
111 set(value, "/lruCapacity", &mut self.lru_capacity);
112
113 set(value, "/inlayHintsType", &mut self.inlay_hints.type_hints);
114 set(value, "/inlayHintsParameter", &mut self.inlay_hints.parameter_hints);
115 set(value, "/inlayHintsChaining", &mut self.inlay_hints.chaining_hints);
116 set(value, "/inlayHintsMaxLength", &mut self.inlay_hints.max_length);
117
118 if let Some(false) = get(value, "cargo_watch_enable") {
119 self.check = None
120 } else {
121 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets }) = &mut self.check
122 {
123 set(value, "/cargoWatchArgs", extra_args);
124 set(value, "/cargoWatchCommand", command);
125 set(value, "/cargoWatchAllTargets", all_targets);
126 }
127 };
128
129 set(value, "/withSysroot", &mut self.with_sysroot);
130 if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt {
131 set(value, "/rustfmtArgs", extra_args);
132 }
94 133
95/// Deserializes a null value to a bool true by default 134 set(value, "/cargoFeatures/noDefaultFeatures", &mut self.cargo.no_default_features);
96fn nullable_bool_true<'de, D>(deserializer: D) -> Result<bool, D::Error> 135 set(value, "/cargoFeatures/allFeatures", &mut self.cargo.all_features);
97where 136 set(value, "/cargoFeatures/features", &mut self.cargo.features);
98 D: Deserializer<'de>, 137 set(value, "/cargoFeatures/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
99{ 138
100 let opt = Option::deserialize(deserializer)?; 139 set(value, "/vscodeLldb", &mut self.vscode_lldb);
101 Ok(opt.unwrap_or(true)) 140
102} 141 set(value, "/featureFlags/lsp.diagnostics", &mut self.publish_diagnostics);
142 set(value, "/featureFlags/notifications.workspace-loaded", &mut self.notifications.workspace_loaded);
143 set(value, "/featureFlags/notifications.cargo-toml-not-found", &mut self.notifications.cargo_toml_not_found);
144 set(value, "/featureFlags/completion.enable-postfix", &mut self.completion.enable_postfix_completions);
145 set(value, "/featureFlags/completion.insertion.add-call-parenthesis", &mut self.completion.add_call_parenthesis);
146 set(value, "/featureFlags/completion.insertion.add-argument-snippets", &mut self.completion.add_call_argument_snippets);
147 set(value, "/featureFlags/call-info.full", &mut self.call_info_full);
103 148
104#[cfg(test)] 149 log::info!("Config::update() = {:#?}", self);
105mod test { 150
106 use super::*; 151 fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
107 152 value.pointer(pointer).and_then(|it| T::deserialize(it).ok())
108 #[test] 153 }
109 fn deserialize_init_options_defaults() { 154
110 // check that null == default for both fields 155 fn set<'a, T: Deserialize<'a> + std::fmt::Debug>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) {
111 let default = ServerConfig::default(); 156 if let Some(new_value) = get(value, pointer) {
112 assert_eq!(default, serde_json::from_str(r#"{}"#).unwrap()); 157 *slot = new_value
113 assert_eq!( 158 }
114 default, 159 }
115 serde_json::from_str(r#"{"publishDecorations":null, "lruCapacity":null}"#).unwrap() 160 }
116 ); 161
162 pub fn update_caps(&mut self, caps: &TextDocumentClientCapabilities) {
163 if let Some(value) = caps.definition.as_ref().and_then(|it| it.link_support) {
164 self.client_caps.location_link = value;
165 }
166 if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) {
167 self.client_caps.line_folding_only = value
168 }
117 } 169 }
118} 170}
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index e8dc953c3..57c4c8ce5 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -579,7 +579,7 @@ impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<Vec<NavigationTarget>>)
579 .into_iter() 579 .into_iter()
580 .map(|nav| (file_id, RangeInfo::new(range, nav))) 580 .map(|nav| (file_id, RangeInfo::new(range, nav)))
581 .try_conv_with_to_vec(world)?; 581 .try_conv_with_to_vec(world)?;
582 if world.config.supports_location_link { 582 if world.config.client_caps.location_link {
583 Ok(links.into()) 583 Ok(links.into())
584 } else { 584 } else {
585 let locations: Vec<Location> = links 585 let locations: Vec<Location> = links
diff --git a/crates/rust-analyzer/src/feature_flags.rs b/crates/rust-analyzer/src/feature_flags.rs
deleted file mode 100644
index dbb3f50a0..000000000
--- a/crates/rust-analyzer/src/feature_flags.rs
+++ /dev/null
@@ -1,77 +0,0 @@
1//! See docs for `FeatureFlags`.
2
3use rustc_hash::FxHashMap;
4
5// FIXME: looks like a much better design is to pass options to each call,
6// rather than to have a global ambient feature flags -- that way, the clients
7// can issue two successive calls with different options.
8
9/// Feature flags hold fine-grained toggles for all *user-visible* features of
10/// rust-analyzer.
11///
12/// The exists such that users are able to disable any annoying feature (and,
13/// with many users and many features, some features are bound to be annoying
14/// for some users)
15///
16/// Note that we purposefully use run-time checked strings, and not something
17/// checked at compile time, to keep things simple and flexible.
18///
19/// Also note that, at the moment, `FeatureFlags` also store features for
20/// `rust-analyzer`. This should be benign layering violation.
21#[derive(Debug)]
22pub struct FeatureFlags {
23 flags: FxHashMap<String, bool>,
24}
25
26impl FeatureFlags {
27 fn new(flags: &[(&str, bool)]) -> FeatureFlags {
28 let flags = flags
29 .iter()
30 .map(|&(name, value)| {
31 check_flag_name(name);
32 (name.to_string(), value)
33 })
34 .collect();
35 FeatureFlags { flags }
36 }
37
38 pub fn set(&mut self, flag: &str, value: bool) -> Result<(), ()> {
39 match self.flags.get_mut(flag) {
40 None => Err(()),
41 Some(slot) => {
42 *slot = value;
43 Ok(())
44 }
45 }
46 }
47
48 pub fn get(&self, flag: &str) -> bool {
49 match self.flags.get(flag) {
50 None => panic!("unknown flag: {:?}", flag),
51 Some(value) => *value,
52 }
53 }
54}
55
56impl Default for FeatureFlags {
57 fn default() -> FeatureFlags {
58 FeatureFlags::new(&[
59 ("lsp.diagnostics", true),
60 ("completion.insertion.add-call-parenthesis", true),
61 ("completion.insertion.add-argument-snippets", true),
62 ("completion.enable-postfix", true),
63 ("call-info.full", true),
64 ("notifications.workspace-loaded", true),
65 ("notifications.cargo-toml-not-found", true),
66 ])
67 }
68}
69
70fn check_flag_name(flag: &str) {
71 for c in flag.bytes() {
72 match c {
73 b'a'..=b'z' | b'-' | b'.' => (),
74 _ => panic!("flag name does not match conventions: {:?}", flag),
75 }
76 }
77}
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index e50e47b19..02953be30 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -33,18 +33,16 @@ mod conv;
33mod main_loop; 33mod main_loop;
34mod markdown; 34mod markdown;
35pub mod req; 35pub mod req;
36mod config; 36pub mod config;
37mod world; 37mod world;
38mod diagnostics; 38mod diagnostics;
39mod semantic_tokens; 39mod semantic_tokens;
40mod feature_flags;
41 40
42use serde::de::DeserializeOwned; 41use serde::de::DeserializeOwned;
43 42
44pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; 43pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
45pub use crate::{ 44pub use crate::{
46 caps::server_capabilities, 45 caps::server_capabilities,
47 config::ServerConfig,
48 main_loop::LspError, 46 main_loop::LspError,
49 main_loop::{main_loop, show_message}, 47 main_loop::{main_loop, show_message},
50}; 48};
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index a89ea86ea..45ae0ad9d 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -17,12 +17,11 @@ use std::{
17use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; 17use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
18use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 18use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
19use lsp_types::{ 19use lsp_types::{
20 ClientCapabilities, NumberOrString, TextDocumentClientCapabilities, WorkDoneProgress, 20 NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams,
21 WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, 21 WorkDoneProgressEnd, WorkDoneProgressReport,
22 WorkDoneProgressReport,
23}; 22};
24use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask, FlycheckConfig}; 23use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask};
25use ra_ide::{Canceled, FileId, InlayHintsConfig, LibraryData, SourceRootId}; 24use ra_ide::{Canceled, FileId, LibraryData, SourceRootId};
26use ra_prof::profile; 25use ra_prof::profile;
27use ra_vfs::{VfsFile, VfsTask, Watch}; 26use ra_vfs::{VfsFile, VfsTask, Watch};
28use relative_path::RelativePathBuf; 27use relative_path::RelativePathBuf;
@@ -31,15 +30,15 @@ use serde::{de::DeserializeOwned, Serialize};
31use threadpool::ThreadPool; 30use threadpool::ThreadPool;
32 31
33use crate::{ 32use crate::{
33 config::Config,
34 diagnostics::DiagnosticTask, 34 diagnostics::DiagnosticTask,
35 feature_flags::FeatureFlags,
36 main_loop::{ 35 main_loop::{
37 pending_requests::{PendingRequest, PendingRequests}, 36 pending_requests::{PendingRequest, PendingRequests},
38 subscriptions::Subscriptions, 37 subscriptions::Subscriptions,
39 }, 38 },
40 req, 39 req,
41 world::{Config, WorldSnapshot, WorldState}, 40 world::{WorldSnapshot, WorldState},
42 Result, ServerConfig, 41 Result,
43}; 42};
44use req::ConfigurationParams; 43use req::ConfigurationParams;
45 44
@@ -65,64 +64,8 @@ impl fmt::Display for LspError {
65 64
66impl Error for LspError {} 65impl Error for LspError {}
67 66
68fn get_feature_flags(config: &ServerConfig, connection: &Connection) -> FeatureFlags { 67pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> {
69 let mut ff = FeatureFlags::default(); 68 log::info!("initial config: {:#?}", config);
70 for (flag, &value) in &config.feature_flags {
71 if ff.set(flag.as_str(), value).is_err() {
72 log::error!("unknown feature flag: {:?}", flag);
73 show_message(
74 req::MessageType::Error,
75 format!("unknown feature flag: {:?}", flag),
76 &connection.sender,
77 );
78 }
79 }
80 log::info!("feature_flags: {:#?}", ff);
81 ff
82}
83
84fn get_config(
85 config: &ServerConfig,
86 text_document_caps: Option<&TextDocumentClientCapabilities>,
87) -> Config {
88 Config {
89 publish_decorations: config.publish_decorations,
90 supports_location_link: text_document_caps
91 .and_then(|it| it.definition)
92 .and_then(|it| it.link_support)
93 .unwrap_or(false),
94 line_folding_only: text_document_caps
95 .and_then(|it| it.folding_range.as_ref())
96 .and_then(|it| it.line_folding_only)
97 .unwrap_or(false),
98 inlay_hints: InlayHintsConfig {
99 type_hints: config.inlay_hints_type,
100 parameter_hints: config.inlay_hints_parameter,
101 chaining_hints: config.inlay_hints_chaining,
102 max_length: config.inlay_hints_max_length,
103 },
104 check: if config.cargo_watch_enable {
105 Some(FlycheckConfig::CargoCommand {
106 command: config.cargo_watch_command.clone(),
107 all_targets: config.cargo_watch_all_targets,
108 extra_args: config.cargo_watch_args.clone(),
109 })
110 } else {
111 None
112 },
113 rustfmt_args: config.rustfmt_args.clone(),
114 vscode_lldb: config.vscode_lldb,
115 proc_macro_srv: None, // FIXME: get this from config
116 }
117}
118
119pub fn main_loop(
120 ws_roots: Vec<PathBuf>,
121 client_caps: ClientCapabilities,
122 config: ServerConfig,
123 connection: Connection,
124) -> Result<()> {
125 log::info!("server_config: {:#?}", config);
126 69
127 // Windows scheduler implements priority boosts: if thread waits for an 70 // Windows scheduler implements priority boosts: if thread waits for an
128 // event (like a condvar), and event fires, priority of the thread is 71 // event (like a condvar), and event fires, priority of the thread is
@@ -143,11 +86,8 @@ pub fn main_loop(
143 SetThreadPriority(thread, thread_priority_above_normal); 86 SetThreadPriority(thread, thread_priority_above_normal);
144 } 87 }
145 88
146 let text_document_caps = client_caps.text_document.as_ref();
147 let mut loop_state = LoopState::default(); 89 let mut loop_state = LoopState::default();
148 let mut world_state = { 90 let mut world_state = {
149 let feature_flags = get_feature_flags(&config, &connection);
150
151 // FIXME: support dynamic workspace loading. 91 // FIXME: support dynamic workspace loading.
152 let workspaces = { 92 let workspaces = {
153 let mut loaded_workspaces = Vec::new(); 93 let mut loaded_workspaces = Vec::new();
@@ -155,7 +95,7 @@ pub fn main_loop(
155 let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( 95 let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot(
156 ws_root.as_path(), 96 ws_root.as_path(),
157 config.with_sysroot, 97 config.with_sysroot,
158 &config.cargo_features, 98 &config.cargo,
159 ); 99 );
160 match workspace { 100 match workspace {
161 Ok(workspace) => loaded_workspaces.push(workspace), 101 Ok(workspace) => loaded_workspaces.push(workspace),
@@ -165,7 +105,7 @@ pub fn main_loop(
165 if let Some(ra_project_model::CargoTomlNotFoundError { .. }) = 105 if let Some(ra_project_model::CargoTomlNotFoundError { .. }) =
166 e.downcast_ref() 106 e.downcast_ref()
167 { 107 {
168 if !feature_flags.get("notifications.cargo-toml-not-found") { 108 if !config.notifications.cargo_toml_not_found {
169 continue; 109 continue;
170 } 110 }
171 } 111 }
@@ -214,8 +154,7 @@ pub fn main_loop(
214 config.lru_capacity, 154 config.lru_capacity,
215 &globs, 155 &globs,
216 Watch(!config.use_client_watching), 156 Watch(!config.use_client_watching),
217 get_config(&config, text_document_caps), 157 config,
218 feature_flags,
219 ) 158 )
220 }; 159 };
221 160
@@ -258,7 +197,6 @@ pub fn main_loop(
258 &task_sender, 197 &task_sender,
259 &libdata_sender, 198 &libdata_sender,
260 &connection, 199 &connection,
261 text_document_caps,
262 &mut world_state, 200 &mut world_state,
263 &mut loop_state, 201 &mut loop_state,
264 event, 202 event,
@@ -369,7 +307,6 @@ fn loop_turn(
369 task_sender: &Sender<Task>, 307 task_sender: &Sender<Task>,
370 libdata_sender: &Sender<LibraryData>, 308 libdata_sender: &Sender<LibraryData>,
371 connection: &Connection, 309 connection: &Connection,
372 text_document_caps: Option<&TextDocumentClientCapabilities>,
373 world_state: &mut WorldState, 310 world_state: &mut WorldState,
374 loop_state: &mut LoopState, 311 loop_state: &mut LoopState,
375 event: Event, 312 event: Event,
@@ -423,28 +360,16 @@ fn loop_turn(
423 log::debug!("config update response: '{:?}", resp); 360 log::debug!("config update response: '{:?}", resp);
424 let Response { error, result, .. } = resp; 361 let Response { error, result, .. } = resp;
425 362
426 match ( 363 match (error, result) {
427 error,
428 result.map(|result| serde_json::from_value::<Vec<ServerConfig>>(result)),
429 ) {
430 (Some(err), _) => { 364 (Some(err), _) => {
431 log::error!("failed to fetch the server settings: {:?}", err) 365 log::error!("failed to fetch the server settings: {:?}", err)
432 } 366 }
433 (None, Some(Ok(new_config))) => { 367 (None, Some(configs)) => {
434 let new_config = new_config 368 if let Some(new_config) = configs.get(0) {
435 .first() 369 let mut config = world_state.config.clone();
436 .expect( 370 config.update(&new_config);
437 "the client is expected to always send a non-empty config data", 371 world_state.update_configuration(config);
438 ) 372 }
439 .to_owned();
440 world_state.update_configuration(
441 new_config.lru_capacity,
442 get_config(&new_config, text_document_caps),
443 get_feature_flags(&new_config, connection),
444 );
445 }
446 (None, Some(Err(e))) => {
447 log::error!("failed to parse client config response: {}", e)
448 } 373 }
449 (None, None) => { 374 (None, None) => {
450 log::error!("received empty server settings response from the client") 375 log::error!("received empty server settings response from the client")
@@ -475,8 +400,8 @@ fn loop_turn(
475 }); 400 });
476 } 401 }
477 402
478 let show_progress = !loop_state.workspace_loaded 403 let show_progress =
479 && world_state.feature_flags.get("notifications.workspace-loaded"); 404 !loop_state.workspace_loaded && world_state.config.notifications.workspace_loaded;
480 405
481 if !loop_state.workspace_loaded 406 if !loop_state.workspace_loaded
482 && loop_state.roots_scanned == loop_state.roots_total 407 && loop_state.roots_scanned == loop_state.roots_total
@@ -964,7 +889,7 @@ fn update_file_notifications_on_threadpool(
964 subscriptions: Vec<FileId>, 889 subscriptions: Vec<FileId>,
965) { 890) {
966 log::trace!("updating notifications for {:?}", subscriptions); 891 log::trace!("updating notifications for {:?}", subscriptions);
967 let publish_diagnostics = world.feature_flags.get("lsp.diagnostics"); 892 let publish_diagnostics = world.config.publish_diagnostics;
968 pool.execute(move || { 893 pool.execute(move || {
969 for file_id in subscriptions { 894 for file_id in subscriptions {
970 if publish_diagnostics { 895 if publish_diagnostics {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index d5cb5d137..23e48c089 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -19,8 +19,8 @@ use lsp_types::{
19 TextEdit, WorkspaceEdit, 19 TextEdit, WorkspaceEdit,
20}; 20};
21use ra_ide::{ 21use ra_ide::{
22 Assist, AssistId, CompletionConfig, FileId, FilePosition, FileRange, Query, RangeInfo, 22 Assist, AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
23 Runnable, RunnableKind, SearchScope, 23 SearchScope,
24}; 24};
25use ra_prof::profile; 25use ra_prof::profile;
26use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; 26use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit};
@@ -31,6 +31,7 @@ use stdx::format_to;
31 31
32use crate::{ 32use crate::{
33 cargo_target_spec::CargoTargetSpec, 33 cargo_target_spec::CargoTargetSpec,
34 config::RustfmtConfig,
34 conv::{ 35 conv::{
35 to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith, 36 to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith,
36 TryConvWithToVec, 37 TryConvWithToVec,
@@ -425,15 +426,7 @@ pub fn handle_completion(
425 return Ok(None); 426 return Ok(None);
426 } 427 }
427 428
428 let config = CompletionConfig { 429 let items = match world.analysis().completions(position, &world.config.completion)? {
429 enable_postfix_completions: world.feature_flags.get("completion.enable-postfix"),
430 add_call_parenthesis: world.feature_flags.get("completion.insertion.add-call-parenthesis"),
431 add_call_argument_snippets: world
432 .feature_flags
433 .get("completion.insertion.add-argument-snippets"),
434 };
435
436 let items = match world.analysis().completions(position, &config)? {
437 None => return Ok(None), 430 None => return Ok(None),
438 Some(items) => items, 431 Some(items) => items,
439 }; 432 };
@@ -457,7 +450,7 @@ pub fn handle_folding_range(
457 let ctx = FoldConvCtx { 450 let ctx = FoldConvCtx {
458 text: &text, 451 text: &text,
459 line_index: &line_index, 452 line_index: &line_index,
460 line_folding_only: world.config.line_folding_only, 453 line_folding_only: world.config.client_caps.line_folding_only,
461 }; 454 };
462 let res = Some(folds.into_iter().map_conv_with(&ctx).collect()); 455 let res = Some(folds.into_iter().map_conv_with(&ctx).collect());
463 Ok(res) 456 Ok(res)
@@ -470,7 +463,7 @@ pub fn handle_signature_help(
470 let _p = profile("handle_signature_help"); 463 let _p = profile("handle_signature_help");
471 let position = params.try_conv_with(&world)?; 464 let position = params.try_conv_with(&world)?;
472 if let Some(call_info) = world.analysis().call_info(position)? { 465 if let Some(call_info) = world.analysis().call_info(position)? {
473 let concise = !world.feature_flags.get("call-info.full"); 466 let concise = !world.config.call_info_full;
474 let mut active_parameter = call_info.active_parameter.map(|it| it as i64); 467 let mut active_parameter = call_info.active_parameter.map(|it| it as i64);
475 if concise && call_info.signature.has_self_param { 468 if concise && call_info.signature.has_self_param {
476 active_parameter = active_parameter.map(|it| it.saturating_sub(1)); 469 active_parameter = active_parameter.map(|it| it.saturating_sub(1));
@@ -610,13 +603,24 @@ pub fn handle_formatting(
610 let file_line_index = world.analysis().file_line_index(file_id)?; 603 let file_line_index = world.analysis().file_line_index(file_id)?;
611 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index); 604 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index);
612 605
613 let mut rustfmt = process::Command::new("rustfmt"); 606 let mut rustfmt = match &world.config.rustfmt {
614 rustfmt.args(&world.config.rustfmt_args); 607 RustfmtConfig::Rustfmt { extra_args } => {
615 if let Some(&crate_id) = crate_ids.first() { 608 let mut cmd = process::Command::new("rustfmt");
616 // Assume all crates are in the same edition 609 cmd.args(extra_args);
617 let edition = world.analysis().crate_edition(crate_id)?; 610 if let Some(&crate_id) = crate_ids.first() {
618 rustfmt.args(&["--edition", &edition.to_string()]); 611 // Assume all crates are in the same edition
619 } 612 let edition = world.analysis().crate_edition(crate_id)?;
613 cmd.arg("--edition");
614 cmd.arg(edition.to_string());
615 }
616 cmd
617 }
618 RustfmtConfig::CustomCommand { command, args } => {
619 let mut cmd = process::Command::new(command);
620 cmd.args(args);
621 cmd
622 }
623 };
620 624
621 if let Ok(path) = params.text_document.uri.to_file_path() { 625 if let Ok(path) = params.text_document.uri.to_file_path() {
622 if let Some(parent) = path.parent() { 626 if let Some(parent) = path.parent() {
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index 2db058eb1..5674f42ef 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -13,8 +13,7 @@ use lsp_types::Url;
13use parking_lot::RwLock; 13use parking_lot::RwLock;
14use ra_flycheck::{url_from_path_with_drive_lowercasing, Flycheck, FlycheckConfig}; 14use ra_flycheck::{url_from_path_with_drive_lowercasing, Flycheck, FlycheckConfig};
15use ra_ide::{ 15use ra_ide::{
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsConfig, LibraryData, 16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId,
17 SourceRootId,
18}; 17};
19use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; 18use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace};
20use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; 19use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
@@ -22,8 +21,8 @@ use relative_path::RelativePathBuf;
22use stdx::format_to; 21use stdx::format_to;
23 22
24use crate::{ 23use crate::{
24 config::Config,
25 diagnostics::{CheckFixes, DiagnosticCollection}, 25 diagnostics::{CheckFixes, DiagnosticCollection},
26 feature_flags::FeatureFlags,
27 main_loop::pending_requests::{CompletedRequest, LatestRequests}, 26 main_loop::pending_requests::{CompletedRequest, LatestRequests},
28 vfs_glob::{Glob, RustPackageFilterBuilder}, 27 vfs_glob::{Glob, RustPackageFilterBuilder},
29 LspError, Result, 28 LspError, Result,
@@ -31,9 +30,7 @@ use crate::{
31use ra_db::ExternSourceId; 30use ra_db::ExternSourceId;
32use rustc_hash::{FxHashMap, FxHashSet}; 31use rustc_hash::{FxHashMap, FxHashSet};
33 32
34fn create_flycheck(workspaces: &[ProjectWorkspace], config: &Config) -> Option<Flycheck> { 33fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> {
35 let check_config = config.check.as_ref()?;
36
37 // FIXME: Figure out the multi-workspace situation 34 // FIXME: Figure out the multi-workspace situation
38 workspaces 35 workspaces
39 .iter() 36 .iter()
@@ -43,7 +40,7 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &Config) -> Option<F
43 }) 40 })
44 .map(|cargo| { 41 .map(|cargo| {
45 let cargo_project_root = cargo.workspace_root().to_path_buf(); 42 let cargo_project_root = cargo.workspace_root().to_path_buf();
46 Some(Flycheck::new(check_config.clone(), cargo_project_root)) 43 Some(Flycheck::new(config.clone(), cargo_project_root))
47 }) 44 })
48 .unwrap_or_else(|| { 45 .unwrap_or_else(|| {
49 log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); 46 log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
@@ -51,18 +48,6 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &Config) -> Option<F
51 }) 48 })
52} 49}
53 50
54#[derive(Debug, Clone)]
55pub struct Config {
56 pub publish_decorations: bool,
57 pub supports_location_link: bool,
58 pub line_folding_only: bool,
59 pub inlay_hints: InlayHintsConfig,
60 pub rustfmt_args: Vec<String>,
61 pub check: Option<FlycheckConfig>,
62 pub vscode_lldb: bool,
63 pub proc_macro_srv: Option<String>,
64}
65
66/// `WorldState` is the primary mutable state of the language server 51/// `WorldState` is the primary mutable state of the language server
67/// 52///
68/// The most interesting components are `vfs`, which stores a consistent 53/// The most interesting components are `vfs`, which stores a consistent
@@ -71,7 +56,6 @@ pub struct Config {
71#[derive(Debug)] 56#[derive(Debug)]
72pub struct WorldState { 57pub struct WorldState {
73 pub config: Config, 58 pub config: Config,
74 pub feature_flags: Arc<FeatureFlags>,
75 pub roots: Vec<PathBuf>, 59 pub roots: Vec<PathBuf>,
76 pub workspaces: Arc<Vec<ProjectWorkspace>>, 60 pub workspaces: Arc<Vec<ProjectWorkspace>>,
77 pub analysis_host: AnalysisHost, 61 pub analysis_host: AnalysisHost,
@@ -85,7 +69,6 @@ pub struct WorldState {
85/// An immutable snapshot of the world's state at a point in time. 69/// An immutable snapshot of the world's state at a point in time.
86pub struct WorldSnapshot { 70pub struct WorldSnapshot {
87 pub config: Config, 71 pub config: Config,
88 pub feature_flags: Arc<FeatureFlags>,
89 pub workspaces: Arc<Vec<ProjectWorkspace>>, 72 pub workspaces: Arc<Vec<ProjectWorkspace>>,
90 pub analysis: Analysis, 73 pub analysis: Analysis,
91 pub latest_requests: Arc<RwLock<LatestRequests>>, 74 pub latest_requests: Arc<RwLock<LatestRequests>>,
@@ -101,7 +84,6 @@ impl WorldState {
101 exclude_globs: &[Glob], 84 exclude_globs: &[Glob],
102 watch: Watch, 85 watch: Watch,
103 config: Config, 86 config: Config,
104 feature_flags: FeatureFlags,
105 ) -> WorldState { 87 ) -> WorldState {
106 let mut change = AnalysisChange::new(); 88 let mut change = AnalysisChange::new();
107 89
@@ -203,13 +185,12 @@ impl WorldState {
203 }); 185 });
204 change.set_crate_graph(crate_graph); 186 change.set_crate_graph(crate_graph);
205 187
206 let flycheck = create_flycheck(&workspaces, &config); 188 let flycheck = config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c));
207 189
208 let mut analysis_host = AnalysisHost::new(lru_capacity); 190 let mut analysis_host = AnalysisHost::new(lru_capacity);
209 analysis_host.apply_change(change); 191 analysis_host.apply_change(change);
210 WorldState { 192 WorldState {
211 config: config, 193 config: config,
212 feature_flags: Arc::new(feature_flags),
213 roots: folder_roots, 194 roots: folder_roots,
214 workspaces: Arc::new(workspaces), 195 workspaces: Arc::new(workspaces),
215 analysis_host, 196 analysis_host,
@@ -221,15 +202,13 @@ impl WorldState {
221 } 202 }
222 } 203 }
223 204
224 pub fn update_configuration( 205 pub fn update_configuration(&mut self, config: Config) {
225 &mut self, 206 self.analysis_host.update_lru_capacity(config.lru_capacity);
226 lru_capacity: Option<usize>, 207 if config.check != self.config.check {
227 config: Config, 208 self.flycheck =
228 feature_flags: FeatureFlags, 209 config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it));
229 ) { 210 }
230 self.feature_flags = Arc::new(feature_flags); 211
231 self.analysis_host.update_lru_capacity(lru_capacity);
232 self.flycheck = create_flycheck(&self.workspaces, &config);
233 self.config = config; 212 self.config = config;
234 } 213 }
235 214
@@ -287,7 +266,6 @@ impl WorldState {
287 pub fn snapshot(&self) -> WorldSnapshot { 266 pub fn snapshot(&self) -> WorldSnapshot {
288 WorldSnapshot { 267 WorldSnapshot {
289 config: self.config.clone(), 268 config: self.config.clone(),
290 feature_flags: Arc::clone(&self.feature_flags),
291 workspaces: Arc::clone(&self.workspaces), 269 workspaces: Arc::clone(&self.workspaces),
292 analysis: self.analysis_host.analysis(), 270 analysis: self.analysis_host.analysis(),
293 vfs: Arc::clone(&self.vfs), 271 vfs: Arc::clone(&self.vfs),
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 5af5eaad2..638813311 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -615,7 +615,7 @@ fn main() { message(); }
615"###, 615"###,
616 ) 616 )
617 .with_config(|config| { 617 .with_config(|config| {
618 config.cargo_features.load_out_dirs_from_check = true; 618 config.cargo.load_out_dirs_from_check = true;
619 }) 619 })
620 .server(); 620 .server();
621 server.wait_until_workspace_is_loaded(); 621 server.wait_until_workspace_is_loaded();
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index d8bed6d7f..7eebedff7 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -11,8 +11,7 @@ use lsp_server::{Connection, Message, Notification, Request};
11use lsp_types::{ 11use lsp_types::{
12 notification::{DidOpenTextDocument, Exit}, 12 notification::{DidOpenTextDocument, Exit},
13 request::Shutdown, 13 request::Shutdown,
14 ClientCapabilities, DidOpenTextDocumentParams, GotoCapability, TextDocumentClientCapabilities, 14 DidOpenTextDocumentParams, TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress,
15 TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress,
16}; 15};
17use serde::Serialize; 16use serde::Serialize;
18use serde_json::{to_string_pretty, Value}; 17use serde_json::{to_string_pretty, Value};
@@ -20,14 +19,17 @@ use tempfile::TempDir;
20use test_utils::{find_mismatch, parse_fixture}; 19use test_utils::{find_mismatch, parse_fixture};
21 20
22use req::{ProgressParams, ProgressParamsValue}; 21use req::{ProgressParams, ProgressParamsValue};
23use rust_analyzer::{main_loop, req, ServerConfig}; 22use rust_analyzer::{
23 config::{ClientCapsConfig, Config},
24 main_loop, req,
25};
24 26
25pub struct Project<'a> { 27pub struct Project<'a> {
26 fixture: &'a str, 28 fixture: &'a str,
27 with_sysroot: bool, 29 with_sysroot: bool,
28 tmp_dir: Option<TempDir>, 30 tmp_dir: Option<TempDir>,
29 roots: Vec<PathBuf>, 31 roots: Vec<PathBuf>,
30 config: Option<Box<dyn Fn(&mut ServerConfig)>>, 32 config: Option<Box<dyn Fn(&mut Config)>>,
31} 33}
32 34
33impl<'a> Project<'a> { 35impl<'a> Project<'a> {
@@ -50,7 +52,7 @@ impl<'a> Project<'a> {
50 self 52 self
51 } 53 }
52 54
53 pub fn with_config(mut self, config: impl Fn(&mut ServerConfig) + 'static) -> Project<'a> { 55 pub fn with_config(mut self, config: impl Fn(&mut Config) + 'static) -> Project<'a> {
54 self.config = Some(Box::new(config)); 56 self.config = Some(Box::new(config));
55 self 57 self
56 } 58 }
@@ -78,8 +80,11 @@ impl<'a> Project<'a> {
78 80
79 let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); 81 let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect();
80 82
81 let mut config = 83 let mut config = Config {
82 ServerConfig { with_sysroot: self.with_sysroot, ..ServerConfig::default() }; 84 client_caps: ClientCapsConfig { location_link: true, ..Default::default() },
85 with_sysroot: self.with_sysroot,
86 ..Config::default()
87 };
83 88
84 if let Some(f) = &self.config { 89 if let Some(f) = &self.config {
85 f(&mut config) 90 f(&mut config)
@@ -105,7 +110,7 @@ pub struct Server {
105impl Server { 110impl Server {
106 fn new( 111 fn new(
107 dir: TempDir, 112 dir: TempDir,
108 config: ServerConfig, 113 config: Config,
109 roots: Vec<PathBuf>, 114 roots: Vec<PathBuf>,
110 files: Vec<(PathBuf, String)>, 115 files: Vec<(PathBuf, String)>,
111 ) -> Server { 116 ) -> Server {
@@ -116,26 +121,7 @@ impl Server {
116 121
117 let _thread = jod_thread::Builder::new() 122 let _thread = jod_thread::Builder::new()
118 .name("test server".to_string()) 123 .name("test server".to_string())
119 .spawn(move || { 124 .spawn(move || main_loop(roots, config, connection).unwrap())
120 main_loop(
121 roots,
122 ClientCapabilities {
123 workspace: None,
124 text_document: Some(TextDocumentClientCapabilities {
125 definition: Some(GotoCapability {
126 dynamic_registration: None,
127 link_support: Some(true),
128 }),
129 ..Default::default()
130 }),
131 window: None,
132 experimental: None,
133 },
134 config,
135 connection,
136 )
137 .unwrap()
138 })
139 .expect("failed to spawn a thread"); 125 .expect("failed to spawn a thread");
140 126
141 let res = 127 let res =