]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/src/config.rs
Auto merge of #13320 - Veykril:ty-alias-hover, r=Veykril
[rust.git] / crates / rust-analyzer / src / config.rs
1 //! Config used by the language server.
2 //!
3 //! We currently get this config from `initialize` LSP request, which is not the
4 //! best way to do it, but was the simplest thing we could implement.
5 //!
6 //! Of particular interest is the `feature_flags` hash map: while other fields
7 //! configure the server itself, feature flags are passed into analysis, and
8 //! tweak things like automatic insertion of `()` in completions.
9
10 use std::{ffi::OsString, fmt, iter, path::PathBuf};
11
12 use flycheck::FlycheckConfig;
13 use ide::{
14     AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
15     HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig,
16     JoinLinesConfig, Snippet, SnippetScope,
17 };
18 use ide_db::{
19     imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
20     SnippetCap,
21 };
22 use itertools::Itertools;
23 use lsp_types::{ClientCapabilities, MarkupKind};
24 use project_model::{
25     CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource,
26     UnsetTestCrates,
27 };
28 use rustc_hash::{FxHashMap, FxHashSet};
29 use serde::{de::DeserializeOwned, Deserialize};
30 use vfs::AbsPathBuf;
31
32 use crate::{
33     caps::completion_item_edit_resolve,
34     diagnostics::DiagnosticsMapConfig,
35     line_index::OffsetEncoding,
36     lsp_ext::{self, supports_utf8, WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope},
37 };
38
39 mod patch_old_style;
40
41 // Conventions for configuration keys to preserve maximal extendability without breakage:
42 //  - Toggles (be it binary true/false or with more options in-between) should almost always suffix as `_enable`
43 //    This has the benefit of namespaces being extensible, and if the suffix doesn't fit later it can be changed without breakage.
44 //  - In general be wary of using the namespace of something verbatim, it prevents us from adding subkeys in the future
45 //  - Don't use abbreviations unless really necessary
46 //  - foo_command = overrides the subcommand, foo_overrideCommand allows full overwriting, extra args only applies for foo_command
47
48 // Defines the server-side configuration of the rust-analyzer. We generate
49 // *parts* of VS Code's `package.json` config from this. Run `cargo test` to
50 // re-generate that file.
51 //
52 // However, editor specific config, which the server doesn't know about, should
53 // be specified directly in `package.json`.
54 //
55 // To deprecate an option by replacing it with another name use `new_name | `old_name` so that we keep
56 // parsing the old name.
57 config_data! {
58     struct ConfigData {
59         /// Placeholder expression to use for missing expressions in assists.
60         assist_expressionFillDefault: ExprFillDefaultDef              = "\"todo\"",
61
62         /// Warm up caches on project load.
63         cachePriming_enable: bool = "true",
64         /// How many worker threads to handle priming caches. The default `0` means to pick automatically.
65         cachePriming_numThreads: ParallelCachePrimingNumThreads = "0",
66
67         /// Automatically refresh project info via `cargo metadata` on
68         /// `Cargo.toml` or `.cargo/config.toml` changes.
69         cargo_autoreload: bool           = "true",
70         /// Run build scripts (`build.rs`) for more precise code analysis.
71         cargo_buildScripts_enable: bool  = "true",
72         /// Override the command rust-analyzer uses to run build scripts and
73         /// build procedural macros. The command is required to output json
74         /// and should therefore include `--message-format=json` or a similar
75         /// option.
76         ///
77         /// By default, a cargo invocation will be constructed for the configured
78         /// targets and features, with the following base command line:
79         ///
80         /// ```bash
81         /// cargo check --quiet --workspace --message-format=json --all-targets
82         /// ```
83         /// .
84         cargo_buildScripts_overrideCommand: Option<Vec<String>> = "null",
85         /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
86         /// avoid checking unnecessary things.
87         cargo_buildScripts_useRustcWrapper: bool = "true",
88         /// Extra environment variables that will be set when running cargo, rustc
89         /// or other commands within the workspace. Useful for setting RUSTFLAGS.
90         cargo_extraEnv: FxHashMap<String, String> = "{}",
91         /// List of features to activate.
92         ///
93         /// Set this to `"all"` to pass `--all-features` to cargo.
94         cargo_features: CargoFeaturesDef      = "[]",
95         /// Whether to pass `--no-default-features` to cargo.
96         cargo_noDefaultFeatures: bool    = "false",
97         /// Internal config for debugging, disables loading of sysroot crates.
98         cargo_noSysroot: bool            = "false",
99         /// Compilation target override (target triple).
100         cargo_target: Option<String>     = "null",
101         /// Unsets `#[cfg(test)]` for the specified crates.
102         cargo_unsetTest: Vec<String>   = "[\"core\"]",
103
104         /// Check all targets and tests (`--all-targets`).
105         checkOnSave_allTargets: bool                     = "true",
106         /// Cargo command to use for `cargo check`.
107         checkOnSave_command: String                      = "\"check\"",
108         /// Run specified `cargo check` command for diagnostics on save.
109         checkOnSave_enable: bool                         = "true",
110         /// Extra arguments for `cargo check`.
111         checkOnSave_extraArgs: Vec<String>               = "[]",
112         /// Extra environment variables that will be set when running `cargo check`.
113         /// Extends `#rust-analyzer.cargo.extraEnv#`.
114         checkOnSave_extraEnv: FxHashMap<String, String> = "{}",
115         /// List of features to activate. Defaults to
116         /// `#rust-analyzer.cargo.features#`.
117         ///
118         /// Set to `"all"` to pass `--all-features` to Cargo.
119         checkOnSave_features: Option<CargoFeaturesDef>      = "null",
120         /// Whether to pass `--no-default-features` to Cargo. Defaults to
121         /// `#rust-analyzer.cargo.noDefaultFeatures#`.
122         checkOnSave_noDefaultFeatures: Option<bool>      = "null",
123         /// Override the command rust-analyzer uses instead of `cargo check` for
124         /// diagnostics on save. The command is required to output json and
125         /// should therefor include `--message-format=json` or a similar option.
126         ///
127         /// If you're changing this because you're using some tool wrapping
128         /// Cargo, you might also want to change
129         /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`.
130         ///
131         /// If there are multiple linked projects, this command is invoked for
132         /// each of them, with the working directory being the project root
133         /// (i.e., the folder containing the `Cargo.toml`).
134         ///
135         /// An example command would be:
136         ///
137         /// ```bash
138         /// cargo check --workspace --message-format=json --all-targets
139         /// ```
140         /// .
141         checkOnSave_overrideCommand: Option<Vec<String>> = "null",
142         /// Check for a specific target. Defaults to
143         /// `#rust-analyzer.cargo.target#`.
144         checkOnSave_target: Option<String>               = "null",
145
146         /// Toggles the additional completions that automatically add imports when completed.
147         /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
148         completion_autoimport_enable: bool       = "true",
149         /// Toggles the additional completions that automatically show method calls and field accesses
150         /// with `self` prefixed to them when inside a method.
151         completion_autoself_enable: bool        = "true",
152         /// Whether to add parenthesis and argument snippets when completing function.
153         completion_callable_snippets: CallableCompletionDef  = "\"fill_arguments\"",
154         /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
155         completion_postfix_enable: bool         = "true",
156         /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.
157         completion_privateEditable_enable: bool = "false",
158         /// Custom completion snippets.
159         // NOTE: Keep this list in sync with the feature docs of user snippets.
160         completion_snippets_custom: FxHashMap<String, SnippetDef> = r#"{
161             "Arc::new": {
162                 "postfix": "arc",
163                 "body": "Arc::new(${receiver})",
164                 "requires": "std::sync::Arc",
165                 "description": "Put the expression into an `Arc`",
166                 "scope": "expr"
167             },
168             "Rc::new": {
169                 "postfix": "rc",
170                 "body": "Rc::new(${receiver})",
171                 "requires": "std::rc::Rc",
172                 "description": "Put the expression into an `Rc`",
173                 "scope": "expr"
174             },
175             "Box::pin": {
176                 "postfix": "pinbox",
177                 "body": "Box::pin(${receiver})",
178                 "requires": "std::boxed::Box",
179                 "description": "Put the expression into a pinned `Box`",
180                 "scope": "expr"
181             },
182             "Ok": {
183                 "postfix": "ok",
184                 "body": "Ok(${receiver})",
185                 "description": "Wrap the expression in a `Result::Ok`",
186                 "scope": "expr"
187             },
188             "Err": {
189                 "postfix": "err",
190                 "body": "Err(${receiver})",
191                 "description": "Wrap the expression in a `Result::Err`",
192                 "scope": "expr"
193             },
194             "Some": {
195                 "postfix": "some",
196                 "body": "Some(${receiver})",
197                 "description": "Wrap the expression in an `Option::Some`",
198                 "scope": "expr"
199             }
200         }"#,
201
202         /// List of rust-analyzer diagnostics to disable.
203         diagnostics_disabled: FxHashSet<String> = "[]",
204         /// Whether to show native rust-analyzer diagnostics.
205         diagnostics_enable: bool                = "true",
206         /// Whether to show experimental rust-analyzer diagnostics that might
207         /// have more false positives than usual.
208         diagnostics_experimental_enable: bool    = "false",
209         /// Map of prefixes to be substituted when parsing diagnostic file paths.
210         /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
211         diagnostics_remapPrefix: FxHashMap<String, String> = "{}",
212         /// List of warnings that should be displayed with hint severity.
213         ///
214         /// The warnings will be indicated by faded text or three dots in code
215         /// and will not show up in the `Problems Panel`.
216         diagnostics_warningsAsHint: Vec<String> = "[]",
217         /// List of warnings that should be displayed with info severity.
218         ///
219         /// The warnings will be indicated by a blue squiggly underline in code
220         /// and a blue icon in the `Problems Panel`.
221         diagnostics_warningsAsInfo: Vec<String> = "[]",
222
223         /// These directories will be ignored by rust-analyzer. They are
224         /// relative to the workspace root, and globs are not supported. You may
225         /// also need to add the folders to Code's `files.watcherExclude`.
226         files_excludeDirs: Vec<PathBuf> = "[]",
227         /// Controls file watching implementation.
228         files_watcher: FilesWatcherDef = "\"client\"",
229         /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
230         highlightRelated_breakPoints_enable: bool = "true",
231         /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
232         highlightRelated_exitPoints_enable: bool = "true",
233         /// Enables highlighting of related references while the cursor is on any identifier.
234         highlightRelated_references_enable: bool = "true",
235         /// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords.
236         highlightRelated_yieldPoints_enable: bool = "true",
237
238         /// Whether to show `Debug` action. Only applies when
239         /// `#rust-analyzer.hover.actions.enable#` is set.
240         hover_actions_debug_enable: bool           = "true",
241         /// Whether to show HoverActions in Rust files.
242         hover_actions_enable: bool          = "true",
243         /// Whether to show `Go to Type Definition` action. Only applies when
244         /// `#rust-analyzer.hover.actions.enable#` is set.
245         hover_actions_gotoTypeDef_enable: bool     = "true",
246         /// Whether to show `Implementations` action. Only applies when
247         /// `#rust-analyzer.hover.actions.enable#` is set.
248         hover_actions_implementations_enable: bool = "true",
249         /// Whether to show `References` action. Only applies when
250         /// `#rust-analyzer.hover.actions.enable#` is set.
251         hover_actions_references_enable: bool      = "false",
252         /// Whether to show `Run` action. Only applies when
253         /// `#rust-analyzer.hover.actions.enable#` is set.
254         hover_actions_run_enable: bool             = "true",
255
256         /// Whether to show documentation on hover.
257         hover_documentation_enable: bool           = "true",
258         /// Whether to show keyword hover popups. Only applies when
259         /// `#rust-analyzer.hover.documentation.enable#` is set.
260         hover_documentation_keywords_enable: bool  = "true",
261         /// Use markdown syntax for links in hover.
262         hover_links_enable: bool = "true",
263
264         /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.
265         imports_granularity_enforce: bool              = "false",
266         /// How imports should be grouped into use statements.
267         imports_granularity_group: ImportGranularityDef  = "\"crate\"",
268         /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.
269         imports_group_enable: bool                           = "true",
270         /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
271         imports_merge_glob: bool           = "true",
272         /// Prefer to unconditionally use imports of the core and alloc crate, over the std crate.
273         imports_prefer_no_std: bool                     = "false",
274         /// The path structure for newly inserted paths to use.
275         imports_prefix: ImportPrefixDef               = "\"plain\"",
276
277         /// Whether to show inlay type hints for binding modes.
278         inlayHints_bindingModeHints_enable: bool                   = "false",
279         /// Whether to show inlay type hints for method chains.
280         inlayHints_chainingHints_enable: bool                      = "true",
281         /// Whether to show inlay hints after a closing `}` to indicate what item it belongs to.
282         inlayHints_closingBraceHints_enable: bool                  = "true",
283         /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
284         /// to always show them).
285         inlayHints_closingBraceHints_minLines: usize               = "25",
286         /// Whether to show inlay type hints for return types of closures.
287         inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef  = "\"never\"",
288         /// Whether to show inlay type hints for elided lifetimes in function signatures.
289         inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
290         /// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
291         inlayHints_lifetimeElisionHints_useParameterNames: bool    = "false",
292         /// Maximum length for inlay hints. Set to null to have an unlimited length.
293         inlayHints_maxLength: Option<usize>                        = "25",
294         /// Whether to show function parameter name inlay hints at the call
295         /// site.
296         inlayHints_parameterHints_enable: bool                     = "true",
297         /// Whether to show inlay type hints for compiler inserted reborrows.
298         inlayHints_reborrowHints_enable: ReborrowHintsDef          = "\"never\"",
299         /// Whether to render leading colons for type hints, and trailing colons for parameter hints.
300         inlayHints_renderColons: bool                              = "true",
301         /// Whether to show inlay type hints for variables.
302         inlayHints_typeHints_enable: bool                          = "true",
303         /// Whether to hide inlay type hints for `let` statements that initialize to a closure.
304         /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.
305         inlayHints_typeHints_hideClosureInitialization: bool       = "false",
306         /// Whether to hide inlay type hints for constructors.
307         inlayHints_typeHints_hideNamedConstructor: bool            = "false",
308
309         /// Join lines merges consecutive declaration and initialization of an assignment.
310         joinLines_joinAssignments: bool = "true",
311         /// Join lines inserts else between consecutive ifs.
312         joinLines_joinElseIf: bool = "true",
313         /// Join lines removes trailing commas.
314         joinLines_removeTrailingComma: bool = "true",
315         /// Join lines unwraps trivial blocks.
316         joinLines_unwrapTrivialBlock: bool = "true",
317
318
319         /// Whether to show `Debug` lens. Only applies when
320         /// `#rust-analyzer.lens.enable#` is set.
321         lens_debug_enable: bool            = "true",
322         /// Whether to show CodeLens in Rust files.
323         lens_enable: bool           = "true",
324         /// Internal config: use custom client-side commands even when the
325         /// client doesn't set the corresponding capability.
326         lens_forceCustomCommands: bool = "true",
327         /// Whether to show `Implementations` lens. Only applies when
328         /// `#rust-analyzer.lens.enable#` is set.
329         lens_implementations_enable: bool  = "true",
330         /// Where to render annotations.
331         lens_location: AnnotationLocation = "\"above_name\"",
332         /// Whether to show `References` lens for Struct, Enum, and Union.
333         /// Only applies when `#rust-analyzer.lens.enable#` is set.
334         lens_references_adt_enable: bool = "false",
335         /// Whether to show `References` lens for Enum Variants.
336         /// Only applies when `#rust-analyzer.lens.enable#` is set.
337         lens_references_enumVariant_enable: bool = "false",
338         /// Whether to show `Method References` lens. Only applies when
339         /// `#rust-analyzer.lens.enable#` is set.
340         lens_references_method_enable: bool = "false",
341         /// Whether to show `References` lens for Trait.
342         /// Only applies when `#rust-analyzer.lens.enable#` is set.
343         lens_references_trait_enable: bool = "false",
344         /// Whether to show `Run` lens. Only applies when
345         /// `#rust-analyzer.lens.enable#` is set.
346         lens_run_enable: bool              = "true",
347
348         /// Disable project auto-discovery in favor of explicitly specified set
349         /// of projects.
350         ///
351         /// Elements must be paths pointing to `Cargo.toml`,
352         /// `rust-project.json`, or JSON objects in `rust-project.json` format.
353         linkedProjects: Vec<ManifestOrProjectJson> = "[]",
354
355         /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
356         lru_capacity: Option<usize>                 = "null",
357
358         /// Whether to show `can't find Cargo.toml` error message.
359         notifications_cargoTomlNotFound: bool      = "true",
360
361         /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
362         procMacro_attributes_enable: bool = "true",
363         /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
364         procMacro_enable: bool                     = "true",
365         /// These proc-macros will be ignored when trying to expand them.
366         ///
367         /// This config takes a map of crate names with the exported proc-macro names to ignore as values.
368         procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>>          = "{}",
369         /// Internal config, path to proc-macro server executable (typically,
370         /// this is rust-analyzer itself, but we override this in tests).
371         procMacro_server: Option<PathBuf>          = "null",
372
373         /// Exclude imports from find-all-references.
374         references_excludeImports: bool = "false",
375
376         /// Command to be executed instead of 'cargo' for runnables.
377         runnables_command: Option<String> = "null",
378         /// Additional arguments to be passed to cargo for runnables such as
379         /// tests or binaries. For example, it may be `--release`.
380         runnables_extraArgs: Vec<String>   = "[]",
381
382         /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
383         /// projects, or "discover" to try to automatically find it if the `rustc-dev` component
384         /// is installed.
385         ///
386         /// Any project which uses rust-analyzer with the rustcPrivate
387         /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
388         ///
389         /// This option does not take effect until rust-analyzer is restarted.
390         rustc_source: Option<String> = "null",
391
392         /// Additional arguments to `rustfmt`.
393         rustfmt_extraArgs: Vec<String>               = "[]",
394         /// Advanced option, fully override the command rust-analyzer uses for
395         /// formatting.
396         rustfmt_overrideCommand: Option<Vec<String>> = "null",
397         /// Enables the use of rustfmt's unstable range formatting command for the
398         /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
399         /// available on a nightly build.
400         rustfmt_rangeFormatting_enable: bool = "false",
401
402         /// Inject additional highlighting into doc comments.
403         ///
404         /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
405         /// doc links.
406         semanticHighlighting_doc_comment_inject_enable: bool = "true",
407         /// Use semantic tokens for operators.
408         ///
409         /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
410         /// they are tagged with modifiers.
411         semanticHighlighting_operator_enable: bool = "true",
412         /// Use specialized semantic tokens for operators.
413         ///
414         /// When enabled, rust-analyzer will emit special token types for operator tokens instead
415         /// of the generic `operator` token type.
416         semanticHighlighting_operator_specialization_enable: bool = "false",
417         /// Use semantic tokens for punctuations.
418         ///
419         /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
420         /// they are tagged with modifiers or have a special role.
421         semanticHighlighting_punctuation_enable: bool = "false",
422         /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
423         /// calls.
424         semanticHighlighting_punctuation_separate_macro_bang: bool = "false",
425         /// Use specialized semantic tokens for punctuations.
426         ///
427         /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead
428         /// of the generic `punctuation` token type.
429         semanticHighlighting_punctuation_specialization_enable: bool = "false",
430         /// Use semantic tokens for strings.
431         ///
432         /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
433         /// By disabling semantic tokens for strings, other grammars can be used to highlight
434         /// their contents.
435         semanticHighlighting_strings_enable: bool = "true",
436
437         /// Show full signature of the callable. Only shows parameters if disabled.
438         signatureInfo_detail: SignatureDetail                           = "\"full\"",
439         /// Show documentation.
440         signatureInfo_documentation_enable: bool                       = "true",
441
442         /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.
443         typing_autoClosingAngleBrackets_enable: bool = "false",
444
445         /// Workspace symbol search kind.
446         workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = "\"only_types\"",
447         /// Limits the number of items returned from a workspace symbol search (Defaults to 128).
448         /// Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.
449         /// Other clients requires all results upfront and might require a higher limit.
450         workspace_symbol_search_limit: usize = "128",
451         /// Workspace symbol search scope.
452         workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = "\"workspace\"",
453     }
454 }
455
456 impl Default for ConfigData {
457     fn default() -> Self {
458         ConfigData::from_json(serde_json::Value::Null, &mut Vec::new())
459     }
460 }
461
462 #[derive(Debug, Clone)]
463 pub struct Config {
464     pub discovered_projects: Option<Vec<ProjectManifest>>,
465     caps: lsp_types::ClientCapabilities,
466     root_path: AbsPathBuf,
467     data: ConfigData,
468     detached_files: Vec<AbsPathBuf>,
469     snippets: Vec<Snippet>,
470 }
471
472 type ParallelCachePrimingNumThreads = u8;
473
474 #[derive(Debug, Clone, Eq, PartialEq)]
475 pub enum LinkedProject {
476     ProjectManifest(ProjectManifest),
477     InlineJsonProject(ProjectJson),
478 }
479
480 impl From<ProjectManifest> for LinkedProject {
481     fn from(v: ProjectManifest) -> Self {
482         LinkedProject::ProjectManifest(v)
483     }
484 }
485
486 impl From<ProjectJson> for LinkedProject {
487     fn from(v: ProjectJson) -> Self {
488         LinkedProject::InlineJsonProject(v)
489     }
490 }
491
492 pub struct CallInfoConfig {
493     pub params_only: bool,
494     pub docs: bool,
495 }
496
497 #[derive(Clone, Debug, PartialEq, Eq)]
498 pub struct LensConfig {
499     // runnables
500     pub run: bool,
501     pub debug: bool,
502
503     // implementations
504     pub implementations: bool,
505
506     // references
507     pub method_refs: bool,
508     pub refs_adt: bool,   // for Struct, Enum, Union and Trait
509     pub refs_trait: bool, // for Struct, Enum, Union and Trait
510     pub enum_variant_refs: bool,
511
512     // annotations
513     pub location: AnnotationLocation,
514 }
515
516 #[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
517 #[serde(rename_all = "snake_case")]
518 pub enum AnnotationLocation {
519     AboveName,
520     AboveWholeItem,
521 }
522
523 impl From<AnnotationLocation> for ide::AnnotationLocation {
524     fn from(location: AnnotationLocation) -> Self {
525         match location {
526             AnnotationLocation::AboveName => ide::AnnotationLocation::AboveName,
527             AnnotationLocation::AboveWholeItem => ide::AnnotationLocation::AboveWholeItem,
528         }
529     }
530 }
531
532 impl LensConfig {
533     pub fn any(&self) -> bool {
534         self.run
535             || self.debug
536             || self.implementations
537             || self.method_refs
538             || self.refs_adt
539             || self.refs_trait
540             || self.enum_variant_refs
541     }
542
543     pub fn none(&self) -> bool {
544         !self.any()
545     }
546
547     pub fn runnable(&self) -> bool {
548         self.run || self.debug
549     }
550
551     pub fn references(&self) -> bool {
552         self.method_refs || self.refs_adt || self.refs_trait || self.enum_variant_refs
553     }
554 }
555
556 #[derive(Clone, Debug, PartialEq, Eq)]
557 pub struct HoverActionsConfig {
558     pub implementations: bool,
559     pub references: bool,
560     pub run: bool,
561     pub debug: bool,
562     pub goto_type_def: bool,
563 }
564
565 impl HoverActionsConfig {
566     pub const NO_ACTIONS: Self = Self {
567         implementations: false,
568         references: false,
569         run: false,
570         debug: false,
571         goto_type_def: false,
572     };
573
574     pub fn any(&self) -> bool {
575         self.implementations || self.references || self.runnable() || self.goto_type_def
576     }
577
578     pub fn none(&self) -> bool {
579         !self.any()
580     }
581
582     pub fn runnable(&self) -> bool {
583         self.run || self.debug
584     }
585 }
586
587 #[derive(Debug, Clone)]
588 pub struct FilesConfig {
589     pub watcher: FilesWatcher,
590     pub exclude: Vec<AbsPathBuf>,
591 }
592
593 #[derive(Debug, Clone)]
594 pub enum FilesWatcher {
595     Client,
596     Server,
597 }
598
599 #[derive(Debug, Clone)]
600 pub struct NotificationsConfig {
601     pub cargo_toml_not_found: bool,
602 }
603
604 #[derive(Debug, Clone)]
605 pub enum RustfmtConfig {
606     Rustfmt { extra_args: Vec<String>, enable_range_formatting: bool },
607     CustomCommand { command: String, args: Vec<String> },
608 }
609
610 /// Configuration for runnable items, such as `main` function or tests.
611 #[derive(Debug, Clone)]
612 pub struct RunnablesConfig {
613     /// Custom command to be executed instead of `cargo` for runnables.
614     pub override_cargo: Option<String>,
615     /// Additional arguments for the `cargo`, e.g. `--release`.
616     pub cargo_extra_args: Vec<String>,
617 }
618
619 /// Configuration for workspace symbol search requests.
620 #[derive(Debug, Clone)]
621 pub struct WorkspaceSymbolConfig {
622     /// In what scope should the symbol be searched in.
623     pub search_scope: WorkspaceSymbolSearchScope,
624     /// What kind of symbol is being searched for.
625     pub search_kind: WorkspaceSymbolSearchKind,
626     /// How many items are returned at most.
627     pub search_limit: usize,
628 }
629
630 pub struct ClientCommandsConfig {
631     pub run_single: bool,
632     pub debug_single: bool,
633     pub show_reference: bool,
634     pub goto_location: bool,
635     pub trigger_parameter_hints: bool,
636 }
637
638 #[derive(Debug)]
639 pub struct ConfigUpdateError {
640     errors: Vec<(String, serde_json::Error)>,
641 }
642
643 impl fmt::Display for ConfigUpdateError {
644     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
645         let errors = self.errors.iter().format_with("\n", |(key, e), f| {
646             f(key)?;
647             f(&": ")?;
648             f(e)
649         });
650         write!(
651             f,
652             "rust-analyzer found {} invalid config value{}:\n{}",
653             self.errors.len(),
654             if self.errors.len() == 1 { "" } else { "s" },
655             errors
656         )
657     }
658 }
659
660 impl Config {
661     pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self {
662         Config {
663             caps,
664             data: ConfigData::default(),
665             detached_files: Vec::new(),
666             discovered_projects: None,
667             root_path,
668             snippets: Default::default(),
669         }
670     }
671
672     pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigUpdateError> {
673         tracing::info!("updating config from JSON: {:#}", json);
674         if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
675             return Ok(());
676         }
677         let mut errors = Vec::new();
678         self.detached_files =
679             get_field::<Vec<PathBuf>>(&mut json, &mut errors, "detachedFiles", None, "[]")
680                 .into_iter()
681                 .map(AbsPathBuf::assert)
682                 .collect();
683         patch_old_style::patch_json_for_outdated_configs(&mut json);
684         self.data = ConfigData::from_json(json, &mut errors);
685         tracing::debug!("deserialized config data: {:#?}", self.data);
686         self.snippets.clear();
687         for (name, def) in self.data.completion_snippets_custom.iter() {
688             if def.prefix.is_empty() && def.postfix.is_empty() {
689                 continue;
690             }
691             let scope = match def.scope {
692                 SnippetScopeDef::Expr => SnippetScope::Expr,
693                 SnippetScopeDef::Type => SnippetScope::Type,
694                 SnippetScopeDef::Item => SnippetScope::Item,
695             };
696             match Snippet::new(
697                 &def.prefix,
698                 &def.postfix,
699                 &def.body,
700                 def.description.as_ref().unwrap_or(name),
701                 &def.requires,
702                 scope,
703             ) {
704                 Some(snippet) => self.snippets.push(snippet),
705                 None => errors.push((
706                     format!("snippet {name} is invalid"),
707                     <serde_json::Error as serde::de::Error>::custom(
708                         "snippet path is invalid or triggers are missing",
709                     ),
710                 )),
711             }
712         }
713
714         self.validate(&mut errors);
715
716         if errors.is_empty() {
717             Ok(())
718         } else {
719             Err(ConfigUpdateError { errors })
720         }
721     }
722
723     fn validate(&self, error_sink: &mut Vec<(String, serde_json::Error)>) {
724         use serde::de::Error;
725         if self.data.checkOnSave_command.is_empty() {
726             error_sink.push((
727                 "/checkOnSave/command".to_string(),
728                 serde_json::Error::custom("expected a non-empty string"),
729             ));
730         }
731     }
732
733     pub fn json_schema() -> serde_json::Value {
734         ConfigData::json_schema()
735     }
736
737     pub fn root_path(&self) -> &AbsPathBuf {
738         &self.root_path
739     }
740
741     pub fn caps(&self) -> &lsp_types::ClientCapabilities {
742         &self.caps
743     }
744
745     pub fn detached_files(&self) -> &[AbsPathBuf] {
746         &self.detached_files
747     }
748 }
749
750 macro_rules! try_ {
751     ($expr:expr) => {
752         || -> _ { Some($expr) }()
753     };
754 }
755 macro_rules! try_or {
756     ($expr:expr, $or:expr) => {
757         try_!($expr).unwrap_or($or)
758     };
759 }
760
761 macro_rules! try_or_def {
762     ($expr:expr) => {
763         try_!($expr).unwrap_or_default()
764     };
765 }
766
767 impl Config {
768     pub fn linked_projects(&self) -> Vec<LinkedProject> {
769         match self.data.linkedProjects.as_slice() {
770             [] => match self.discovered_projects.as_ref() {
771                 Some(discovered_projects) => {
772                     let exclude_dirs: Vec<_> = self
773                         .data
774                         .files_excludeDirs
775                         .iter()
776                         .map(|p| self.root_path.join(p))
777                         .collect();
778                     discovered_projects
779                         .iter()
780                         .filter(|p| {
781                             let (ProjectManifest::ProjectJson(path)
782                             | ProjectManifest::CargoToml(path)) = p;
783                             !exclude_dirs.iter().any(|p| path.starts_with(p))
784                         })
785                         .cloned()
786                         .map(LinkedProject::from)
787                         .collect()
788                 }
789                 None => Vec::new(),
790             },
791             linked_projects => linked_projects
792                 .iter()
793                 .filter_map(|linked_project| match linked_project {
794                     ManifestOrProjectJson::Manifest(it) => {
795                         let path = self.root_path.join(it);
796                         ProjectManifest::from_manifest_file(path)
797                             .map_err(|e| tracing::error!("failed to load linked project: {}", e))
798                             .ok()
799                             .map(Into::into)
800                     }
801                     ManifestOrProjectJson::ProjectJson(it) => {
802                         Some(ProjectJson::new(&self.root_path, it.clone()).into())
803                     }
804                 })
805                 .collect(),
806         }
807     }
808
809     pub fn did_save_text_document_dynamic_registration(&self) -> bool {
810         let caps = try_or_def!(self.caps.text_document.as_ref()?.synchronization.clone()?);
811         caps.did_save == Some(true) && caps.dynamic_registration == Some(true)
812     }
813
814     pub fn did_change_watched_files_dynamic_registration(&self) -> bool {
815         try_or_def!(
816             self.caps.workspace.as_ref()?.did_change_watched_files.as_ref()?.dynamic_registration?
817         )
818     }
819
820     pub fn prefill_caches(&self) -> bool {
821         self.data.cachePriming_enable
822     }
823
824     pub fn location_link(&self) -> bool {
825         try_or_def!(self.caps.text_document.as_ref()?.definition?.link_support?)
826     }
827
828     pub fn line_folding_only(&self) -> bool {
829         try_or_def!(self.caps.text_document.as_ref()?.folding_range.as_ref()?.line_folding_only?)
830     }
831
832     pub fn hierarchical_symbols(&self) -> bool {
833         try_or_def!(
834             self.caps
835                 .text_document
836                 .as_ref()?
837                 .document_symbol
838                 .as_ref()?
839                 .hierarchical_document_symbol_support?
840         )
841     }
842
843     pub fn code_action_literals(&self) -> bool {
844         try_!(self
845             .caps
846             .text_document
847             .as_ref()?
848             .code_action
849             .as_ref()?
850             .code_action_literal_support
851             .as_ref()?)
852         .is_some()
853     }
854
855     pub fn work_done_progress(&self) -> bool {
856         try_or_def!(self.caps.window.as_ref()?.work_done_progress?)
857     }
858
859     pub fn will_rename(&self) -> bool {
860         try_or_def!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?)
861     }
862
863     pub fn change_annotation_support(&self) -> bool {
864         try_!(self
865             .caps
866             .workspace
867             .as_ref()?
868             .workspace_edit
869             .as_ref()?
870             .change_annotation_support
871             .as_ref()?)
872         .is_some()
873     }
874
875     pub fn code_action_resolve(&self) -> bool {
876         try_or_def!(self
877             .caps
878             .text_document
879             .as_ref()?
880             .code_action
881             .as_ref()?
882             .resolve_support
883             .as_ref()?
884             .properties
885             .as_slice())
886         .iter()
887         .any(|it| it == "edit")
888     }
889
890     pub fn signature_help_label_offsets(&self) -> bool {
891         try_or_def!(
892             self.caps
893                 .text_document
894                 .as_ref()?
895                 .signature_help
896                 .as_ref()?
897                 .signature_information
898                 .as_ref()?
899                 .parameter_information
900                 .as_ref()?
901                 .label_offset_support?
902         )
903     }
904
905     pub fn completion_label_details_support(&self) -> bool {
906         try_!(self
907             .caps
908             .text_document
909             .as_ref()?
910             .completion
911             .as_ref()?
912             .completion_item
913             .as_ref()?
914             .label_details_support
915             .as_ref()?)
916         .is_some()
917     }
918
919     pub fn offset_encoding(&self) -> OffsetEncoding {
920         if supports_utf8(&self.caps) {
921             OffsetEncoding::Utf8
922         } else {
923             OffsetEncoding::Utf16
924         }
925     }
926
927     fn experimental(&self, index: &'static str) -> bool {
928         try_or_def!(self.caps.experimental.as_ref()?.get(index)?.as_bool()?)
929     }
930
931     pub fn code_action_group(&self) -> bool {
932         self.experimental("codeActionGroup")
933     }
934
935     pub fn server_status_notification(&self) -> bool {
936         self.experimental("serverStatusNotification")
937     }
938
939     pub fn publish_diagnostics(&self) -> bool {
940         self.data.diagnostics_enable
941     }
942
943     pub fn diagnostics(&self) -> DiagnosticsConfig {
944         DiagnosticsConfig {
945             proc_attr_macros_enabled: self.expand_proc_attr_macros(),
946             proc_macros_enabled: self.data.procMacro_enable,
947             disable_experimental: !self.data.diagnostics_experimental_enable,
948             disabled: self.data.diagnostics_disabled.clone(),
949             expr_fill_default: match self.data.assist_expressionFillDefault {
950                 ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
951                 ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
952             },
953             insert_use: self.insert_use_config(),
954             prefer_no_std: self.data.imports_prefer_no_std,
955         }
956     }
957
958     pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
959         DiagnosticsMapConfig {
960             remap_prefix: self.data.diagnostics_remapPrefix.clone(),
961             warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
962             warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
963         }
964     }
965
966     pub fn extra_env(&self) -> &FxHashMap<String, String> {
967         &self.data.cargo_extraEnv
968     }
969
970     pub fn check_on_save_extra_env(&self) -> FxHashMap<String, String> {
971         let mut extra_env = self.data.cargo_extraEnv.clone();
972         extra_env.extend(self.data.checkOnSave_extraEnv.clone());
973         extra_env
974     }
975
976     pub fn lru_capacity(&self) -> Option<usize> {
977         self.data.lru_capacity
978     }
979
980     pub fn proc_macro_srv(&self) -> Option<(AbsPathBuf, Vec<OsString>)> {
981         if !self.data.procMacro_enable {
982             return None;
983         }
984         let path = match &self.data.procMacro_server {
985             Some(it) => self.root_path.join(it),
986             None => AbsPathBuf::assert(std::env::current_exe().ok()?),
987         };
988         Some((path, vec!["proc-macro".into()]))
989     }
990
991     pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
992         &self.data.procMacro_ignored
993     }
994
995     pub fn expand_proc_attr_macros(&self) -> bool {
996         self.data.procMacro_enable && self.data.procMacro_attributes_enable
997     }
998
999     pub fn files(&self) -> FilesConfig {
1000         FilesConfig {
1001             watcher: match self.data.files_watcher {
1002                 FilesWatcherDef::Client if self.did_change_watched_files_dynamic_registration() => {
1003                     FilesWatcher::Client
1004                 }
1005                 _ => FilesWatcher::Server,
1006             },
1007             exclude: self.data.files_excludeDirs.iter().map(|it| self.root_path.join(it)).collect(),
1008         }
1009     }
1010
1011     pub fn notifications(&self) -> NotificationsConfig {
1012         NotificationsConfig { cargo_toml_not_found: self.data.notifications_cargoTomlNotFound }
1013     }
1014
1015     pub fn cargo_autoreload(&self) -> bool {
1016         self.data.cargo_autoreload
1017     }
1018
1019     pub fn run_build_scripts(&self) -> bool {
1020         self.data.cargo_buildScripts_enable || self.data.procMacro_enable
1021     }
1022
1023     pub fn cargo(&self) -> CargoConfig {
1024         let rustc_source = self.data.rustc_source.as_ref().map(|rustc_src| {
1025             if rustc_src == "discover" {
1026                 RustcSource::Discover
1027             } else {
1028                 RustcSource::Path(self.root_path.join(rustc_src))
1029             }
1030         });
1031
1032         CargoConfig {
1033             features: match &self.data.cargo_features {
1034                 CargoFeaturesDef::All => CargoFeatures::All,
1035                 CargoFeaturesDef::Selected(features) => CargoFeatures::Selected {
1036                     features: features.clone(),
1037                     no_default_features: self.data.cargo_noDefaultFeatures,
1038                 },
1039             },
1040             target: self.data.cargo_target.clone(),
1041             no_sysroot: self.data.cargo_noSysroot,
1042             rustc_source,
1043             unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()),
1044             wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper,
1045             run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(),
1046             extra_env: self.data.cargo_extraEnv.clone(),
1047         }
1048     }
1049
1050     pub fn rustfmt(&self) -> RustfmtConfig {
1051         match &self.data.rustfmt_overrideCommand {
1052             Some(args) if !args.is_empty() => {
1053                 let mut args = args.clone();
1054                 let command = args.remove(0);
1055                 RustfmtConfig::CustomCommand { command, args }
1056             }
1057             Some(_) | None => RustfmtConfig::Rustfmt {
1058                 extra_args: self.data.rustfmt_extraArgs.clone(),
1059                 enable_range_formatting: self.data.rustfmt_rangeFormatting_enable,
1060             },
1061         }
1062     }
1063
1064     pub fn flycheck(&self) -> Option<FlycheckConfig> {
1065         if !self.data.checkOnSave_enable {
1066             return None;
1067         }
1068         let flycheck_config = match &self.data.checkOnSave_overrideCommand {
1069             Some(args) if !args.is_empty() => {
1070                 let mut args = args.clone();
1071                 let command = args.remove(0);
1072                 FlycheckConfig::CustomCommand {
1073                     command,
1074                     args,
1075                     extra_env: self.check_on_save_extra_env(),
1076                 }
1077             }
1078             Some(_) | None => FlycheckConfig::CargoCommand {
1079                 command: self.data.checkOnSave_command.clone(),
1080                 target_triple: self
1081                     .data
1082                     .checkOnSave_target
1083                     .clone()
1084                     .or_else(|| self.data.cargo_target.clone()),
1085                 all_targets: self.data.checkOnSave_allTargets,
1086                 no_default_features: self
1087                     .data
1088                     .checkOnSave_noDefaultFeatures
1089                     .unwrap_or(self.data.cargo_noDefaultFeatures),
1090                 all_features: matches!(
1091                     self.data.checkOnSave_features.as_ref().unwrap_or(&self.data.cargo_features),
1092                     CargoFeaturesDef::All
1093                 ),
1094                 features: match self
1095                     .data
1096                     .checkOnSave_features
1097                     .clone()
1098                     .unwrap_or_else(|| self.data.cargo_features.clone())
1099                 {
1100                     CargoFeaturesDef::All => vec![],
1101                     CargoFeaturesDef::Selected(it) => it,
1102                 },
1103                 extra_args: self.data.checkOnSave_extraArgs.clone(),
1104                 extra_env: self.check_on_save_extra_env(),
1105             },
1106         };
1107         Some(flycheck_config)
1108     }
1109
1110     pub fn runnables(&self) -> RunnablesConfig {
1111         RunnablesConfig {
1112             override_cargo: self.data.runnables_command.clone(),
1113             cargo_extra_args: self.data.runnables_extraArgs.clone(),
1114         }
1115     }
1116
1117     pub fn inlay_hints(&self) -> InlayHintsConfig {
1118         InlayHintsConfig {
1119             render_colons: self.data.inlayHints_renderColons,
1120             type_hints: self.data.inlayHints_typeHints_enable,
1121             parameter_hints: self.data.inlayHints_parameterHints_enable,
1122             chaining_hints: self.data.inlayHints_chainingHints_enable,
1123             closure_return_type_hints: match self.data.inlayHints_closureReturnTypeHints_enable {
1124                 ClosureReturnTypeHintsDef::Always => ide::ClosureReturnTypeHints::Always,
1125                 ClosureReturnTypeHintsDef::Never => ide::ClosureReturnTypeHints::Never,
1126                 ClosureReturnTypeHintsDef::WithBlock => ide::ClosureReturnTypeHints::WithBlock,
1127             },
1128             lifetime_elision_hints: match self.data.inlayHints_lifetimeElisionHints_enable {
1129                 LifetimeElisionDef::Always => ide::LifetimeElisionHints::Always,
1130                 LifetimeElisionDef::Never => ide::LifetimeElisionHints::Never,
1131                 LifetimeElisionDef::SkipTrivial => ide::LifetimeElisionHints::SkipTrivial,
1132             },
1133             hide_named_constructor_hints: self.data.inlayHints_typeHints_hideNamedConstructor,
1134             hide_closure_initialization_hints: self
1135                 .data
1136                 .inlayHints_typeHints_hideClosureInitialization,
1137             reborrow_hints: match self.data.inlayHints_reborrowHints_enable {
1138                 ReborrowHintsDef::Always => ide::ReborrowHints::Always,
1139                 ReborrowHintsDef::Never => ide::ReborrowHints::Never,
1140                 ReborrowHintsDef::Mutable => ide::ReborrowHints::MutableOnly,
1141             },
1142             binding_mode_hints: self.data.inlayHints_bindingModeHints_enable,
1143             param_names_for_lifetime_elision_hints: self
1144                 .data
1145                 .inlayHints_lifetimeElisionHints_useParameterNames,
1146             max_length: self.data.inlayHints_maxLength,
1147             closing_brace_hints_min_lines: if self.data.inlayHints_closingBraceHints_enable {
1148                 Some(self.data.inlayHints_closingBraceHints_minLines)
1149             } else {
1150                 None
1151             },
1152         }
1153     }
1154
1155     fn insert_use_config(&self) -> InsertUseConfig {
1156         InsertUseConfig {
1157             granularity: match self.data.imports_granularity_group {
1158                 ImportGranularityDef::Preserve => ImportGranularity::Preserve,
1159                 ImportGranularityDef::Item => ImportGranularity::Item,
1160                 ImportGranularityDef::Crate => ImportGranularity::Crate,
1161                 ImportGranularityDef::Module => ImportGranularity::Module,
1162             },
1163             enforce_granularity: self.data.imports_granularity_enforce,
1164             prefix_kind: match self.data.imports_prefix {
1165                 ImportPrefixDef::Plain => PrefixKind::Plain,
1166                 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
1167                 ImportPrefixDef::BySelf => PrefixKind::BySelf,
1168             },
1169             group: self.data.imports_group_enable,
1170             skip_glob_imports: !self.data.imports_merge_glob,
1171         }
1172     }
1173
1174     pub fn completion(&self) -> CompletionConfig {
1175         CompletionConfig {
1176             enable_postfix_completions: self.data.completion_postfix_enable,
1177             enable_imports_on_the_fly: self.data.completion_autoimport_enable
1178                 && completion_item_edit_resolve(&self.caps),
1179             enable_self_on_the_fly: self.data.completion_autoself_enable,
1180             enable_private_editable: self.data.completion_privateEditable_enable,
1181             callable: match self.data.completion_callable_snippets {
1182                 CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments),
1183                 CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses),
1184                 CallableCompletionDef::None => None,
1185             },
1186             insert_use: self.insert_use_config(),
1187             prefer_no_std: self.data.imports_prefer_no_std,
1188             snippet_cap: SnippetCap::new(try_or_def!(
1189                 self.caps
1190                     .text_document
1191                     .as_ref()?
1192                     .completion
1193                     .as_ref()?
1194                     .completion_item
1195                     .as_ref()?
1196                     .snippet_support?
1197             )),
1198             snippets: self.snippets.clone(),
1199         }
1200     }
1201
1202     pub fn find_all_refs_exclude_imports(&self) -> bool {
1203         self.data.references_excludeImports
1204     }
1205
1206     pub fn snippet_cap(&self) -> bool {
1207         self.experimental("snippetTextEdit")
1208     }
1209
1210     pub fn assist(&self) -> AssistConfig {
1211         AssistConfig {
1212             snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
1213             allowed: None,
1214             insert_use: self.insert_use_config(),
1215             prefer_no_std: self.data.imports_prefer_no_std,
1216         }
1217     }
1218
1219     pub fn join_lines(&self) -> JoinLinesConfig {
1220         JoinLinesConfig {
1221             join_else_if: self.data.joinLines_joinElseIf,
1222             remove_trailing_comma: self.data.joinLines_removeTrailingComma,
1223             unwrap_trivial_blocks: self.data.joinLines_unwrapTrivialBlock,
1224             join_assignments: self.data.joinLines_joinAssignments,
1225         }
1226     }
1227
1228     pub fn call_info(&self) -> CallInfoConfig {
1229         CallInfoConfig {
1230             params_only: matches!(self.data.signatureInfo_detail, SignatureDetail::Parameters),
1231             docs: self.data.signatureInfo_documentation_enable,
1232         }
1233     }
1234
1235     pub fn lens(&self) -> LensConfig {
1236         LensConfig {
1237             run: self.data.lens_enable && self.data.lens_run_enable,
1238             debug: self.data.lens_enable && self.data.lens_debug_enable,
1239             implementations: self.data.lens_enable && self.data.lens_implementations_enable,
1240             method_refs: self.data.lens_enable && self.data.lens_references_method_enable,
1241             refs_adt: self.data.lens_enable && self.data.lens_references_adt_enable,
1242             refs_trait: self.data.lens_enable && self.data.lens_references_trait_enable,
1243             enum_variant_refs: self.data.lens_enable
1244                 && self.data.lens_references_enumVariant_enable,
1245             location: self.data.lens_location,
1246         }
1247     }
1248
1249     pub fn hover_actions(&self) -> HoverActionsConfig {
1250         let enable = self.experimental("hoverActions") && self.data.hover_actions_enable;
1251         HoverActionsConfig {
1252             implementations: enable && self.data.hover_actions_implementations_enable,
1253             references: enable && self.data.hover_actions_references_enable,
1254             run: enable && self.data.hover_actions_run_enable,
1255             debug: enable && self.data.hover_actions_debug_enable,
1256             goto_type_def: enable && self.data.hover_actions_gotoTypeDef_enable,
1257         }
1258     }
1259
1260     pub fn highlighting_config(&self) -> HighlightConfig {
1261         HighlightConfig {
1262             strings: self.data.semanticHighlighting_strings_enable,
1263             punctuation: self.data.semanticHighlighting_punctuation_enable,
1264             specialize_punctuation: self
1265                 .data
1266                 .semanticHighlighting_punctuation_specialization_enable,
1267             macro_bang: self.data.semanticHighlighting_punctuation_separate_macro_bang,
1268             operator: self.data.semanticHighlighting_operator_enable,
1269             specialize_operator: self.data.semanticHighlighting_operator_specialization_enable,
1270             inject_doc_comment: self.data.semanticHighlighting_doc_comment_inject_enable,
1271             syntactic_name_ref_highlighting: false,
1272         }
1273     }
1274
1275     pub fn hover(&self) -> HoverConfig {
1276         HoverConfig {
1277             links_in_hover: self.data.hover_links_enable,
1278             documentation: self.data.hover_documentation_enable.then(|| {
1279                 let is_markdown = try_or_def!(self
1280                     .caps
1281                     .text_document
1282                     .as_ref()?
1283                     .hover
1284                     .as_ref()?
1285                     .content_format
1286                     .as_ref()?
1287                     .as_slice())
1288                 .contains(&MarkupKind::Markdown);
1289                 if is_markdown {
1290                     HoverDocFormat::Markdown
1291                 } else {
1292                     HoverDocFormat::PlainText
1293                 }
1294             }),
1295             keywords: self.data.hover_documentation_keywords_enable,
1296         }
1297     }
1298
1299     pub fn workspace_symbol(&self) -> WorkspaceSymbolConfig {
1300         WorkspaceSymbolConfig {
1301             search_scope: match self.data.workspace_symbol_search_scope {
1302                 WorkspaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace,
1303                 WorkspaceSymbolSearchScopeDef::WorkspaceAndDependencies => {
1304                     WorkspaceSymbolSearchScope::WorkspaceAndDependencies
1305                 }
1306             },
1307             search_kind: match self.data.workspace_symbol_search_kind {
1308                 WorkspaceSymbolSearchKindDef::OnlyTypes => WorkspaceSymbolSearchKind::OnlyTypes,
1309                 WorkspaceSymbolSearchKindDef::AllSymbols => WorkspaceSymbolSearchKind::AllSymbols,
1310             },
1311             search_limit: self.data.workspace_symbol_search_limit,
1312         }
1313     }
1314
1315     pub fn semantic_tokens_refresh(&self) -> bool {
1316         try_or_def!(self.caps.workspace.as_ref()?.semantic_tokens.as_ref()?.refresh_support?)
1317     }
1318
1319     pub fn code_lens_refresh(&self) -> bool {
1320         try_or_def!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?)
1321     }
1322
1323     pub fn insert_replace_support(&self) -> bool {
1324         try_or_def!(
1325             self.caps
1326                 .text_document
1327                 .as_ref()?
1328                 .completion
1329                 .as_ref()?
1330                 .completion_item
1331                 .as_ref()?
1332                 .insert_replace_support?
1333         )
1334     }
1335
1336     pub fn client_commands(&self) -> ClientCommandsConfig {
1337         let commands =
1338             try_or!(self.caps.experimental.as_ref()?.get("commands")?, &serde_json::Value::Null);
1339         let commands: Option<lsp_ext::ClientCommandOptions> =
1340             serde_json::from_value(commands.clone()).ok();
1341         let force = commands.is_none() && self.data.lens_forceCustomCommands;
1342         let commands = commands.map(|it| it.commands).unwrap_or_default();
1343
1344         let get = |name: &str| commands.iter().any(|it| it == name) || force;
1345
1346         ClientCommandsConfig {
1347             run_single: get("rust-analyzer.runSingle"),
1348             debug_single: get("rust-analyzer.debugSingle"),
1349             show_reference: get("rust-analyzer.showReferences"),
1350             goto_location: get("rust-analyzer.gotoLocation"),
1351             trigger_parameter_hints: get("editor.action.triggerParameterHints"),
1352         }
1353     }
1354
1355     pub fn highlight_related(&self) -> HighlightRelatedConfig {
1356         HighlightRelatedConfig {
1357             references: self.data.highlightRelated_references_enable,
1358             break_points: self.data.highlightRelated_breakPoints_enable,
1359             exit_points: self.data.highlightRelated_exitPoints_enable,
1360             yield_points: self.data.highlightRelated_yieldPoints_enable,
1361         }
1362     }
1363
1364     pub fn prime_caches_num_threads(&self) -> u8 {
1365         match self.data.cachePriming_numThreads {
1366             0 => num_cpus::get_physical().try_into().unwrap_or(u8::MAX),
1367             n => n,
1368         }
1369     }
1370
1371     pub fn typing_autoclose_angle(&self) -> bool {
1372         self.data.typing_autoClosingAngleBrackets_enable
1373     }
1374 }
1375 // Deserialization definitions
1376
1377 macro_rules! create_bool_or_string_de {
1378     ($ident:ident<$bool:literal, $string:literal>) => {
1379         fn $ident<'de, D>(d: D) -> Result<(), D::Error>
1380         where
1381             D: serde::Deserializer<'de>,
1382         {
1383             struct V;
1384             impl<'de> serde::de::Visitor<'de> for V {
1385                 type Value = ();
1386
1387                 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1388                     formatter.write_str(concat!(
1389                         stringify!($bool),
1390                         " or \"",
1391                         stringify!($string),
1392                         "\""
1393                     ))
1394                 }
1395
1396                 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
1397                 where
1398                     E: serde::de::Error,
1399                 {
1400                     match v {
1401                         $bool => Ok(()),
1402                         _ => Err(serde::de::Error::invalid_value(
1403                             serde::de::Unexpected::Bool(v),
1404                             &self,
1405                         )),
1406                     }
1407                 }
1408
1409                 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
1410                 where
1411                     E: serde::de::Error,
1412                 {
1413                     match v {
1414                         $string => Ok(()),
1415                         _ => Err(serde::de::Error::invalid_value(
1416                             serde::de::Unexpected::Str(v),
1417                             &self,
1418                         )),
1419                     }
1420                 }
1421
1422                 fn visit_enum<A>(self, a: A) -> Result<Self::Value, A::Error>
1423                 where
1424                     A: serde::de::EnumAccess<'de>,
1425                 {
1426                     use serde::de::VariantAccess;
1427                     let (variant, va) = a.variant::<&'de str>()?;
1428                     va.unit_variant()?;
1429                     match variant {
1430                         $string => Ok(()),
1431                         _ => Err(serde::de::Error::invalid_value(
1432                             serde::de::Unexpected::Str(variant),
1433                             &self,
1434                         )),
1435                     }
1436                 }
1437             }
1438             d.deserialize_any(V)
1439         }
1440     };
1441 }
1442 create_bool_or_string_de!(true_or_always<true, "always">);
1443 create_bool_or_string_de!(false_or_never<false, "never">);
1444
1445 macro_rules! named_unit_variant {
1446     ($variant:ident) => {
1447         pub(super) fn $variant<'de, D>(deserializer: D) -> Result<(), D::Error>
1448         where
1449             D: serde::Deserializer<'de>,
1450         {
1451             struct V;
1452             impl<'de> serde::de::Visitor<'de> for V {
1453                 type Value = ();
1454                 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1455                     f.write_str(concat!("\"", stringify!($variant), "\""))
1456                 }
1457                 fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
1458                     if value == stringify!($variant) {
1459                         Ok(())
1460                     } else {
1461                         Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
1462                     }
1463                 }
1464             }
1465             deserializer.deserialize_str(V)
1466         }
1467     };
1468 }
1469
1470 mod de_unit_v {
1471     named_unit_variant!(all);
1472     named_unit_variant!(skip_trivial);
1473     named_unit_variant!(mutable);
1474     named_unit_variant!(with_block);
1475 }
1476
1477 #[derive(Deserialize, Debug, Clone, Copy)]
1478 #[serde(rename_all = "snake_case")]
1479 enum SnippetScopeDef {
1480     Expr,
1481     Item,
1482     Type,
1483 }
1484
1485 impl Default for SnippetScopeDef {
1486     fn default() -> Self {
1487         SnippetScopeDef::Expr
1488     }
1489 }
1490
1491 #[derive(Deserialize, Debug, Clone, Default)]
1492 #[serde(default)]
1493 struct SnippetDef {
1494     #[serde(deserialize_with = "single_or_array")]
1495     prefix: Vec<String>,
1496     #[serde(deserialize_with = "single_or_array")]
1497     postfix: Vec<String>,
1498     description: Option<String>,
1499     #[serde(deserialize_with = "single_or_array")]
1500     body: Vec<String>,
1501     #[serde(deserialize_with = "single_or_array")]
1502     requires: Vec<String>,
1503     scope: SnippetScopeDef,
1504 }
1505
1506 fn single_or_array<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
1507 where
1508     D: serde::Deserializer<'de>,
1509 {
1510     struct SingleOrVec;
1511
1512     impl<'de> serde::de::Visitor<'de> for SingleOrVec {
1513         type Value = Vec<String>;
1514
1515         fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1516             formatter.write_str("string or array of strings")
1517         }
1518
1519         fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
1520         where
1521             E: serde::de::Error,
1522         {
1523             Ok(vec![value.to_owned()])
1524         }
1525
1526         fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
1527         where
1528             A: serde::de::SeqAccess<'de>,
1529         {
1530             Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(seq))
1531         }
1532     }
1533
1534     deserializer.deserialize_any(SingleOrVec)
1535 }
1536
1537 #[derive(Deserialize, Debug, Clone)]
1538 #[serde(untagged)]
1539 enum ManifestOrProjectJson {
1540     Manifest(PathBuf),
1541     ProjectJson(ProjectJsonData),
1542 }
1543
1544 #[derive(Deserialize, Debug, Clone)]
1545 #[serde(rename_all = "snake_case")]
1546 enum ExprFillDefaultDef {
1547     Todo,
1548     Default,
1549 }
1550
1551 #[derive(Deserialize, Debug, Clone)]
1552 #[serde(rename_all = "snake_case")]
1553 enum ImportGranularityDef {
1554     Preserve,
1555     Item,
1556     Crate,
1557     Module,
1558 }
1559
1560 #[derive(Deserialize, Debug, Copy, Clone)]
1561 #[serde(rename_all = "snake_case")]
1562 enum CallableCompletionDef {
1563     FillArguments,
1564     AddParentheses,
1565     None,
1566 }
1567
1568 #[derive(Deserialize, Debug, Clone)]
1569 #[serde(untagged)]
1570 enum CargoFeaturesDef {
1571     #[serde(deserialize_with = "de_unit_v::all")]
1572     All,
1573     Selected(Vec<String>),
1574 }
1575
1576 #[derive(Deserialize, Debug, Clone)]
1577 #[serde(untagged)]
1578 enum LifetimeElisionDef {
1579     #[serde(deserialize_with = "true_or_always")]
1580     Always,
1581     #[serde(deserialize_with = "false_or_never")]
1582     Never,
1583     #[serde(deserialize_with = "de_unit_v::skip_trivial")]
1584     SkipTrivial,
1585 }
1586
1587 #[derive(Deserialize, Debug, Clone)]
1588 #[serde(untagged)]
1589 enum ClosureReturnTypeHintsDef {
1590     #[serde(deserialize_with = "true_or_always")]
1591     Always,
1592     #[serde(deserialize_with = "false_or_never")]
1593     Never,
1594     #[serde(deserialize_with = "de_unit_v::with_block")]
1595     WithBlock,
1596 }
1597
1598 #[derive(Deserialize, Debug, Clone)]
1599 #[serde(untagged)]
1600 enum ReborrowHintsDef {
1601     #[serde(deserialize_with = "true_or_always")]
1602     Always,
1603     #[serde(deserialize_with = "false_or_never")]
1604     Never,
1605     #[serde(deserialize_with = "de_unit_v::mutable")]
1606     Mutable,
1607 }
1608
1609 #[derive(Deserialize, Debug, Clone)]
1610 #[serde(rename_all = "snake_case")]
1611 enum FilesWatcherDef {
1612     Client,
1613     Notify,
1614     Server,
1615 }
1616
1617 #[derive(Deserialize, Debug, Clone)]
1618 #[serde(rename_all = "snake_case")]
1619 enum ImportPrefixDef {
1620     Plain,
1621     #[serde(alias = "self")]
1622     BySelf,
1623     #[serde(alias = "crate")]
1624     ByCrate,
1625 }
1626
1627 #[derive(Deserialize, Debug, Clone)]
1628 #[serde(rename_all = "snake_case")]
1629 enum WorkspaceSymbolSearchScopeDef {
1630     Workspace,
1631     WorkspaceAndDependencies,
1632 }
1633
1634 #[derive(Deserialize, Debug, Clone)]
1635 #[serde(rename_all = "snake_case")]
1636 enum SignatureDetail {
1637     Full,
1638     Parameters,
1639 }
1640
1641 #[derive(Deserialize, Debug, Clone)]
1642 #[serde(rename_all = "snake_case")]
1643 enum WorkspaceSymbolSearchKindDef {
1644     OnlyTypes,
1645     AllSymbols,
1646 }
1647
1648 macro_rules! _config_data {
1649     (struct $name:ident {
1650         $(
1651             $(#[doc=$doc:literal])*
1652             $field:ident $(| $alias:ident)*: $ty:ty = $default:expr,
1653         )*
1654     }) => {
1655         #[allow(non_snake_case)]
1656         #[derive(Debug, Clone)]
1657         struct $name { $($field: $ty,)* }
1658         impl $name {
1659             fn from_json(mut json: serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>) -> $name {
1660                 $name {$(
1661                     $field: get_field(
1662                         &mut json,
1663                         error_sink,
1664                         stringify!($field),
1665                         None$(.or(Some(stringify!($alias))))*,
1666                         $default,
1667                     ),
1668                 )*}
1669             }
1670
1671             fn json_schema() -> serde_json::Value {
1672                 schema(&[
1673                     $({
1674                         let field = stringify!($field);
1675                         let ty = stringify!($ty);
1676
1677                         (field, ty, &[$($doc),*], $default)
1678                     },)*
1679                 ])
1680             }
1681
1682             #[cfg(test)]
1683             fn manual() -> String {
1684                 manual(&[
1685                     $({
1686                         let field = stringify!($field);
1687                         let ty = stringify!($ty);
1688
1689                         (field, ty, &[$($doc),*], $default)
1690                     },)*
1691                 ])
1692             }
1693         }
1694
1695         #[test]
1696         fn fields_are_sorted() {
1697             [$(stringify!($field)),*].windows(2).for_each(|w| assert!(w[0] <= w[1], "{} <= {} does not hold", w[0], w[1]));
1698         }
1699     };
1700 }
1701 use _config_data as config_data;
1702
1703 fn get_field<T: DeserializeOwned>(
1704     json: &mut serde_json::Value,
1705     error_sink: &mut Vec<(String, serde_json::Error)>,
1706     field: &'static str,
1707     alias: Option<&'static str>,
1708     default: &str,
1709 ) -> T {
1710     let default = serde_json::from_str(default).unwrap();
1711     // XXX: check alias first, to work-around the VS Code where it pre-fills the
1712     // defaults instead of sending an empty object.
1713     alias
1714         .into_iter()
1715         .chain(iter::once(field))
1716         .find_map(move |field| {
1717             let mut pointer = field.replace('_', "/");
1718             pointer.insert(0, '/');
1719             json.pointer_mut(&pointer).and_then(|it| match serde_json::from_value(it.take()) {
1720                 Ok(it) => Some(it),
1721                 Err(e) => {
1722                     tracing::warn!("Failed to deserialize config field at {}: {:?}", pointer, e);
1723                     error_sink.push((pointer, e));
1724                     None
1725                 }
1726             })
1727         })
1728         .unwrap_or(default)
1729 }
1730
1731 fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json::Value {
1732     for ((f1, ..), (f2, ..)) in fields.iter().zip(&fields[1..]) {
1733         fn key(f: &str) -> &str {
1734             f.splitn(2, '_').next().unwrap()
1735         }
1736         assert!(key(f1) <= key(f2), "wrong field order: {:?} {:?}", f1, f2);
1737     }
1738
1739     let map = fields
1740         .iter()
1741         .map(|(field, ty, doc, default)| {
1742             let name = field.replace('_', ".");
1743             let name = format!("rust-analyzer.{}", name);
1744             let props = field_props(field, ty, doc, default);
1745             (name, props)
1746         })
1747         .collect::<serde_json::Map<_, _>>();
1748     map.into()
1749 }
1750
1751 fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value {
1752     let doc = doc_comment_to_string(doc);
1753     let doc = doc.trim_end_matches('\n');
1754     assert!(
1755         doc.ends_with('.') && doc.starts_with(char::is_uppercase),
1756         "bad docs for {}: {:?}",
1757         field,
1758         doc
1759     );
1760     let default = default.parse::<serde_json::Value>().unwrap();
1761
1762     let mut map = serde_json::Map::default();
1763     macro_rules! set {
1764         ($($key:literal: $value:tt),*$(,)?) => {{$(
1765             map.insert($key.into(), serde_json::json!($value));
1766         )*}};
1767     }
1768     set!("markdownDescription": doc);
1769     set!("default": default);
1770
1771     match ty {
1772         "bool" => set!("type": "boolean"),
1773         "usize" => set!("type": "integer", "minimum": 0),
1774         "String" => set!("type": "string"),
1775         "Vec<String>" => set! {
1776             "type": "array",
1777             "items": { "type": "string" },
1778         },
1779         "Vec<PathBuf>" => set! {
1780             "type": "array",
1781             "items": { "type": "string" },
1782         },
1783         "FxHashSet<String>" => set! {
1784             "type": "array",
1785             "items": { "type": "string" },
1786             "uniqueItems": true,
1787         },
1788         "FxHashMap<Box<str>, Box<[Box<str>]>>" => set! {
1789             "type": "object",
1790         },
1791         "FxHashMap<String, SnippetDef>" => set! {
1792             "type": "object",
1793         },
1794         "FxHashMap<String, String>" => set! {
1795             "type": "object",
1796         },
1797         "Option<usize>" => set! {
1798             "type": ["null", "integer"],
1799             "minimum": 0,
1800         },
1801         "Option<String>" => set! {
1802             "type": ["null", "string"],
1803         },
1804         "Option<PathBuf>" => set! {
1805             "type": ["null", "string"],
1806         },
1807         "Option<bool>" => set! {
1808             "type": ["null", "boolean"],
1809         },
1810         "Option<Vec<String>>" => set! {
1811             "type": ["null", "array"],
1812             "items": { "type": "string" },
1813         },
1814         "MergeBehaviorDef" => set! {
1815             "type": "string",
1816             "enum": ["none", "crate", "module"],
1817             "enumDescriptions": [
1818                 "Do not merge imports at all.",
1819                 "Merge imports from the same crate into a single `use` statement.",
1820                 "Merge imports from the same module into a single `use` statement."
1821             ],
1822         },
1823         "ExprFillDefaultDef" => set! {
1824             "type": "string",
1825             "enum": ["todo", "default"],
1826             "enumDescriptions": [
1827                 "Fill missing expressions with the `todo` macro",
1828                 "Fill missing expressions with reasonable defaults, `new` or `default` constructors."
1829             ],
1830         },
1831         "ImportGranularityDef" => set! {
1832             "type": "string",
1833             "enum": ["preserve", "crate", "module", "item"],
1834             "enumDescriptions": [
1835                 "Do not change the granularity of any imports and preserve the original structure written by the developer.",
1836                 "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.",
1837                 "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.",
1838                 "Flatten imports so that each has its own use statement."
1839             ],
1840         },
1841         "ImportPrefixDef" => set! {
1842             "type": "string",
1843             "enum": [
1844                 "plain",
1845                 "self",
1846                 "crate"
1847             ],
1848             "enumDescriptions": [
1849                 "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item.",
1850                 "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item. Prefixes `self` in front of the path if it starts with a module.",
1851                 "Force import paths to be absolute by always starting them with `crate` or the extern crate name they come from."
1852             ],
1853         },
1854         "Vec<ManifestOrProjectJson>" => set! {
1855             "type": "array",
1856             "items": { "type": ["string", "object"] },
1857         },
1858         "WorkspaceSymbolSearchScopeDef" => set! {
1859             "type": "string",
1860             "enum": ["workspace", "workspace_and_dependencies"],
1861             "enumDescriptions": [
1862                 "Search in current workspace only.",
1863                 "Search in current workspace and dependencies."
1864             ],
1865         },
1866         "WorkspaceSymbolSearchKindDef" => set! {
1867             "type": "string",
1868             "enum": ["only_types", "all_symbols"],
1869             "enumDescriptions": [
1870                 "Search for types only.",
1871                 "Search for all symbols kinds."
1872             ],
1873         },
1874         "ParallelCachePrimingNumThreads" => set! {
1875             "type": "number",
1876             "minimum": 0,
1877             "maximum": 255
1878         },
1879         "LifetimeElisionDef" => set! {
1880             "type": "string",
1881             "enum": [
1882                 "always",
1883                 "never",
1884                 "skip_trivial"
1885             ],
1886             "enumDescriptions": [
1887                 "Always show lifetime elision hints.",
1888                 "Never show lifetime elision hints.",
1889                 "Only show lifetime elision hints if a return type is involved."
1890             ]
1891         },
1892         "ClosureReturnTypeHintsDef" => set! {
1893             "type": "string",
1894             "enum": [
1895                 "always",
1896                 "never",
1897                 "with_block"
1898             ],
1899             "enumDescriptions": [
1900                 "Always show type hints for return types of closures.",
1901                 "Never show type hints for return types of closures.",
1902                 "Only show type hints for return types of closures with blocks."
1903             ]
1904         },
1905         "ReborrowHintsDef" => set! {
1906             "type": "string",
1907             "enum": [
1908                 "always",
1909                 "never",
1910                 "mutable"
1911             ],
1912             "enumDescriptions": [
1913                 "Always show reborrow hints.",
1914                 "Never show reborrow hints.",
1915                 "Only show mutable reborrow hints."
1916             ]
1917         },
1918         "CargoFeaturesDef" => set! {
1919             "anyOf": [
1920                 {
1921                     "type": "string",
1922                     "enum": [
1923                         "all"
1924                     ],
1925                     "enumDescriptions": [
1926                         "Pass `--all-features` to cargo",
1927                     ]
1928                 },
1929                 {
1930                     "type": "array",
1931                     "items": { "type": "string" }
1932                 }
1933             ],
1934         },
1935         "Option<CargoFeaturesDef>" => set! {
1936             "anyOf": [
1937                 {
1938                     "type": "string",
1939                     "enum": [
1940                         "all"
1941                     ],
1942                     "enumDescriptions": [
1943                         "Pass `--all-features` to cargo",
1944                     ]
1945                 },
1946                 {
1947                     "type": "array",
1948                     "items": { "type": "string" }
1949                 },
1950                 { "type": "null" }
1951             ],
1952         },
1953         "CallableCompletionDef" => set! {
1954             "type": "string",
1955             "enum": [
1956                 "fill_arguments",
1957                 "add_parentheses",
1958                 "none",
1959             ],
1960             "enumDescriptions": [
1961                 "Add call parentheses and pre-fill arguments.",
1962                 "Add call parentheses.",
1963                 "Do no snippet completions for callables."
1964             ]
1965         },
1966         "SignatureDetail" => set! {
1967             "type": "string",
1968             "enum": ["full", "parameters"],
1969             "enumDescriptions": [
1970                 "Show the entire signature.",
1971                 "Show only the parameters."
1972             ],
1973         },
1974         "FilesWatcherDef" => set! {
1975             "type": "string",
1976             "enum": ["client", "server"],
1977             "enumDescriptions": [
1978                 "Use the client (editor) to watch files for changes",
1979                 "Use server-side file watching",
1980             ],
1981         },
1982         "AnnotationLocation" => set! {
1983             "type": "string",
1984             "enum": ["above_name", "above_whole_item"],
1985             "enumDescriptions": [
1986                 "Render annotations above the name of the item.",
1987                 "Render annotations above the whole item, including documentation comments and attributes."
1988             ],
1989         },
1990         _ => panic!("missing entry for {}: {}", ty, default),
1991     }
1992
1993     map.into()
1994 }
1995
1996 #[cfg(test)]
1997 fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String {
1998     fields
1999         .iter()
2000         .map(|(field, _ty, doc, default)| {
2001             let name = format!("rust-analyzer.{}", field.replace('_', "."));
2002             let doc = doc_comment_to_string(*doc);
2003             if default.contains('\n') {
2004                 format!(
2005                     r#"[[{}]]{}::
2006 +
2007 --
2008 Default:
2009 ----
2010 {}
2011 ----
2012 {}
2013 --
2014 "#,
2015                     name, name, default, doc
2016                 )
2017             } else {
2018                 format!("[[{}]]{} (default: `{}`)::\n+\n--\n{}--\n", name, name, default, doc)
2019             }
2020         })
2021         .collect::<String>()
2022 }
2023
2024 fn doc_comment_to_string(doc: &[&str]) -> String {
2025     doc.iter().map(|it| it.strip_prefix(' ').unwrap_or(it)).map(|it| format!("{}\n", it)).collect()
2026 }
2027
2028 #[cfg(test)]
2029 mod tests {
2030     use std::fs;
2031
2032     use test_utils::{ensure_file_contents, project_root};
2033
2034     use super::*;
2035
2036     #[test]
2037     fn generate_package_json_config() {
2038         let s = Config::json_schema();
2039         let schema = format!("{:#}", s);
2040         let mut schema = schema
2041             .trim_start_matches('{')
2042             .trim_end_matches('}')
2043             .replace("  ", "    ")
2044             .replace('\n', "\n            ")
2045             .trim_start_matches('\n')
2046             .trim_end()
2047             .to_string();
2048         schema.push_str(",\n");
2049
2050         // Transform the asciidoc form link to markdown style.
2051         //
2052         // https://link[text] => [text](https://link)
2053         let url_matches = schema.match_indices("https://");
2054         let mut url_offsets = url_matches.map(|(idx, _)| idx).collect::<Vec<usize>>();
2055         url_offsets.reverse();
2056         for idx in url_offsets {
2057             let link = &schema[idx..];
2058             // matching on whitespace to ignore normal links
2059             if let Some(link_end) = link.find(|c| c == ' ' || c == '[') {
2060                 if link.chars().nth(link_end) == Some('[') {
2061                     if let Some(link_text_end) = link.find(']') {
2062                         let link_text = link[link_end..(link_text_end + 1)].to_string();
2063
2064                         schema.replace_range((idx + link_end)..(idx + link_text_end + 1), "");
2065                         schema.insert(idx, '(');
2066                         schema.insert(idx + link_end + 1, ')');
2067                         schema.insert_str(idx, &link_text);
2068                     }
2069                 }
2070             }
2071         }
2072
2073         let package_json_path = project_root().join("editors/code/package.json");
2074         let mut package_json = fs::read_to_string(&package_json_path).unwrap();
2075
2076         let start_marker = "                \"$generated-start\": {},\n";
2077         let end_marker = "                \"$generated-end\": {}\n";
2078
2079         let start = package_json.find(start_marker).unwrap() + start_marker.len();
2080         let end = package_json.find(end_marker).unwrap();
2081
2082         let p = remove_ws(&package_json[start..end]);
2083         let s = remove_ws(&schema);
2084         if !p.contains(&s) {
2085             package_json.replace_range(start..end, &schema);
2086             ensure_file_contents(&package_json_path, &package_json)
2087         }
2088     }
2089
2090     #[test]
2091     fn generate_config_documentation() {
2092         let docs_path = project_root().join("docs/user/generated_config.adoc");
2093         let expected = ConfigData::manual();
2094         ensure_file_contents(&docs_path, &expected);
2095     }
2096
2097     fn remove_ws(text: &str) -> String {
2098         text.replace(char::is_whitespace, "")
2099     }
2100 }