aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_flycheck/src/lib.rs2
-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.rs252
-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.rs79
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs18
-rw-r--r--crates/rust-analyzer/src/world.rs32
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs2
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs42
14 files changed, 196 insertions, 374 deletions
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_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 fb7895ce0..3c8f55f1e 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -7,24 +7,36 @@
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;
11
12use lsp_types::TextDocumentClientCapabilities; 10use lsp_types::TextDocumentClientCapabilities;
13use ra_flycheck::FlycheckConfig; 11use ra_flycheck::FlycheckConfig;
14use ra_ide::InlayHintsConfig; 12use ra_ide::{CompletionConfig, InlayHintsConfig};
15use ra_project_model::CargoFeatures; 13use ra_project_model::CargoConfig;
16use serde::{Deserialize, Deserializer}; 14use serde::Deserialize;
17 15
18#[derive(Debug, Clone)] 16#[derive(Debug, Clone)]
19pub struct Config { 17pub struct Config {
18 pub client_caps: ClientCapsConfig,
20 pub publish_decorations: bool, 19 pub publish_decorations: bool,
21 pub supports_location_link: bool, 20 pub publish_diagnostics: bool,
22 pub line_folding_only: bool, 21 pub notifications: NotificationsConfig,
23 pub inlay_hints: InlayHintsConfig, 22 pub inlay_hints: InlayHintsConfig,
23 pub completion: CompletionConfig,
24 pub call_info_full: bool,
24 pub rustfmt: RustfmtConfig, 25 pub rustfmt: RustfmtConfig,
25 pub check: Option<FlycheckConfig>, 26 pub check: Option<FlycheckConfig>,
26 pub vscode_lldb: bool, 27 pub vscode_lldb: bool,
27 pub proc_macro_srv: Option<String>, 28 pub proc_macro_srv: Option<String>,
29 pub lru_capacity: Option<usize>,
30 pub use_client_watching: bool,
31 pub exclude_globs: Vec<String>,
32 pub cargo: CargoConfig,
33 pub with_sysroot: bool,
34}
35
36#[derive(Debug, Clone)]
37pub struct NotificationsConfig {
38 pub workspace_loaded: bool,
39 pub cargo_toml_not_found: bool,
28} 40}
29 41
30#[derive(Debug, Clone)] 42#[derive(Debug, Clone)]
@@ -39,148 +51,120 @@ pub enum RustfmtConfig {
39 }, 51 },
40} 52}
41 53
42impl Default for RustfmtConfig { 54#[derive(Debug, Clone, Default)]
43 fn default() -> Self { 55pub struct ClientCapsConfig {
44 RustfmtConfig::Rustfmt { extra_args: Vec::new() } 56 pub location_link: bool,
45 } 57 pub line_folding_only: bool,
46} 58}
47 59
48pub(crate) fn get_config( 60impl Default for Config {
49 config: &ServerConfig, 61 fn default() -> Self {
50 text_document_caps: Option<&TextDocumentClientCapabilities>, 62 Config {
51) -> Config { 63 publish_decorations: false,
52 Config { 64 publish_diagnostics: true,
53 publish_decorations: config.publish_decorations, 65 notifications: NotificationsConfig {
54 supports_location_link: text_document_caps 66 workspace_loaded: true,
55 .and_then(|it| it.definition) 67 cargo_toml_not_found: true,
56 .and_then(|it| it.link_support) 68 },
57 .unwrap_or(false), 69 client_caps: ClientCapsConfig::default(),
58 line_folding_only: text_document_caps 70 inlay_hints: InlayHintsConfig {
59 .and_then(|it| it.folding_range.as_ref()) 71 type_hints: true,
60 .and_then(|it| it.line_folding_only) 72 parameter_hints: true,
61 .unwrap_or(false), 73 chaining_hints: true,
62 inlay_hints: InlayHintsConfig { 74 max_length: None,
63 type_hints: config.inlay_hints_type, 75 },
64 parameter_hints: config.inlay_hints_parameter, 76 completion: CompletionConfig {
65 chaining_hints: config.inlay_hints_chaining, 77 enable_postfix_completions: true,
66 max_length: config.inlay_hints_max_length, 78 add_call_parenthesis: true,
67 }, 79 add_call_argument_snippets: true,
68 check: if config.cargo_watch_enable { 80 },
69 Some(FlycheckConfig::CargoCommand { 81 call_info_full: true,
70 command: config.cargo_watch_command.clone(), 82 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
71 all_targets: config.cargo_watch_all_targets, 83 check: Some(FlycheckConfig::CargoCommand {
72 extra_args: config.cargo_watch_args.clone(), 84 command: "check".to_string(),
73 }) 85 all_targets: true,
74 } else { 86 extra_args: Vec::new(),
75 None 87 }),
76 }, 88 vscode_lldb: false,
77 rustfmt: RustfmtConfig::Rustfmt { extra_args: config.rustfmt_args.clone() }, 89 proc_macro_srv: None,
78 vscode_lldb: config.vscode_lldb, 90 lru_capacity: None,
79 proc_macro_srv: None, // FIXME: get this from config 91 use_client_watching: false,
92 exclude_globs: Vec::new(),
93 cargo: CargoConfig::default(),
94 with_sysroot: true,
95 }
80 } 96 }
81} 97}
82 98
83/// Client provided initialization options 99impl Config {
84#[derive(Deserialize, Clone, Debug, PartialEq, Eq)] 100 #[rustfmt::skip]
85#[serde(rename_all = "camelCase", default)] 101 pub fn update(&mut self, value: &serde_json::Value) {
86pub struct ServerConfig { 102 log::info!("Config::update({:#})", value);
87 /// Whether the client supports our custom highlighting publishing decorations.
88 /// This is different to the highlightingOn setting, which is whether the user
89 /// wants our custom highlighting to be used.
90 ///
91 /// Defaults to `false`
92 #[serde(deserialize_with = "nullable_bool_false")]
93 pub publish_decorations: bool,
94 103
95 pub exclude_globs: Vec<String>, 104 let client_caps = self.client_caps.clone();
96 #[serde(deserialize_with = "nullable_bool_false")] 105 *self = Default::default();
97 pub use_client_watching: bool, 106 self.client_caps = client_caps;
98 107
99 pub lru_capacity: Option<usize>, 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);
100 112
101 #[serde(deserialize_with = "nullable_bool_true")] 113 set(value, "/inlayHintsType", &mut self.inlay_hints.type_hints);
102 pub inlay_hints_type: bool, 114 set(value, "/inlayHintsParameter", &mut self.inlay_hints.parameter_hints);
103 #[serde(deserialize_with = "nullable_bool_true")] 115 set(value, "/inlayHintsChaining", &mut self.inlay_hints.chaining_hints);
104 pub inlay_hints_parameter: bool, 116 set(value, "/inlayHintsMaxLength", &mut self.inlay_hints.max_length);
105 #[serde(deserialize_with = "nullable_bool_true")]
106 pub inlay_hints_chaining: bool,
107 pub inlay_hints_max_length: Option<usize>,
108
109 pub cargo_watch_enable: bool,
110 pub cargo_watch_args: Vec<String>,
111 pub cargo_watch_command: String,
112 pub cargo_watch_all_targets: bool,
113
114 /// For internal usage to make integrated tests faster.
115 #[serde(deserialize_with = "nullable_bool_true")]
116 pub with_sysroot: bool,
117 117
118 /// Fine grained feature flags to disable specific features. 118 if let Some(false) = get(value, "cargo_watch_enable") {
119 pub feature_flags: FxHashMap<String, bool>, 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 }
120 133
121 pub rustfmt_args: Vec<String>, 134 set(value, "/cargoFeatures/noDefaultFeatures", &mut self.cargo.no_default_features);
135 set(value, "/cargoFeatures/allFeatures", &mut self.cargo.all_features);
136 set(value, "/cargoFeatures/features", &mut self.cargo.features);
137 set(value, "/cargoFeatures/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
122 138
123 /// Cargo feature configurations. 139 set(value, "/vscodeLldb", &mut self.vscode_lldb);
124 pub cargo_features: CargoFeatures,
125 140
126 /// Enabled if the vscode_lldb extension is available. 141 set(value, "/featureFlags/lsp.diagnostics", &mut self.publish_diagnostics);
127 pub vscode_lldb: bool, 142 set(value, "/featureFlags/notifications.workspace-loaded", &mut self.notifications.workspace_loaded);
128} 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);
129 148
130impl Default for ServerConfig { 149 log::info!("Config::update() = {:#?}", self);
131 fn default() -> ServerConfig {
132 ServerConfig {
133 publish_decorations: false,
134 exclude_globs: Vec::new(),
135 use_client_watching: false,
136 lru_capacity: None,
137 inlay_hints_type: true,
138 inlay_hints_parameter: true,
139 inlay_hints_chaining: true,
140 inlay_hints_max_length: None,
141 cargo_watch_enable: true,
142 cargo_watch_args: Vec::new(),
143 cargo_watch_command: "check".to_string(),
144 cargo_watch_all_targets: true,
145 with_sysroot: true,
146 feature_flags: FxHashMap::default(),
147 cargo_features: Default::default(),
148 rustfmt_args: Vec::new(),
149 vscode_lldb: false,
150 }
151 }
152}
153 150
154/// Deserializes a null value to a bool false by default 151 fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
155fn nullable_bool_false<'de, D>(deserializer: D) -> Result<bool, D::Error> 152 value.pointer(pointer).and_then(|it| T::deserialize(it).ok())
156where 153 }
157 D: Deserializer<'de>,
158{
159 let opt = Option::deserialize(deserializer)?;
160 Ok(opt.unwrap_or(false))
161}
162 154
163/// Deserializes a null value to a bool true by default 155 fn set<'a, T: Deserialize<'a> + std::fmt::Debug>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) {
164fn nullable_bool_true<'de, D>(deserializer: D) -> Result<bool, D::Error> 156 if let Some(new_value) = get(value, pointer) {
165where 157 *slot = new_value
166 D: Deserializer<'de>, 158 }
167{ 159 }
168 let opt = Option::deserialize(deserializer)?; 160 }
169 Ok(opt.unwrap_or(true))
170}
171 161
172#[cfg(test)] 162 pub fn update_caps(&mut self, caps: &TextDocumentClientCapabilities) {
173mod test { 163 if let Some(value) = caps.definition.as_ref().and_then(|it| it.link_support) {
174 use super::*; 164 self.client_caps.location_link = value;
175 165 }
176 #[test] 166 if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) {
177 fn deserialize_init_options_defaults() { 167 self.client_caps.line_folding_only = value
178 // check that null == default for both fields 168 }
179 let default = ServerConfig::default();
180 assert_eq!(default, serde_json::from_str(r#"{}"#).unwrap());
181 assert_eq!(
182 default,
183 serde_json::from_str(r#"{"publishDecorations":null, "lruCapacity":null}"#).unwrap()
184 );
185 } 169 }
186} 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 de40e2ac2..45ae0ad9d 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -17,9 +17,8 @@ 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}; 23use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask};
25use ra_ide::{Canceled, FileId, LibraryData, SourceRootId}; 24use ra_ide::{Canceled, FileId, LibraryData, SourceRootId};
@@ -31,16 +30,15 @@ use serde::{de::DeserializeOwned, Serialize};
31use threadpool::ThreadPool; 30use threadpool::ThreadPool;
32 31
33use crate::{ 32use crate::{
34 config::get_config, 33 config::Config,
35 diagnostics::DiagnosticTask, 34 diagnostics::DiagnosticTask,
36 feature_flags::FeatureFlags,
37 main_loop::{ 35 main_loop::{
38 pending_requests::{PendingRequest, PendingRequests}, 36 pending_requests::{PendingRequest, PendingRequests},
39 subscriptions::Subscriptions, 37 subscriptions::Subscriptions,
40 }, 38 },
41 req, 39 req,
42 world::{WorldSnapshot, WorldState}, 40 world::{WorldSnapshot, WorldState},
43 Result, ServerConfig, 41 Result,
44}; 42};
45use req::ConfigurationParams; 43use req::ConfigurationParams;
46 44
@@ -66,29 +64,8 @@ impl fmt::Display for LspError {
66 64
67impl Error for LspError {} 65impl Error for LspError {}
68 66
69fn get_feature_flags(config: &ServerConfig, connection: &Connection) -> FeatureFlags { 67pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> {
70 let mut ff = FeatureFlags::default(); 68 log::info!("initial config: {:#?}", config);
71 for (flag, &value) in &config.feature_flags {
72 if ff.set(flag.as_str(), value).is_err() {
73 log::error!("unknown feature flag: {:?}", flag);
74 show_message(
75 req::MessageType::Error,
76 format!("unknown feature flag: {:?}", flag),
77 &connection.sender,
78 );
79 }
80 }
81 log::info!("feature_flags: {:#?}", ff);
82 ff
83}
84
85pub fn main_loop(
86 ws_roots: Vec<PathBuf>,
87 client_caps: ClientCapabilities,
88 config: ServerConfig,
89 connection: Connection,
90) -> Result<()> {
91 log::info!("server_config: {:#?}", config);
92 69
93 // Windows scheduler implements priority boosts: if thread waits for an 70 // Windows scheduler implements priority boosts: if thread waits for an
94 // 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
@@ -109,11 +86,8 @@ pub fn main_loop(
109 SetThreadPriority(thread, thread_priority_above_normal); 86 SetThreadPriority(thread, thread_priority_above_normal);
110 } 87 }
111 88
112 let text_document_caps = client_caps.text_document.as_ref();
113 let mut loop_state = LoopState::default(); 89 let mut loop_state = LoopState::default();
114 let mut world_state = { 90 let mut world_state = {
115 let feature_flags = get_feature_flags(&config, &connection);
116
117 // FIXME: support dynamic workspace loading. 91 // FIXME: support dynamic workspace loading.
118 let workspaces = { 92 let workspaces = {
119 let mut loaded_workspaces = Vec::new(); 93 let mut loaded_workspaces = Vec::new();
@@ -121,7 +95,7 @@ pub fn main_loop(
121 let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( 95 let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot(
122 ws_root.as_path(), 96 ws_root.as_path(),
123 config.with_sysroot, 97 config.with_sysroot,
124 &config.cargo_features, 98 &config.cargo,
125 ); 99 );
126 match workspace { 100 match workspace {
127 Ok(workspace) => loaded_workspaces.push(workspace), 101 Ok(workspace) => loaded_workspaces.push(workspace),
@@ -131,7 +105,7 @@ pub fn main_loop(
131 if let Some(ra_project_model::CargoTomlNotFoundError { .. }) = 105 if let Some(ra_project_model::CargoTomlNotFoundError { .. }) =
132 e.downcast_ref() 106 e.downcast_ref()
133 { 107 {
134 if !feature_flags.get("notifications.cargo-toml-not-found") { 108 if !config.notifications.cargo_toml_not_found {
135 continue; 109 continue;
136 } 110 }
137 } 111 }
@@ -180,8 +154,7 @@ pub fn main_loop(
180 config.lru_capacity, 154 config.lru_capacity,
181 &globs, 155 &globs,
182 Watch(!config.use_client_watching), 156 Watch(!config.use_client_watching),
183 get_config(&config, text_document_caps), 157 config,
184 feature_flags,
185 ) 158 )
186 }; 159 };
187 160
@@ -224,7 +197,6 @@ pub fn main_loop(
224 &task_sender, 197 &task_sender,
225 &libdata_sender, 198 &libdata_sender,
226 &connection, 199 &connection,
227 text_document_caps,
228 &mut world_state, 200 &mut world_state,
229 &mut loop_state, 201 &mut loop_state,
230 event, 202 event,
@@ -335,7 +307,6 @@ fn loop_turn(
335 task_sender: &Sender<Task>, 307 task_sender: &Sender<Task>,
336 libdata_sender: &Sender<LibraryData>, 308 libdata_sender: &Sender<LibraryData>,
337 connection: &Connection, 309 connection: &Connection,
338 text_document_caps: Option<&TextDocumentClientCapabilities>,
339 world_state: &mut WorldState, 310 world_state: &mut WorldState,
340 loop_state: &mut LoopState, 311 loop_state: &mut LoopState,
341 event: Event, 312 event: Event,
@@ -389,28 +360,16 @@ fn loop_turn(
389 log::debug!("config update response: '{:?}", resp); 360 log::debug!("config update response: '{:?}", resp);
390 let Response { error, result, .. } = resp; 361 let Response { error, result, .. } = resp;
391 362
392 match ( 363 match (error, result) {
393 error,
394 result.map(|result| serde_json::from_value::<Vec<ServerConfig>>(result)),
395 ) {
396 (Some(err), _) => { 364 (Some(err), _) => {
397 log::error!("failed to fetch the server settings: {:?}", err) 365 log::error!("failed to fetch the server settings: {:?}", err)
398 } 366 }
399 (None, Some(Ok(new_config))) => { 367 (None, Some(configs)) => {
400 let new_config = new_config 368 if let Some(new_config) = configs.get(0) {
401 .first() 369 let mut config = world_state.config.clone();
402 .expect( 370 config.update(&new_config);
403 "the client is expected to always send a non-empty config data", 371 world_state.update_configuration(config);
404 ) 372 }
405 .to_owned();
406 world_state.update_configuration(
407 new_config.lru_capacity,
408 get_config(&new_config, text_document_caps),
409 get_feature_flags(&new_config, connection),
410 );
411 }
412 (None, Some(Err(e))) => {
413 log::error!("failed to parse client config response: {}", e)
414 } 373 }
415 (None, None) => { 374 (None, None) => {
416 log::error!("received empty server settings response from the client") 375 log::error!("received empty server settings response from the client")
@@ -441,8 +400,8 @@ fn loop_turn(
441 }); 400 });
442 } 401 }
443 402
444 let show_progress = !loop_state.workspace_loaded 403 let show_progress =
445 && world_state.feature_flags.get("notifications.workspace-loaded"); 404 !loop_state.workspace_loaded && world_state.config.notifications.workspace_loaded;
446 405
447 if !loop_state.workspace_loaded 406 if !loop_state.workspace_loaded
448 && loop_state.roots_scanned == loop_state.roots_total 407 && loop_state.roots_scanned == loop_state.roots_total
@@ -930,7 +889,7 @@ fn update_file_notifications_on_threadpool(
930 subscriptions: Vec<FileId>, 889 subscriptions: Vec<FileId>,
931) { 890) {
932 log::trace!("updating notifications for {:?}", subscriptions); 891 log::trace!("updating notifications for {:?}", subscriptions);
933 let publish_diagnostics = world.feature_flags.get("lsp.diagnostics"); 892 let publish_diagnostics = world.config.publish_diagnostics;
934 pool.execute(move || { 893 pool.execute(move || {
935 for file_id in subscriptions { 894 for file_id in subscriptions {
936 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 bb99b38a8..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};
@@ -426,15 +426,7 @@ pub fn handle_completion(
426 return Ok(None); 426 return Ok(None);
427 } 427 }
428 428
429 let config = CompletionConfig { 429 let items = match world.analysis().completions(position, &world.config.completion)? {
430 enable_postfix_completions: world.feature_flags.get("completion.enable-postfix"),
431 add_call_parenthesis: world.feature_flags.get("completion.insertion.add-call-parenthesis"),
432 add_call_argument_snippets: world
433 .feature_flags
434 .get("completion.insertion.add-argument-snippets"),
435 };
436
437 let items = match world.analysis().completions(position, &config)? {
438 None => return Ok(None), 430 None => return Ok(None),
439 Some(items) => items, 431 Some(items) => items,
440 }; 432 };
@@ -458,7 +450,7 @@ pub fn handle_folding_range(
458 let ctx = FoldConvCtx { 450 let ctx = FoldConvCtx {
459 text: &text, 451 text: &text,
460 line_index: &line_index, 452 line_index: &line_index,
461 line_folding_only: world.config.line_folding_only, 453 line_folding_only: world.config.client_caps.line_folding_only,
462 }; 454 };
463 let res = Some(folds.into_iter().map_conv_with(&ctx).collect()); 455 let res = Some(folds.into_iter().map_conv_with(&ctx).collect());
464 Ok(res) 456 Ok(res)
@@ -471,7 +463,7 @@ pub fn handle_signature_help(
471 let _p = profile("handle_signature_help"); 463 let _p = profile("handle_signature_help");
472 let position = params.try_conv_with(&world)?; 464 let position = params.try_conv_with(&world)?;
473 if let Some(call_info) = world.analysis().call_info(position)? { 465 if let Some(call_info) = world.analysis().call_info(position)? {
474 let concise = !world.feature_flags.get("call-info.full"); 466 let concise = !world.config.call_info_full;
475 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);
476 if concise && call_info.signature.has_self_param { 468 if concise && call_info.signature.has_self_param {
477 active_parameter = active_parameter.map(|it| it.saturating_sub(1)); 469 active_parameter = active_parameter.map(|it| it.saturating_sub(1));
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index 124de4d8e..5674f42ef 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -11,7 +11,7 @@ use std::{
11use crossbeam_channel::{unbounded, Receiver}; 11use crossbeam_channel::{unbounded, Receiver};
12use lsp_types::Url; 12use lsp_types::Url;
13use parking_lot::RwLock; 13use parking_lot::RwLock;
14use ra_flycheck::{url_from_path_with_drive_lowercasing, Flycheck}; 14use ra_flycheck::{url_from_path_with_drive_lowercasing, Flycheck, FlycheckConfig};
15use ra_ide::{ 15use ra_ide::{
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId, 16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId,
17}; 17};
@@ -23,7 +23,6 @@ use stdx::format_to;
23use crate::{ 23use crate::{
24 config::Config, 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");
@@ -59,7 +56,6 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &Config) -> Option<F
59#[derive(Debug)] 56#[derive(Debug)]
60pub struct WorldState { 57pub struct WorldState {
61 pub config: Config, 58 pub config: Config,
62 pub feature_flags: Arc<FeatureFlags>,
63 pub roots: Vec<PathBuf>, 59 pub roots: Vec<PathBuf>,
64 pub workspaces: Arc<Vec<ProjectWorkspace>>, 60 pub workspaces: Arc<Vec<ProjectWorkspace>>,
65 pub analysis_host: AnalysisHost, 61 pub analysis_host: AnalysisHost,
@@ -73,7 +69,6 @@ pub struct WorldState {
73/// 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.
74pub struct WorldSnapshot { 70pub struct WorldSnapshot {
75 pub config: Config, 71 pub config: Config,
76 pub feature_flags: Arc<FeatureFlags>,
77 pub workspaces: Arc<Vec<ProjectWorkspace>>, 72 pub workspaces: Arc<Vec<ProjectWorkspace>>,
78 pub analysis: Analysis, 73 pub analysis: Analysis,
79 pub latest_requests: Arc<RwLock<LatestRequests>>, 74 pub latest_requests: Arc<RwLock<LatestRequests>>,
@@ -89,7 +84,6 @@ impl WorldState {
89 exclude_globs: &[Glob], 84 exclude_globs: &[Glob],
90 watch: Watch, 85 watch: Watch,
91 config: Config, 86 config: Config,
92 feature_flags: FeatureFlags,
93 ) -> WorldState { 87 ) -> WorldState {
94 let mut change = AnalysisChange::new(); 88 let mut change = AnalysisChange::new();
95 89
@@ -191,13 +185,12 @@ impl WorldState {
191 }); 185 });
192 change.set_crate_graph(crate_graph); 186 change.set_crate_graph(crate_graph);
193 187
194 let flycheck = create_flycheck(&workspaces, &config); 188 let flycheck = config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c));
195 189
196 let mut analysis_host = AnalysisHost::new(lru_capacity); 190 let mut analysis_host = AnalysisHost::new(lru_capacity);
197 analysis_host.apply_change(change); 191 analysis_host.apply_change(change);
198 WorldState { 192 WorldState {
199 config: config, 193 config: config,
200 feature_flags: Arc::new(feature_flags),
201 roots: folder_roots, 194 roots: folder_roots,
202 workspaces: Arc::new(workspaces), 195 workspaces: Arc::new(workspaces),
203 analysis_host, 196 analysis_host,
@@ -209,15 +202,13 @@ impl WorldState {
209 } 202 }
210 } 203 }
211 204
212 pub fn update_configuration( 205 pub fn update_configuration(&mut self, config: Config) {
213 &mut self, 206 self.analysis_host.update_lru_capacity(config.lru_capacity);
214 lru_capacity: Option<usize>, 207 if config.check != self.config.check {
215 config: Config, 208 self.flycheck =
216 feature_flags: FeatureFlags, 209 config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it));
217 ) { 210 }
218 self.feature_flags = Arc::new(feature_flags); 211
219 self.analysis_host.update_lru_capacity(lru_capacity);
220 self.flycheck = create_flycheck(&self.workspaces, &config);
221 self.config = config; 212 self.config = config;
222 } 213 }
223 214
@@ -275,7 +266,6 @@ impl WorldState {
275 pub fn snapshot(&self) -> WorldSnapshot { 266 pub fn snapshot(&self) -> WorldSnapshot {
276 WorldSnapshot { 267 WorldSnapshot {
277 config: self.config.clone(), 268 config: self.config.clone(),
278 feature_flags: Arc::clone(&self.feature_flags),
279 workspaces: Arc::clone(&self.workspaces), 269 workspaces: Arc::clone(&self.workspaces),
280 analysis: self.analysis_host.analysis(), 270 analysis: self.analysis_host.analysis(),
281 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 =