diff options
-rw-r--r-- | crates/rust-analyzer/src/bin/main.rs | 4 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 293 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 6 |
3 files changed, 169 insertions, 134 deletions
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 047772d0c..65f1a6d15 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -120,8 +120,8 @@ fn run_server() -> Result<()> { | |||
120 | }; | 120 | }; |
121 | 121 | ||
122 | let mut config = Config::new(root_path); | 122 | let mut config = Config::new(root_path); |
123 | if let Some(value) = &initialize_params.initialization_options { | 123 | if let Some(json) = initialize_params.initialization_options { |
124 | config.update(value); | 124 | config.update(json); |
125 | } | 125 | } |
126 | config.update_caps(&initialize_params.capabilities); | 126 | config.update_caps(&initialize_params.capabilities); |
127 | 127 | ||
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 9e7de0243..9dd81b4fc 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -111,14 +111,8 @@ pub struct NotificationsConfig { | |||
111 | 111 | ||
112 | #[derive(Debug, Clone)] | 112 | #[derive(Debug, Clone)] |
113 | pub enum RustfmtConfig { | 113 | pub enum RustfmtConfig { |
114 | Rustfmt { | 114 | Rustfmt { extra_args: Vec<String> }, |
115 | extra_args: Vec<String>, | 115 | CustomCommand { command: String, args: Vec<String> }, |
116 | }, | ||
117 | #[allow(unused)] | ||
118 | CustomCommand { | ||
119 | command: String, | ||
120 | args: Vec<String>, | ||
121 | }, | ||
122 | } | 116 | } |
123 | 117 | ||
124 | #[derive(Debug, Clone, Default)] | 118 | #[derive(Debug, Clone, Default)] |
@@ -178,146 +172,110 @@ impl Config { | |||
178 | } | 172 | } |
179 | } | 173 | } |
180 | 174 | ||
181 | #[rustfmt::skip] | 175 | pub fn update(&mut self, json: serde_json::Value) { |
182 | pub fn update(&mut self, value: &serde_json::Value) { | 176 | log::info!("Config::update({:#})", json); |
183 | log::info!("Config::update({:#})", value); | 177 | let data = ConfigData::from_json(json); |
184 | 178 | ||
185 | let client_caps = self.client_caps.clone(); | 179 | self.with_sysroot = data.withSysroot; |
186 | let linked_projects = self.linked_projects.clone(); | 180 | self.publish_diagnostics = data.diagnostics_enable; |
187 | *self = Config::new(self.root_path.clone()); | 181 | self.diagnostics = DiagnosticsConfig { |
188 | self.client_caps = client_caps; | 182 | warnings_as_info: data.diagnostics_warningsAsInfo, |
189 | self.linked_projects = linked_projects; | 183 | warnings_as_hint: data.diagnostics_warningsAsHint, |
190 | 184 | }; | |
191 | set(value, "/withSysroot", &mut self.with_sysroot); | 185 | self.lru_capacity = data.lruCapacity; |
192 | set(value, "/diagnostics/enable", &mut self.publish_diagnostics); | 186 | self.files.watcher = match data.files_watcher.as_str() { |
193 | set(value, "/diagnostics/warningsAsInfo", &mut self.diagnostics.warnings_as_info); | 187 | "notify" => FilesWatcher::Notify, |
194 | set(value, "/diagnostics/warningsAsHint", &mut self.diagnostics.warnings_as_hint); | 188 | "client" | _ => FilesWatcher::Client, |
195 | set(value, "/lruCapacity", &mut self.lru_capacity); | 189 | }; |
196 | self.files.watcher = match get(value, "/files/watcher") { | 190 | self.notifications = |
197 | Some("client") => FilesWatcher::Client, | 191 | NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound }; |
198 | Some("notify") | _ => FilesWatcher::Notify | 192 | self.cargo = CargoConfig { |
193 | no_default_features: data.cargo_noDefaultFeatures, | ||
194 | all_features: data.cargo_allFeatures, | ||
195 | features: data.cargo_features.clone(), | ||
196 | load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck, | ||
197 | target: data.cargo_target, | ||
199 | }; | 198 | }; |
200 | set(value, "/notifications/cargoTomlNotFound", &mut self.notifications.cargo_toml_not_found); | ||
201 | |||
202 | set(value, "/cargo/noDefaultFeatures", &mut self.cargo.no_default_features); | ||
203 | set(value, "/cargo/allFeatures", &mut self.cargo.all_features); | ||
204 | set(value, "/cargo/features", &mut self.cargo.features); | ||
205 | set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check); | ||
206 | set(value, "/cargo/target", &mut self.cargo.target); | ||
207 | |||
208 | match get(value, "/procMacro/enable") { | ||
209 | Some(true) => { | ||
210 | if let Ok(path) = std::env::current_exe() { | ||
211 | self.proc_macro_srv = Some((path, vec!["proc-macro".into()])); | ||
212 | } | ||
213 | } | ||
214 | _ => self.proc_macro_srv = None, | ||
215 | } | ||
216 | 199 | ||
217 | match get::<Vec<String>>(value, "/rustfmt/overrideCommand") { | 200 | self.proc_macro_srv = if data.procMacro_enable { |
201 | std::env::current_exe().ok().map(|path| (path, vec!["proc-macro".into()])) | ||
202 | } else { | ||
203 | None | ||
204 | }; | ||
205 | |||
206 | self.rustfmt = match data.rustfmt_overrideCommand { | ||
218 | Some(mut args) if !args.is_empty() => { | 207 | Some(mut args) if !args.is_empty() => { |
219 | let command = args.remove(0); | 208 | let command = args.remove(0); |
220 | self.rustfmt = RustfmtConfig::CustomCommand { | 209 | RustfmtConfig::CustomCommand { command, args } |
221 | command, | ||
222 | args, | ||
223 | } | ||
224 | } | ||
225 | _ => { | ||
226 | if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt { | ||
227 | set(value, "/rustfmt/extraArgs", extra_args); | ||
228 | } | ||
229 | } | 210 | } |
211 | Some(_) | None => RustfmtConfig::Rustfmt { extra_args: data.rustfmt_extraArgs }, | ||
230 | }; | 212 | }; |
231 | 213 | ||
232 | if let Some(false) = get(value, "/checkOnSave/enable") { | 214 | self.flycheck = if data.checkOnSave_enable { |
233 | // check is disabled | 215 | let flycheck_config = match data.checkOnSave_overrideCommand { |
234 | self.flycheck = None; | ||
235 | } else { | ||
236 | // check is enabled | ||
237 | match get::<Vec<String>>(value, "/checkOnSave/overrideCommand") { | ||
238 | // first see if the user has completely overridden the command | ||
239 | Some(mut args) if !args.is_empty() => { | 216 | Some(mut args) if !args.is_empty() => { |
240 | let command = args.remove(0); | 217 | let command = args.remove(0); |
241 | self.flycheck = Some(FlycheckConfig::CustomCommand { | 218 | FlycheckConfig::CustomCommand { command, args } |
242 | command, | ||
243 | args, | ||
244 | }); | ||
245 | } | ||
246 | // otherwise configure command customizations | ||
247 | _ => { | ||
248 | if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features }) | ||
249 | = &mut self.flycheck | ||
250 | { | ||
251 | set(value, "/checkOnSave/extraArgs", extra_args); | ||
252 | set(value, "/checkOnSave/command", command); | ||
253 | set(value, "/checkOnSave/allTargets", all_targets); | ||
254 | *all_features = get(value, "/checkOnSave/allFeatures").unwrap_or(self.cargo.all_features); | ||
255 | *features = get(value, "/checkOnSave/features").unwrap_or(self.cargo.features.clone()); | ||
256 | } | ||
257 | } | 219 | } |
220 | Some(_) | None => FlycheckConfig::CargoCommand { | ||
221 | command: data.checkOnSave_command, | ||
222 | all_targets: data.checkOnSave_allTargets, | ||
223 | all_features: data.checkOnSave_allFeatures.unwrap_or(data.cargo_allFeatures), | ||
224 | features: data.checkOnSave_features.unwrap_or(data.cargo_features), | ||
225 | extra_args: data.checkOnSave_extraArgs, | ||
226 | }, | ||
258 | }; | 227 | }; |
259 | } | 228 | Some(flycheck_config) |
260 | |||
261 | set(value, "/inlayHints/typeHints", &mut self.inlay_hints.type_hints); | ||
262 | set(value, "/inlayHints/parameterHints", &mut self.inlay_hints.parameter_hints); | ||
263 | set(value, "/inlayHints/chainingHints", &mut self.inlay_hints.chaining_hints); | ||
264 | set(value, "/inlayHints/maxLength", &mut self.inlay_hints.max_length); | ||
265 | set(value, "/completion/postfix/enable", &mut self.completion.enable_postfix_completions); | ||
266 | set(value, "/completion/addCallParenthesis", &mut self.completion.add_call_parenthesis); | ||
267 | set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets); | ||
268 | set(value, "/callInfo/full", &mut self.call_info_full); | ||
269 | |||
270 | let mut lens_enabled = true; | ||
271 | set(value, "/lens/enable", &mut lens_enabled); | ||
272 | if lens_enabled { | ||
273 | set(value, "/lens/run", &mut self.lens.run); | ||
274 | set(value, "/lens/debug", &mut self.lens.debug); | ||
275 | set(value, "/lens/implementations", &mut self.lens.implementations); | ||
276 | } else { | 229 | } else { |
277 | self.lens = LensConfig::NO_LENS; | 230 | None |
278 | } | 231 | }; |
279 | 232 | ||
280 | if let Some(linked_projects) = get::<Vec<ManifestOrProjectJson>>(value, "/linkedProjects") { | 233 | self.inlay_hints = InlayHintsConfig { |
281 | if !linked_projects.is_empty() { | 234 | type_hints: data.inlayHints_typeHints, |
282 | self.linked_projects.clear(); | 235 | parameter_hints: data.inlayHints_parameterHints, |
283 | for linked_project in linked_projects { | 236 | chaining_hints: data.inlayHints_chainingHints, |
284 | let linked_project = match linked_project { | 237 | max_length: data.inlayHints_maxLength, |
285 | ManifestOrProjectJson::Manifest(it) => { | 238 | }; |
286 | let path = self.root_path.join(it); | ||
287 | match ProjectManifest::from_manifest_file(path) { | ||
288 | Ok(it) => it.into(), | ||
289 | Err(_) => continue, | ||
290 | } | ||
291 | } | ||
292 | ManifestOrProjectJson::ProjectJson(it) => ProjectJson::new(&self.root_path, it).into(), | ||
293 | }; | ||
294 | self.linked_projects.push(linked_project); | ||
295 | } | ||
296 | } | ||
297 | } | ||
298 | 239 | ||
299 | let mut use_hover_actions = false; | 240 | self.completion.enable_postfix_completions = data.completion_postfix_enable; |
300 | set(value, "/hoverActions/enable", &mut use_hover_actions); | 241 | self.completion.add_call_parenthesis = data.completion_addCallParenthesis; |
301 | if use_hover_actions { | 242 | self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets; |
302 | set(value, "/hoverActions/implementations", &mut self.hover.implementations); | ||
303 | set(value, "/hoverActions/run", &mut self.hover.run); | ||
304 | set(value, "/hoverActions/debug", &mut self.hover.debug); | ||
305 | set(value, "/hoverActions/gotoTypeDef", &mut self.hover.goto_type_def); | ||
306 | } else { | ||
307 | self.hover = HoverConfig::NO_ACTIONS; | ||
308 | } | ||
309 | 243 | ||
310 | log::info!("Config::update() = {:#?}", self); | 244 | self.call_info_full = data.callInfo_full; |
311 | 245 | ||
312 | fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { | 246 | self.lens = LensConfig { |
313 | value.pointer(pointer).and_then(|it| T::deserialize(it).ok()) | 247 | run: data.lens_enable && data.lens_run, |
314 | } | 248 | debug: data.lens_enable && data.lens_debug, |
249 | implementations: data.lens_enable && data.lens_implementations, | ||
250 | }; | ||
315 | 251 | ||
316 | fn set<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) { | 252 | if !data.linkedProjects.is_empty() { |
317 | if let Some(new_value) = get(value, pointer) { | 253 | self.linked_projects.clear(); |
318 | *slot = new_value | 254 | for linked_project in data.linkedProjects { |
255 | let linked_project = match linked_project { | ||
256 | ManifestOrProjectJson::Manifest(it) => { | ||
257 | let path = self.root_path.join(it); | ||
258 | match ProjectManifest::from_manifest_file(path) { | ||
259 | Ok(it) => it.into(), | ||
260 | Err(_) => continue, | ||
261 | } | ||
262 | } | ||
263 | ManifestOrProjectJson::ProjectJson(it) => { | ||
264 | ProjectJson::new(&self.root_path, it).into() | ||
265 | } | ||
266 | }; | ||
267 | self.linked_projects.push(linked_project); | ||
319 | } | 268 | } |
320 | } | 269 | } |
270 | |||
271 | self.hover = HoverConfig { | ||
272 | implementations: data.hoverActions_enable && data.hoverActions_implementations, | ||
273 | run: data.hoverActions_enable && data.hoverActions_run, | ||
274 | debug: data.hoverActions_enable && data.hoverActions_debug, | ||
275 | goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef, | ||
276 | }; | ||
277 | |||
278 | log::info!("Config::update() = {:#?}", self); | ||
321 | } | 279 | } |
322 | 280 | ||
323 | pub fn update_caps(&mut self, caps: &ClientCapabilities) { | 281 | pub fn update_caps(&mut self, caps: &ClientCapabilities) { |
@@ -380,3 +338,80 @@ enum ManifestOrProjectJson { | |||
380 | Manifest(PathBuf), | 338 | Manifest(PathBuf), |
381 | ProjectJson(ProjectJsonData), | 339 | ProjectJson(ProjectJsonData), |
382 | } | 340 | } |
341 | |||
342 | macro_rules! config_data { | ||
343 | (struct $name:ident { $($field:ident: $ty:ty = $default:expr,)*}) => { | ||
344 | #[allow(non_snake_case)] | ||
345 | struct $name { $($field: $ty,)* } | ||
346 | impl $name { | ||
347 | fn from_json(mut json: serde_json::Value) -> $name { | ||
348 | $name {$( | ||
349 | $field: { | ||
350 | let pointer = stringify!($field).replace('_', "/"); | ||
351 | let pointer = format!("/{}", pointer); | ||
352 | json.pointer_mut(&pointer) | ||
353 | .and_then(|it| serde_json::from_value(it.take()).ok()) | ||
354 | .unwrap_or($default) | ||
355 | }, | ||
356 | )*} | ||
357 | } | ||
358 | } | ||
359 | |||
360 | }; | ||
361 | } | ||
362 | |||
363 | config_data! { | ||
364 | struct ConfigData { | ||
365 | callInfo_full: bool = true, | ||
366 | |||
367 | cargo_allFeatures: bool = false, | ||
368 | cargo_features: Vec<String> = Vec::new(), | ||
369 | cargo_loadOutDirsFromCheck: bool = false, | ||
370 | cargo_noDefaultFeatures: bool = false, | ||
371 | cargo_target: Option<String> = None, | ||
372 | |||
373 | checkOnSave_allFeatures: Option<bool> = None, | ||
374 | checkOnSave_allTargets: bool = true, | ||
375 | checkOnSave_command: String = "check".into(), | ||
376 | checkOnSave_enable: bool = false, | ||
377 | checkOnSave_extraArgs: Vec<String> = Vec::new(), | ||
378 | checkOnSave_features: Option<Vec<String>> = None, | ||
379 | checkOnSave_overrideCommand: Option<Vec<String>> = None, | ||
380 | |||
381 | completion_addCallArgumentSnippets: bool = true, | ||
382 | completion_addCallParenthesis: bool = true, | ||
383 | completion_postfix_enable: bool = true, | ||
384 | |||
385 | diagnostics_enable: bool = true, | ||
386 | diagnostics_warningsAsHint: Vec<String> = Vec::new(), | ||
387 | diagnostics_warningsAsInfo: Vec<String> = Vec::new(), | ||
388 | |||
389 | files_watcher: String = "client".into(), | ||
390 | |||
391 | hoverActions_debug: bool = true, | ||
392 | hoverActions_enable: bool = true, | ||
393 | hoverActions_gotoTypeDef: bool = true, | ||
394 | hoverActions_implementations: bool = true, | ||
395 | hoverActions_run: bool = true, | ||
396 | |||
397 | inlayHints_chainingHints: bool = true, | ||
398 | inlayHints_maxLength: Option<usize> = None, | ||
399 | inlayHints_parameterHints: bool = true, | ||
400 | inlayHints_typeHints: bool = true, | ||
401 | |||
402 | lens_debug: bool = true, | ||
403 | lens_enable: bool = true, | ||
404 | lens_implementations: bool = true, | ||
405 | lens_run: bool = true, | ||
406 | |||
407 | linkedProjects: Vec<ManifestOrProjectJson> = Vec::new(), | ||
408 | lruCapacity: Option<usize> = None, | ||
409 | notifications_cargoTomlNotFound: bool = true, | ||
410 | procMacro_enable: bool = false, | ||
411 | |||
412 | rustfmt_extraArgs: Vec<String> = Vec::new(), | ||
413 | rustfmt_overrideCommand: Option<Vec<String>> = None, | ||
414 | |||
415 | withSysroot: bool = true, | ||
416 | } | ||
417 | } | ||
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 4e556bd50..b48239058 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -467,10 +467,10 @@ impl GlobalState { | |||
467 | (Some(err), _) => { | 467 | (Some(err), _) => { |
468 | log::error!("failed to fetch the server settings: {:?}", err) | 468 | log::error!("failed to fetch the server settings: {:?}", err) |
469 | } | 469 | } |
470 | (None, Some(configs)) => { | 470 | (None, Some(mut configs)) => { |
471 | if let Some(new_config) = configs.get(0) { | 471 | if let Some(json) = configs.get_mut(0) { |
472 | let mut config = this.config.clone(); | 472 | let mut config = this.config.clone(); |
473 | config.update(&new_config); | 473 | config.update(json.take()); |
474 | this.update_configuration(config); | 474 | this.update_configuration(config); |
475 | } | 475 | } |
476 | } | 476 | } |