• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::{fmt, iter, ops::Not, path::PathBuf};
11 
12 use cfg::{CfgAtom, CfgDiff};
13 use flycheck::FlycheckConfig;
14 use ide::{
15     AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
16     HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig,
17     JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope,
18 };
19 use ide_db::{
20     imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
21     SnippetCap,
22 };
23 use itertools::Itertools;
24 use lsp_types::{ClientCapabilities, MarkupKind};
25 use project_model::{
26     CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource,
27 };
28 use rustc_hash::{FxHashMap, FxHashSet};
29 use serde::{de::DeserializeOwned, Deserialize};
30 use vfs::{AbsPath, AbsPathBuf};
31 
32 use crate::{
33     caps::completion_item_edit_resolve,
34     diagnostics::DiagnosticsMapConfig,
35     line_index::PositionEncoding,
36     lsp_ext::{self, negotiated_encoding, 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         /// Whether to insert #[must_use] when generating `as_` methods
60         /// for enum variants.
61         assist_emitMustUse: bool               = "false",
62         /// Placeholder expression to use for missing expressions in assists.
63         assist_expressionFillDefault: ExprFillDefaultDef              = "\"todo\"",
64 
65         /// Warm up caches on project load.
66         cachePriming_enable: bool = "true",
67         /// How many worker threads to handle priming caches. The default `0` means to pick automatically.
68         cachePriming_numThreads: ParallelCachePrimingNumThreads = "0",
69 
70         /// Automatically refresh project info via `cargo metadata` on
71         /// `Cargo.toml` or `.cargo/config.toml` changes.
72         cargo_autoreload: bool           = "true",
73         /// Run build scripts (`build.rs`) for more precise code analysis.
74         cargo_buildScripts_enable: bool  = "true",
75         /// Specifies the working directory for running build scripts.
76         /// - "workspace": run build scripts for a workspace in the workspace's root directory.
77         ///   This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.
78         /// - "root": run build scripts in the project's root directory.
79         /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
80         /// is set.
81         cargo_buildScripts_invocationLocation: InvocationLocation = "\"workspace\"",
82         /// Specifies the invocation strategy to use when running the build scripts command.
83         /// If `per_workspace` is set, the command will be executed for each workspace.
84         /// If `once` is set, the command will be executed once.
85         /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
86         /// is set.
87         cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
88         /// Override the command rust-analyzer uses to run build scripts and
89         /// build procedural macros. The command is required to output json
90         /// and should therefore include `--message-format=json` or a similar
91         /// option.
92         ///
93         /// By default, a cargo invocation will be constructed for the configured
94         /// targets and features, with the following base command line:
95         ///
96         /// ```bash
97         /// cargo check --quiet --workspace --message-format=json --all-targets
98         /// ```
99         /// .
100         cargo_buildScripts_overrideCommand: Option<Vec<String>> = "null",
101         /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
102         /// avoid checking unnecessary things.
103         cargo_buildScripts_useRustcWrapper: bool = "true",
104         /// List of cfg options to enable with the given values.
105         cargo_cfgs: FxHashMap<String, String> = "{}",
106         /// Extra arguments that are passed to every cargo invocation.
107         cargo_extraArgs: Vec<String> = "[]",
108         /// Extra environment variables that will be set when running cargo, rustc
109         /// or other commands within the workspace. Useful for setting RUSTFLAGS.
110         cargo_extraEnv: FxHashMap<String, String> = "{}",
111         /// List of features to activate.
112         ///
113         /// Set this to `"all"` to pass `--all-features` to cargo.
114         cargo_features: CargoFeaturesDef      = "[]",
115         /// Whether to pass `--no-default-features` to cargo.
116         cargo_noDefaultFeatures: bool    = "false",
117         /// Relative path to the sysroot, or "discover" to try to automatically find it via
118         /// "rustc --print sysroot".
119         ///
120         /// Unsetting this disables sysroot loading.
121         ///
122         /// This option does not take effect until rust-analyzer is restarted.
123         cargo_sysroot: Option<String>    = "\"discover\"",
124         /// Relative path to the sysroot library sources. If left unset, this will default to
125         /// `{cargo.sysroot}/lib/rustlib/src/rust/library`.
126         ///
127         /// This option does not take effect until rust-analyzer is restarted.
128         cargo_sysrootSrc: Option<String>    = "null",
129         /// Compilation target override (target triple).
130         // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work
131         // than `checkOnSave_target`
132         cargo_target: Option<String>     = "null",
133         /// Unsets the implicit `#[cfg(test)]` for the specified crates.
134         cargo_unsetTest: Vec<String>     = "[\"core\"]",
135 
136         /// Run the check command for diagnostics on save.
137         checkOnSave | checkOnSave_enable: bool                         = "true",
138 
139         /// Check all targets and tests (`--all-targets`).
140         check_allTargets | checkOnSave_allTargets: bool                  = "true",
141         /// Cargo command to use for `cargo check`.
142         check_command | checkOnSave_command: String                      = "\"check\"",
143         /// Extra arguments for `cargo check`.
144         check_extraArgs | checkOnSave_extraArgs: Vec<String>             = "[]",
145         /// Extra environment variables that will be set when running `cargo check`.
146         /// Extends `#rust-analyzer.cargo.extraEnv#`.
147         check_extraEnv | checkOnSave_extraEnv: FxHashMap<String, String> = "{}",
148         /// List of features to activate. Defaults to
149         /// `#rust-analyzer.cargo.features#`.
150         ///
151         /// Set to `"all"` to pass `--all-features` to Cargo.
152         check_features | checkOnSave_features: Option<CargoFeaturesDef>  = "null",
153         /// Specifies the working directory for running checks.
154         /// - "workspace": run checks for workspaces in the corresponding workspaces' root directories.
155         // FIXME: Ideally we would support this in some way
156         ///   This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.
157         /// - "root": run checks in the project's root directory.
158         /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
159         /// is set.
160         check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"",
161         /// Specifies the invocation strategy to use when running the checkOnSave command.
162         /// If `per_workspace` is set, the command will be executed for each workspace.
163         /// If `once` is set, the command will be executed once.
164         /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
165         /// is set.
166         check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
167         /// Whether to pass `--no-default-features` to Cargo. Defaults to
168         /// `#rust-analyzer.cargo.noDefaultFeatures#`.
169         check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option<bool>         = "null",
170         /// Override the command rust-analyzer uses instead of `cargo check` for
171         /// diagnostics on save. The command is required to output json and
172         /// should therefore include `--message-format=json` or a similar option
173         /// (if your client supports the `colorDiagnosticOutput` experimental
174         /// capability, you can use `--message-format=json-diagnostic-rendered-ansi`).
175         ///
176         /// If you're changing this because you're using some tool wrapping
177         /// Cargo, you might also want to change
178         /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`.
179         ///
180         /// If there are multiple linked projects, this command is invoked for
181         /// each of them, with the working directory being the project root
182         /// (i.e., the folder containing the `Cargo.toml`).
183         ///
184         /// An example command would be:
185         ///
186         /// ```bash
187         /// cargo check --workspace --message-format=json --all-targets
188         /// ```
189         /// .
190         check_overrideCommand | checkOnSave_overrideCommand: Option<Vec<String>>             = "null",
191         /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.
192         ///
193         /// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g.
194         /// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`.
195         ///
196         /// Aliased as `"checkOnSave.targets"`.
197         check_targets | checkOnSave_targets | checkOnSave_target: Option<CheckOnSaveTargets> = "null",
198 
199         /// Toggles the additional completions that automatically add imports when completed.
200         /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
201         completion_autoimport_enable: bool       = "true",
202         /// Toggles the additional completions that automatically show method calls and field accesses
203         /// with `self` prefixed to them when inside a method.
204         completion_autoself_enable: bool        = "true",
205         /// Whether to add parenthesis and argument snippets when completing function.
206         completion_callable_snippets: CallableCompletionDef  = "\"fill_arguments\"",
207         /// Maximum number of completions to return. If `None`, the limit is infinite.
208         completion_limit: Option<usize> = "null",
209         /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
210         completion_postfix_enable: bool         = "true",
211         /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.
212         completion_privateEditable_enable: bool = "false",
213         /// Custom completion snippets.
214         // NOTE: Keep this list in sync with the feature docs of user snippets.
215         completion_snippets_custom: FxHashMap<String, SnippetDef> = r#"{
216             "Arc::new": {
217                 "postfix": "arc",
218                 "body": "Arc::new(${receiver})",
219                 "requires": "std::sync::Arc",
220                 "description": "Put the expression into an `Arc`",
221                 "scope": "expr"
222             },
223             "Rc::new": {
224                 "postfix": "rc",
225                 "body": "Rc::new(${receiver})",
226                 "requires": "std::rc::Rc",
227                 "description": "Put the expression into an `Rc`",
228                 "scope": "expr"
229             },
230             "Box::pin": {
231                 "postfix": "pinbox",
232                 "body": "Box::pin(${receiver})",
233                 "requires": "std::boxed::Box",
234                 "description": "Put the expression into a pinned `Box`",
235                 "scope": "expr"
236             },
237             "Ok": {
238                 "postfix": "ok",
239                 "body": "Ok(${receiver})",
240                 "description": "Wrap the expression in a `Result::Ok`",
241                 "scope": "expr"
242             },
243             "Err": {
244                 "postfix": "err",
245                 "body": "Err(${receiver})",
246                 "description": "Wrap the expression in a `Result::Err`",
247                 "scope": "expr"
248             },
249             "Some": {
250                 "postfix": "some",
251                 "body": "Some(${receiver})",
252                 "description": "Wrap the expression in an `Option::Some`",
253                 "scope": "expr"
254             }
255         }"#,
256 
257         /// List of rust-analyzer diagnostics to disable.
258         diagnostics_disabled: FxHashSet<String> = "[]",
259         /// Whether to show native rust-analyzer diagnostics.
260         diagnostics_enable: bool                = "true",
261         /// Whether to show experimental rust-analyzer diagnostics that might
262         /// have more false positives than usual.
263         diagnostics_experimental_enable: bool    = "false",
264         /// Map of prefixes to be substituted when parsing diagnostic file paths.
265         /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
266         diagnostics_remapPrefix: FxHashMap<String, String> = "{}",
267         /// List of warnings that should be displayed with hint severity.
268         ///
269         /// The warnings will be indicated by faded text or three dots in code
270         /// and will not show up in the `Problems Panel`.
271         diagnostics_warningsAsHint: Vec<String> = "[]",
272         /// List of warnings that should be displayed with info severity.
273         ///
274         /// The warnings will be indicated by a blue squiggly underline in code
275         /// and a blue icon in the `Problems Panel`.
276         diagnostics_warningsAsInfo: Vec<String> = "[]",
277         /// These directories will be ignored by rust-analyzer. They are
278         /// relative to the workspace root, and globs are not supported. You may
279         /// also need to add the folders to Code's `files.watcherExclude`.
280         files_excludeDirs: Vec<PathBuf> = "[]",
281         /// Controls file watching implementation.
282         files_watcher: FilesWatcherDef = "\"client\"",
283 
284         /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
285         highlightRelated_breakPoints_enable: bool = "true",
286         /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure.
287         highlightRelated_closureCaptures_enable: bool = "true",
288         /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
289         highlightRelated_exitPoints_enable: bool = "true",
290         /// Enables highlighting of related references while the cursor is on any identifier.
291         highlightRelated_references_enable: bool = "true",
292         /// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords.
293         highlightRelated_yieldPoints_enable: bool = "true",
294 
295         /// Whether to show `Debug` action. Only applies when
296         /// `#rust-analyzer.hover.actions.enable#` is set.
297         hover_actions_debug_enable: bool           = "true",
298         /// Whether to show HoverActions in Rust files.
299         hover_actions_enable: bool          = "true",
300         /// Whether to show `Go to Type Definition` action. Only applies when
301         /// `#rust-analyzer.hover.actions.enable#` is set.
302         hover_actions_gotoTypeDef_enable: bool     = "true",
303         /// Whether to show `Implementations` action. Only applies when
304         /// `#rust-analyzer.hover.actions.enable#` is set.
305         hover_actions_implementations_enable: bool = "true",
306         /// Whether to show `References` action. Only applies when
307         /// `#rust-analyzer.hover.actions.enable#` is set.
308         hover_actions_references_enable: bool      = "false",
309         /// Whether to show `Run` action. Only applies when
310         /// `#rust-analyzer.hover.actions.enable#` is set.
311         hover_actions_run_enable: bool             = "true",
312 
313         /// Whether to show documentation on hover.
314         hover_documentation_enable: bool           = "true",
315         /// Whether to show keyword hover popups. Only applies when
316         /// `#rust-analyzer.hover.documentation.enable#` is set.
317         hover_documentation_keywords_enable: bool  = "true",
318         /// Use markdown syntax for links on hover.
319         hover_links_enable: bool = "true",
320         /// How to render the align information in a memory layout hover.
321         hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = "\"hexadecimal\"",
322         /// Whether to show memory layout data on hover.
323         hover_memoryLayout_enable: bool = "true",
324         /// How to render the niche information in a memory layout hover.
325         hover_memoryLayout_niches: Option<bool> = "false",
326         /// How to render the offset information in a memory layout hover.
327         hover_memoryLayout_offset: Option<MemoryLayoutHoverRenderKindDef> = "\"hexadecimal\"",
328         /// How to render the size information in a memory layout hover.
329         hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = "\"both\"",
330 
331         /// 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.
332         imports_granularity_enforce: bool              = "false",
333         /// How imports should be grouped into use statements.
334         imports_granularity_group: ImportGranularityDef  = "\"crate\"",
335         /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.
336         imports_group_enable: bool                           = "true",
337         /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
338         imports_merge_glob: bool           = "true",
339         /// Prefer to unconditionally use imports of the core and alloc crate, over the std crate.
340         imports_prefer_no_std: bool                     = "false",
341         /// The path structure for newly inserted paths to use.
342         imports_prefix: ImportPrefixDef               = "\"plain\"",
343 
344         /// Whether to show inlay type hints for binding modes.
345         inlayHints_bindingModeHints_enable: bool                   = "false",
346         /// Whether to show inlay type hints for method chains.
347         inlayHints_chainingHints_enable: bool                      = "true",
348         /// Whether to show inlay hints after a closing `}` to indicate what item it belongs to.
349         inlayHints_closingBraceHints_enable: bool                  = "true",
350         /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
351         /// to always show them).
352         inlayHints_closingBraceHints_minLines: usize               = "25",
353         /// Whether to show inlay hints for closure captures.
354         inlayHints_closureCaptureHints_enable: bool                          = "false",
355         /// Whether to show inlay type hints for return types of closures.
356         inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef  = "\"never\"",
357         /// Closure notation in type and chaining inlay hints.
358         inlayHints_closureStyle: ClosureStyle                                = "\"impl_fn\"",
359         /// Whether to show enum variant discriminant hints.
360         inlayHints_discriminantHints_enable: DiscriminantHintsDef            = "\"never\"",
361         /// Whether to show inlay hints for type adjustments.
362         inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = "\"never\"",
363         /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.
364         inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = "false",
365         /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
366         inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = "\"prefix\"",
367         /// Whether to show inlay type hints for elided lifetimes in function signatures.
368         inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
369         /// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
370         inlayHints_lifetimeElisionHints_useParameterNames: bool    = "false",
371         /// Maximum length for inlay hints. Set to null to have an unlimited length.
372         inlayHints_maxLength: Option<usize>                        = "25",
373         /// Whether to show function parameter name inlay hints at the call
374         /// site.
375         inlayHints_parameterHints_enable: bool                     = "true",
376         /// Whether to show inlay hints for compiler inserted reborrows.
377         /// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.
378         inlayHints_reborrowHints_enable: ReborrowHintsDef          = "\"never\"",
379         /// Whether to render leading colons for type hints, and trailing colons for parameter hints.
380         inlayHints_renderColons: bool                              = "true",
381         /// Whether to show inlay type hints for variables.
382         inlayHints_typeHints_enable: bool                          = "true",
383         /// Whether to hide inlay type hints for `let` statements that initialize to a closure.
384         /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.
385         inlayHints_typeHints_hideClosureInitialization: bool       = "false",
386         /// Whether to hide inlay type hints for constructors.
387         inlayHints_typeHints_hideNamedConstructor: bool            = "false",
388         /// Enables the experimental support for interpreting tests.
389         interpret_tests: bool                                      = "false",
390 
391         /// Join lines merges consecutive declaration and initialization of an assignment.
392         joinLines_joinAssignments: bool = "true",
393         /// Join lines inserts else between consecutive ifs.
394         joinLines_joinElseIf: bool = "true",
395         /// Join lines removes trailing commas.
396         joinLines_removeTrailingComma: bool = "true",
397         /// Join lines unwraps trivial blocks.
398         joinLines_unwrapTrivialBlock: bool = "true",
399 
400 
401         /// Whether to show `Debug` lens. Only applies when
402         /// `#rust-analyzer.lens.enable#` is set.
403         lens_debug_enable: bool            = "true",
404         /// Whether to show CodeLens in Rust files.
405         lens_enable: bool           = "true",
406         /// Internal config: use custom client-side commands even when the
407         /// client doesn't set the corresponding capability.
408         lens_forceCustomCommands: bool = "true",
409         /// Whether to show `Implementations` lens. Only applies when
410         /// `#rust-analyzer.lens.enable#` is set.
411         lens_implementations_enable: bool  = "true",
412         /// Where to render annotations.
413         lens_location: AnnotationLocation = "\"above_name\"",
414         /// Whether to show `References` lens for Struct, Enum, and Union.
415         /// Only applies when `#rust-analyzer.lens.enable#` is set.
416         lens_references_adt_enable: bool = "false",
417         /// Whether to show `References` lens for Enum Variants.
418         /// Only applies when `#rust-analyzer.lens.enable#` is set.
419         lens_references_enumVariant_enable: bool = "false",
420         /// Whether to show `Method References` lens. Only applies when
421         /// `#rust-analyzer.lens.enable#` is set.
422         lens_references_method_enable: bool = "false",
423         /// Whether to show `References` lens for Trait.
424         /// Only applies when `#rust-analyzer.lens.enable#` is set.
425         lens_references_trait_enable: bool = "false",
426         /// Whether to show `Run` lens. Only applies when
427         /// `#rust-analyzer.lens.enable#` is set.
428         lens_run_enable: bool              = "true",
429 
430         /// Disable project auto-discovery in favor of explicitly specified set
431         /// of projects.
432         ///
433         /// Elements must be paths pointing to `Cargo.toml`,
434         /// `rust-project.json`, or JSON objects in `rust-project.json` format.
435         linkedProjects: Vec<ManifestOrProjectJson> = "[]",
436 
437         /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
438         lru_capacity: Option<usize>                 = "null",
439         /// Sets the LRU capacity of the specified queries.
440         lru_query_capacities: FxHashMap<Box<str>, usize> = "{}",
441 
442         /// Whether to show `can't find Cargo.toml` error message.
443         notifications_cargoTomlNotFound: bool      = "true",
444 
445         /// How many worker threads in the main loop. The default `null` means to pick automatically.
446         numThreads: Option<usize> = "null",
447 
448         /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
449         procMacro_attributes_enable: bool = "true",
450         /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
451         procMacro_enable: bool                     = "true",
452         /// These proc-macros will be ignored when trying to expand them.
453         ///
454         /// This config takes a map of crate names with the exported proc-macro names to ignore as values.
455         procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>>          = "{}",
456         /// Internal config, path to proc-macro server executable.
457         procMacro_server: Option<PathBuf>          = "null",
458 
459         /// Exclude imports from find-all-references.
460         references_excludeImports: bool = "false",
461 
462         /// Command to be executed instead of 'cargo' for runnables.
463         runnables_command: Option<String> = "null",
464         /// Additional arguments to be passed to cargo for runnables such as
465         /// tests or binaries. For example, it may be `--release`.
466         runnables_extraArgs: Vec<String>   = "[]",
467 
468         /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
469         /// projects, or "discover" to try to automatically find it if the `rustc-dev` component
470         /// is installed.
471         ///
472         /// Any project which uses rust-analyzer with the rustcPrivate
473         /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
474         ///
475         /// This option does not take effect until rust-analyzer is restarted.
476         rustc_source: Option<String> = "null",
477 
478         /// Additional arguments to `rustfmt`.
479         rustfmt_extraArgs: Vec<String>               = "[]",
480         /// Advanced option, fully override the command rust-analyzer uses for
481         /// formatting. This should be the equivalent of `rustfmt` here, and
482         /// not that of `cargo fmt`. The file contents will be passed on the
483         /// standard input and the formatted result will be read from the
484         /// standard output.
485         rustfmt_overrideCommand: Option<Vec<String>> = "null",
486         /// Enables the use of rustfmt's unstable range formatting command for the
487         /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
488         /// available on a nightly build.
489         rustfmt_rangeFormatting_enable: bool = "false",
490 
491         /// Inject additional highlighting into doc comments.
492         ///
493         /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
494         /// doc links.
495         semanticHighlighting_doc_comment_inject_enable: bool = "true",
496         /// Whether the server is allowed to emit non-standard tokens and modifiers.
497         semanticHighlighting_nonStandardTokens: bool = "true",
498         /// Use semantic tokens for operators.
499         ///
500         /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
501         /// they are tagged with modifiers.
502         semanticHighlighting_operator_enable: bool = "true",
503         /// Use specialized semantic tokens for operators.
504         ///
505         /// When enabled, rust-analyzer will emit special token types for operator tokens instead
506         /// of the generic `operator` token type.
507         semanticHighlighting_operator_specialization_enable: bool = "false",
508         /// Use semantic tokens for punctuation.
509         ///
510         /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
511         /// they are tagged with modifiers or have a special role.
512         semanticHighlighting_punctuation_enable: bool = "false",
513         /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
514         /// calls.
515         semanticHighlighting_punctuation_separate_macro_bang: bool = "false",
516         /// Use specialized semantic tokens for punctuation.
517         ///
518         /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead
519         /// of the generic `punctuation` token type.
520         semanticHighlighting_punctuation_specialization_enable: bool = "false",
521         /// Use semantic tokens for strings.
522         ///
523         /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
524         /// By disabling semantic tokens for strings, other grammars can be used to highlight
525         /// their contents.
526         semanticHighlighting_strings_enable: bool = "true",
527 
528         /// Show full signature of the callable. Only shows parameters if disabled.
529         signatureInfo_detail: SignatureDetail                           = "\"full\"",
530         /// Show documentation.
531         signatureInfo_documentation_enable: bool                       = "true",
532 
533         /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.
534         typing_autoClosingAngleBrackets_enable: bool = "false",
535 
536         /// Workspace symbol search kind.
537         workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = "\"only_types\"",
538         /// Limits the number of items returned from a workspace symbol search (Defaults to 128).
539         /// Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.
540         /// Other clients requires all results upfront and might require a higher limit.
541         workspace_symbol_search_limit: usize = "128",
542         /// Workspace symbol search scope.
543         workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = "\"workspace\"",
544     }
545 }
546 
547 impl Default for ConfigData {
default() -> Self548     fn default() -> Self {
549         ConfigData::from_json(serde_json::Value::Null, &mut Vec::new())
550     }
551 }
552 
553 #[derive(Debug, Clone)]
554 pub struct Config {
555     discovered_projects: Vec<ProjectManifest>,
556     /// The workspace roots as registered by the LSP client
557     workspace_roots: Vec<AbsPathBuf>,
558     caps: lsp_types::ClientCapabilities,
559     root_path: AbsPathBuf,
560     data: ConfigData,
561     detached_files: Vec<AbsPathBuf>,
562     snippets: Vec<Snippet>,
563 }
564 
565 type ParallelCachePrimingNumThreads = u8;
566 
567 #[derive(Debug, Clone, Eq, PartialEq)]
568 pub enum LinkedProject {
569     ProjectManifest(ProjectManifest),
570     InlineJsonProject(ProjectJson),
571 }
572 
573 impl From<ProjectManifest> for LinkedProject {
from(v: ProjectManifest) -> Self574     fn from(v: ProjectManifest) -> Self {
575         LinkedProject::ProjectManifest(v)
576     }
577 }
578 
579 impl From<ProjectJson> for LinkedProject {
from(v: ProjectJson) -> Self580     fn from(v: ProjectJson) -> Self {
581         LinkedProject::InlineJsonProject(v)
582     }
583 }
584 
585 pub struct CallInfoConfig {
586     pub params_only: bool,
587     pub docs: bool,
588 }
589 
590 #[derive(Clone, Debug, PartialEq, Eq)]
591 pub struct LensConfig {
592     // runnables
593     pub run: bool,
594     pub debug: bool,
595     pub interpret: bool,
596 
597     // implementations
598     pub implementations: bool,
599 
600     // references
601     pub method_refs: bool,
602     pub refs_adt: bool,   // for Struct, Enum, Union and Trait
603     pub refs_trait: bool, // for Struct, Enum, Union and Trait
604     pub enum_variant_refs: bool,
605 
606     // annotations
607     pub location: AnnotationLocation,
608 }
609 
610 #[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
611 #[serde(rename_all = "snake_case")]
612 pub enum AnnotationLocation {
613     AboveName,
614     AboveWholeItem,
615 }
616 
617 impl From<AnnotationLocation> for ide::AnnotationLocation {
from(location: AnnotationLocation) -> Self618     fn from(location: AnnotationLocation) -> Self {
619         match location {
620             AnnotationLocation::AboveName => ide::AnnotationLocation::AboveName,
621             AnnotationLocation::AboveWholeItem => ide::AnnotationLocation::AboveWholeItem,
622         }
623     }
624 }
625 
626 impl LensConfig {
any(&self) -> bool627     pub fn any(&self) -> bool {
628         self.run
629             || self.debug
630             || self.implementations
631             || self.method_refs
632             || self.refs_adt
633             || self.refs_trait
634             || self.enum_variant_refs
635     }
636 
none(&self) -> bool637     pub fn none(&self) -> bool {
638         !self.any()
639     }
640 
runnable(&self) -> bool641     pub fn runnable(&self) -> bool {
642         self.run || self.debug
643     }
644 
references(&self) -> bool645     pub fn references(&self) -> bool {
646         self.method_refs || self.refs_adt || self.refs_trait || self.enum_variant_refs
647     }
648 }
649 
650 #[derive(Clone, Debug, PartialEq, Eq)]
651 pub struct HoverActionsConfig {
652     pub implementations: bool,
653     pub references: bool,
654     pub run: bool,
655     pub debug: bool,
656     pub goto_type_def: bool,
657 }
658 
659 impl HoverActionsConfig {
660     pub const NO_ACTIONS: Self = Self {
661         implementations: false,
662         references: false,
663         run: false,
664         debug: false,
665         goto_type_def: false,
666     };
667 
any(&self) -> bool668     pub fn any(&self) -> bool {
669         self.implementations || self.references || self.runnable() || self.goto_type_def
670     }
671 
none(&self) -> bool672     pub fn none(&self) -> bool {
673         !self.any()
674     }
675 
runnable(&self) -> bool676     pub fn runnable(&self) -> bool {
677         self.run || self.debug
678     }
679 }
680 
681 #[derive(Debug, Clone)]
682 pub struct FilesConfig {
683     pub watcher: FilesWatcher,
684     pub exclude: Vec<AbsPathBuf>,
685 }
686 
687 #[derive(Debug, Clone)]
688 pub enum FilesWatcher {
689     Client,
690     Server,
691 }
692 
693 #[derive(Debug, Clone)]
694 pub struct NotificationsConfig {
695     pub cargo_toml_not_found: bool,
696 }
697 
698 #[derive(Debug, Clone)]
699 pub enum RustfmtConfig {
700     Rustfmt { extra_args: Vec<String>, enable_range_formatting: bool },
701     CustomCommand { command: String, args: Vec<String> },
702 }
703 
704 /// Configuration for runnable items, such as `main` function or tests.
705 #[derive(Debug, Clone)]
706 pub struct RunnablesConfig {
707     /// Custom command to be executed instead of `cargo` for runnables.
708     pub override_cargo: Option<String>,
709     /// Additional arguments for the `cargo`, e.g. `--release`.
710     pub cargo_extra_args: Vec<String>,
711 }
712 
713 /// Configuration for workspace symbol search requests.
714 #[derive(Debug, Clone)]
715 pub struct WorkspaceSymbolConfig {
716     /// In what scope should the symbol be searched in.
717     pub search_scope: WorkspaceSymbolSearchScope,
718     /// What kind of symbol is being searched for.
719     pub search_kind: WorkspaceSymbolSearchKind,
720     /// How many items are returned at most.
721     pub search_limit: usize,
722 }
723 
724 pub struct ClientCommandsConfig {
725     pub run_single: bool,
726     pub debug_single: bool,
727     pub show_reference: bool,
728     pub goto_location: bool,
729     pub trigger_parameter_hints: bool,
730 }
731 
732 #[derive(Debug)]
733 pub struct ConfigError {
734     errors: Vec<(String, serde_json::Error)>,
735 }
736 
737 impl fmt::Display for ConfigError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result738     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
739         let errors = self.errors.iter().format_with("\n", |(key, e), f| {
740             f(key)?;
741             f(&": ")?;
742             f(e)
743         });
744         write!(
745             f,
746             "invalid config value{}:\n{}",
747             if self.errors.len() == 1 { "" } else { "s" },
748             errors
749         )
750     }
751 }
752 
753 impl Config {
new( root_path: AbsPathBuf, caps: ClientCapabilities, workspace_roots: Vec<AbsPathBuf>, ) -> Self754     pub fn new(
755         root_path: AbsPathBuf,
756         caps: ClientCapabilities,
757         workspace_roots: Vec<AbsPathBuf>,
758     ) -> Self {
759         Config {
760             caps,
761             data: ConfigData::default(),
762             detached_files: Vec::new(),
763             discovered_projects: Vec::new(),
764             root_path,
765             snippets: Default::default(),
766             workspace_roots,
767         }
768     }
769 
rediscover_workspaces(&mut self)770     pub fn rediscover_workspaces(&mut self) {
771         let discovered = ProjectManifest::discover_all(&self.workspace_roots);
772         tracing::info!("discovered projects: {:?}", discovered);
773         if discovered.is_empty() {
774             tracing::error!("failed to find any projects in {:?}", &self.workspace_roots);
775         }
776         self.discovered_projects = discovered;
777     }
778 
remove_workspace(&mut self, path: &AbsPath)779     pub fn remove_workspace(&mut self, path: &AbsPath) {
780         if let Some(position) = self.workspace_roots.iter().position(|it| it == path) {
781             self.workspace_roots.remove(position);
782         }
783     }
784 
add_workspaces(&mut self, paths: impl Iterator<Item = AbsPathBuf>)785     pub fn add_workspaces(&mut self, paths: impl Iterator<Item = AbsPathBuf>) {
786         self.workspace_roots.extend(paths);
787     }
788 
update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigError>789     pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigError> {
790         tracing::info!("updating config from JSON: {:#}", json);
791         if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
792             return Ok(());
793         }
794         let mut errors = Vec::new();
795         self.detached_files =
796             get_field::<Vec<PathBuf>>(&mut json, &mut errors, "detachedFiles", None, "[]")
797                 .into_iter()
798                 .map(AbsPathBuf::assert)
799                 .collect();
800         patch_old_style::patch_json_for_outdated_configs(&mut json);
801         self.data = ConfigData::from_json(json, &mut errors);
802         tracing::debug!("deserialized config data: {:#?}", self.data);
803         self.snippets.clear();
804         for (name, def) in self.data.completion_snippets_custom.iter() {
805             if def.prefix.is_empty() && def.postfix.is_empty() {
806                 continue;
807             }
808             let scope = match def.scope {
809                 SnippetScopeDef::Expr => SnippetScope::Expr,
810                 SnippetScopeDef::Type => SnippetScope::Type,
811                 SnippetScopeDef::Item => SnippetScope::Item,
812             };
813             match Snippet::new(
814                 &def.prefix,
815                 &def.postfix,
816                 &def.body,
817                 def.description.as_ref().unwrap_or(name),
818                 &def.requires,
819                 scope,
820             ) {
821                 Some(snippet) => self.snippets.push(snippet),
822                 None => errors.push((
823                     format!("snippet {name} is invalid"),
824                     <serde_json::Error as serde::de::Error>::custom(
825                         "snippet path is invalid or triggers are missing",
826                     ),
827                 )),
828             }
829         }
830 
831         self.validate(&mut errors);
832 
833         if errors.is_empty() {
834             Ok(())
835         } else {
836             Err(ConfigError { errors })
837         }
838     }
839 
validate(&self, error_sink: &mut Vec<(String, serde_json::Error)>)840     fn validate(&self, error_sink: &mut Vec<(String, serde_json::Error)>) {
841         use serde::de::Error;
842         if self.data.check_command.is_empty() {
843             error_sink.push((
844                 "/check/command".to_string(),
845                 serde_json::Error::custom("expected a non-empty string"),
846             ));
847         }
848     }
849 
json_schema() -> serde_json::Value850     pub fn json_schema() -> serde_json::Value {
851         ConfigData::json_schema()
852     }
853 
root_path(&self) -> &AbsPathBuf854     pub fn root_path(&self) -> &AbsPathBuf {
855         &self.root_path
856     }
857 
caps(&self) -> &lsp_types::ClientCapabilities858     pub fn caps(&self) -> &lsp_types::ClientCapabilities {
859         &self.caps
860     }
861 
detached_files(&self) -> &[AbsPathBuf]862     pub fn detached_files(&self) -> &[AbsPathBuf] {
863         &self.detached_files
864     }
865 }
866 
867 macro_rules! try_ {
868     ($expr:expr) => {
869         || -> _ { Some($expr) }()
870     };
871 }
872 macro_rules! try_or {
873     ($expr:expr, $or:expr) => {
874         try_!($expr).unwrap_or($or)
875     };
876 }
877 
878 macro_rules! try_or_def {
879     ($expr:expr) => {
880         try_!($expr).unwrap_or_default()
881     };
882 }
883 
884 impl Config {
has_linked_projects(&self) -> bool885     pub fn has_linked_projects(&self) -> bool {
886         !self.data.linkedProjects.is_empty()
887     }
linked_projects(&self) -> Vec<LinkedProject>888     pub fn linked_projects(&self) -> Vec<LinkedProject> {
889         match self.data.linkedProjects.as_slice() {
890             [] => {
891                 let exclude_dirs: Vec<_> =
892                     self.data.files_excludeDirs.iter().map(|p| self.root_path.join(p)).collect();
893                 self.discovered_projects
894                     .iter()
895                     .filter(
896                         |(ProjectManifest::ProjectJson(path)
897                          | ProjectManifest::CargoToml(path))| {
898                             !exclude_dirs.iter().any(|p| path.starts_with(p))
899                         },
900                     )
901                     .cloned()
902                     .map(LinkedProject::from)
903                     .collect()
904             }
905             linked_projects => linked_projects
906                 .iter()
907                 .filter_map(|linked_project| match linked_project {
908                     ManifestOrProjectJson::Manifest(it) => {
909                         let path = self.root_path.join(it);
910                         ProjectManifest::from_manifest_file(path)
911                             .map_err(|e| tracing::error!("failed to load linked project: {}", e))
912                             .ok()
913                             .map(Into::into)
914                     }
915                     ManifestOrProjectJson::ProjectJson(it) => {
916                         Some(ProjectJson::new(&self.root_path, it.clone()).into())
917                     }
918                 })
919                 .collect(),
920         }
921     }
922 
add_linked_projects(&mut self, linked_projects: Vec<ProjectJsonData>)923     pub fn add_linked_projects(&mut self, linked_projects: Vec<ProjectJsonData>) {
924         let mut linked_projects = linked_projects
925             .into_iter()
926             .map(ManifestOrProjectJson::ProjectJson)
927             .collect::<Vec<ManifestOrProjectJson>>();
928 
929         self.data.linkedProjects.append(&mut linked_projects);
930     }
931 
did_save_text_document_dynamic_registration(&self) -> bool932     pub fn did_save_text_document_dynamic_registration(&self) -> bool {
933         let caps = try_or_def!(self.caps.text_document.as_ref()?.synchronization.clone()?);
934         caps.did_save == Some(true) && caps.dynamic_registration == Some(true)
935     }
936 
did_change_watched_files_dynamic_registration(&self) -> bool937     pub fn did_change_watched_files_dynamic_registration(&self) -> bool {
938         try_or_def!(
939             self.caps.workspace.as_ref()?.did_change_watched_files.as_ref()?.dynamic_registration?
940         )
941     }
942 
prefill_caches(&self) -> bool943     pub fn prefill_caches(&self) -> bool {
944         self.data.cachePriming_enable
945     }
946 
location_link(&self) -> bool947     pub fn location_link(&self) -> bool {
948         try_or_def!(self.caps.text_document.as_ref()?.definition?.link_support?)
949     }
950 
line_folding_only(&self) -> bool951     pub fn line_folding_only(&self) -> bool {
952         try_or_def!(self.caps.text_document.as_ref()?.folding_range.as_ref()?.line_folding_only?)
953     }
954 
hierarchical_symbols(&self) -> bool955     pub fn hierarchical_symbols(&self) -> bool {
956         try_or_def!(
957             self.caps
958                 .text_document
959                 .as_ref()?
960                 .document_symbol
961                 .as_ref()?
962                 .hierarchical_document_symbol_support?
963         )
964     }
965 
code_action_literals(&self) -> bool966     pub fn code_action_literals(&self) -> bool {
967         try_!(self
968             .caps
969             .text_document
970             .as_ref()?
971             .code_action
972             .as_ref()?
973             .code_action_literal_support
974             .as_ref()?)
975         .is_some()
976     }
977 
work_done_progress(&self) -> bool978     pub fn work_done_progress(&self) -> bool {
979         try_or_def!(self.caps.window.as_ref()?.work_done_progress?)
980     }
981 
will_rename(&self) -> bool982     pub fn will_rename(&self) -> bool {
983         try_or_def!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?)
984     }
985 
change_annotation_support(&self) -> bool986     pub fn change_annotation_support(&self) -> bool {
987         try_!(self
988             .caps
989             .workspace
990             .as_ref()?
991             .workspace_edit
992             .as_ref()?
993             .change_annotation_support
994             .as_ref()?)
995         .is_some()
996     }
997 
code_action_resolve(&self) -> bool998     pub fn code_action_resolve(&self) -> bool {
999         try_or_def!(self
1000             .caps
1001             .text_document
1002             .as_ref()?
1003             .code_action
1004             .as_ref()?
1005             .resolve_support
1006             .as_ref()?
1007             .properties
1008             .as_slice())
1009         .iter()
1010         .any(|it| it == "edit")
1011     }
1012 
signature_help_label_offsets(&self) -> bool1013     pub fn signature_help_label_offsets(&self) -> bool {
1014         try_or_def!(
1015             self.caps
1016                 .text_document
1017                 .as_ref()?
1018                 .signature_help
1019                 .as_ref()?
1020                 .signature_information
1021                 .as_ref()?
1022                 .parameter_information
1023                 .as_ref()?
1024                 .label_offset_support?
1025         )
1026     }
1027 
completion_label_details_support(&self) -> bool1028     pub fn completion_label_details_support(&self) -> bool {
1029         try_!(self
1030             .caps
1031             .text_document
1032             .as_ref()?
1033             .completion
1034             .as_ref()?
1035             .completion_item
1036             .as_ref()?
1037             .label_details_support
1038             .as_ref()?)
1039         .is_some()
1040     }
1041 
semantics_tokens_augments_syntax_tokens(&self) -> bool1042     pub fn semantics_tokens_augments_syntax_tokens(&self) -> bool {
1043         try_!(self.caps.text_document.as_ref()?.semantic_tokens.as_ref()?.augments_syntax_tokens?)
1044             .unwrap_or(false)
1045     }
1046 
position_encoding(&self) -> PositionEncoding1047     pub fn position_encoding(&self) -> PositionEncoding {
1048         negotiated_encoding(&self.caps)
1049     }
1050 
experimental(&self, index: &'static str) -> bool1051     fn experimental(&self, index: &'static str) -> bool {
1052         try_or_def!(self.caps.experimental.as_ref()?.get(index)?.as_bool()?)
1053     }
1054 
code_action_group(&self) -> bool1055     pub fn code_action_group(&self) -> bool {
1056         self.experimental("codeActionGroup")
1057     }
1058 
local_docs(&self) -> bool1059     pub fn local_docs(&self) -> bool {
1060         self.experimental("localDocs")
1061     }
1062 
open_server_logs(&self) -> bool1063     pub fn open_server_logs(&self) -> bool {
1064         self.experimental("openServerLogs")
1065     }
1066 
server_status_notification(&self) -> bool1067     pub fn server_status_notification(&self) -> bool {
1068         self.experimental("serverStatusNotification")
1069     }
1070 
1071     /// Whether the client supports colored output for full diagnostics from `checkOnSave`.
color_diagnostic_output(&self) -> bool1072     pub fn color_diagnostic_output(&self) -> bool {
1073         self.experimental("colorDiagnosticOutput")
1074     }
1075 
publish_diagnostics(&self) -> bool1076     pub fn publish_diagnostics(&self) -> bool {
1077         self.data.diagnostics_enable
1078     }
1079 
diagnostics(&self) -> DiagnosticsConfig1080     pub fn diagnostics(&self) -> DiagnosticsConfig {
1081         DiagnosticsConfig {
1082             proc_attr_macros_enabled: self.expand_proc_attr_macros(),
1083             proc_macros_enabled: self.data.procMacro_enable,
1084             disable_experimental: !self.data.diagnostics_experimental_enable,
1085             disabled: self.data.diagnostics_disabled.clone(),
1086             expr_fill_default: match self.data.assist_expressionFillDefault {
1087                 ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
1088                 ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
1089             },
1090             insert_use: self.insert_use_config(),
1091             prefer_no_std: self.data.imports_prefer_no_std,
1092         }
1093     }
1094 
diagnostics_map(&self) -> DiagnosticsMapConfig1095     pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
1096         DiagnosticsMapConfig {
1097             remap_prefix: self.data.diagnostics_remapPrefix.clone(),
1098             warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
1099             warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
1100         }
1101     }
1102 
extra_args(&self) -> &Vec<String>1103     pub fn extra_args(&self) -> &Vec<String> {
1104         &self.data.cargo_extraArgs
1105     }
1106 
extra_env(&self) -> &FxHashMap<String, String>1107     pub fn extra_env(&self) -> &FxHashMap<String, String> {
1108         &self.data.cargo_extraEnv
1109     }
1110 
check_extra_args(&self) -> Vec<String>1111     pub fn check_extra_args(&self) -> Vec<String> {
1112         let mut extra_args = self.extra_args().clone();
1113         extra_args.extend_from_slice(&self.data.check_extraArgs);
1114         extra_args
1115     }
1116 
check_extra_env(&self) -> FxHashMap<String, String>1117     pub fn check_extra_env(&self) -> FxHashMap<String, String> {
1118         let mut extra_env = self.data.cargo_extraEnv.clone();
1119         extra_env.extend(self.data.check_extraEnv.clone());
1120         extra_env
1121     }
1122 
lru_parse_query_capacity(&self) -> Option<usize>1123     pub fn lru_parse_query_capacity(&self) -> Option<usize> {
1124         self.data.lru_capacity
1125     }
1126 
lru_query_capacities(&self) -> Option<&FxHashMap<Box<str>, usize>>1127     pub fn lru_query_capacities(&self) -> Option<&FxHashMap<Box<str>, usize>> {
1128         self.data.lru_query_capacities.is_empty().not().then(|| &self.data.lru_query_capacities)
1129     }
1130 
proc_macro_srv(&self) -> Option<AbsPathBuf>1131     pub fn proc_macro_srv(&self) -> Option<AbsPathBuf> {
1132         let path = self.data.procMacro_server.clone()?;
1133         Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(&path)))
1134     }
1135 
dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>>1136     pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
1137         &self.data.procMacro_ignored
1138     }
1139 
expand_proc_macros(&self) -> bool1140     pub fn expand_proc_macros(&self) -> bool {
1141         self.data.procMacro_enable
1142     }
1143 
expand_proc_attr_macros(&self) -> bool1144     pub fn expand_proc_attr_macros(&self) -> bool {
1145         self.data.procMacro_enable && self.data.procMacro_attributes_enable
1146     }
1147 
files(&self) -> FilesConfig1148     pub fn files(&self) -> FilesConfig {
1149         FilesConfig {
1150             watcher: match self.data.files_watcher {
1151                 FilesWatcherDef::Client if self.did_change_watched_files_dynamic_registration() => {
1152                     FilesWatcher::Client
1153                 }
1154                 _ => FilesWatcher::Server,
1155             },
1156             exclude: self.data.files_excludeDirs.iter().map(|it| self.root_path.join(it)).collect(),
1157         }
1158     }
1159 
notifications(&self) -> NotificationsConfig1160     pub fn notifications(&self) -> NotificationsConfig {
1161         NotificationsConfig { cargo_toml_not_found: self.data.notifications_cargoTomlNotFound }
1162     }
1163 
cargo_autoreload(&self) -> bool1164     pub fn cargo_autoreload(&self) -> bool {
1165         self.data.cargo_autoreload
1166     }
1167 
run_build_scripts(&self) -> bool1168     pub fn run_build_scripts(&self) -> bool {
1169         self.data.cargo_buildScripts_enable || self.data.procMacro_enable
1170     }
1171 
cargo(&self) -> CargoConfig1172     pub fn cargo(&self) -> CargoConfig {
1173         let rustc_source = self.data.rustc_source.as_ref().map(|rustc_src| {
1174             if rustc_src == "discover" {
1175                 RustLibSource::Discover
1176             } else {
1177                 RustLibSource::Path(self.root_path.join(rustc_src))
1178             }
1179         });
1180         let sysroot = self.data.cargo_sysroot.as_ref().map(|sysroot| {
1181             if sysroot == "discover" {
1182                 RustLibSource::Discover
1183             } else {
1184                 RustLibSource::Path(self.root_path.join(sysroot))
1185             }
1186         });
1187         let sysroot_src =
1188             self.data.cargo_sysrootSrc.as_ref().map(|sysroot| self.root_path.join(sysroot));
1189 
1190         CargoConfig {
1191             features: match &self.data.cargo_features {
1192                 CargoFeaturesDef::All => CargoFeatures::All,
1193                 CargoFeaturesDef::Selected(features) => CargoFeatures::Selected {
1194                     features: features.clone(),
1195                     no_default_features: self.data.cargo_noDefaultFeatures,
1196                 },
1197             },
1198             target: self.data.cargo_target.clone(),
1199             sysroot,
1200             sysroot_src,
1201             rustc_source,
1202             cfg_overrides: project_model::CfgOverrides {
1203                 global: CfgDiff::new(
1204                     self.data
1205                         .cargo_cfgs
1206                         .iter()
1207                         .map(|(key, val)| {
1208                             if val.is_empty() {
1209                                 CfgAtom::Flag(key.into())
1210                             } else {
1211                                 CfgAtom::KeyValue { key: key.into(), value: val.into() }
1212                             }
1213                         })
1214                         .collect(),
1215                     vec![],
1216                 )
1217                 .unwrap(),
1218                 selective: self
1219                     .data
1220                     .cargo_unsetTest
1221                     .iter()
1222                     .map(|it| {
1223                         (
1224                             it.clone(),
1225                             CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap(),
1226                         )
1227                     })
1228                     .collect(),
1229             },
1230             wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper,
1231             invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy {
1232                 InvocationStrategy::Once => project_model::InvocationStrategy::Once,
1233                 InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace,
1234             },
1235             invocation_location: match self.data.cargo_buildScripts_invocationLocation {
1236                 InvocationLocation::Root => {
1237                     project_model::InvocationLocation::Root(self.root_path.clone())
1238                 }
1239                 InvocationLocation::Workspace => project_model::InvocationLocation::Workspace,
1240             },
1241             run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(),
1242             extra_args: self.data.cargo_extraArgs.clone(),
1243             extra_env: self.data.cargo_extraEnv.clone(),
1244         }
1245     }
1246 
rustfmt(&self) -> RustfmtConfig1247     pub fn rustfmt(&self) -> RustfmtConfig {
1248         match &self.data.rustfmt_overrideCommand {
1249             Some(args) if !args.is_empty() => {
1250                 let mut args = args.clone();
1251                 let command = args.remove(0);
1252                 RustfmtConfig::CustomCommand { command, args }
1253             }
1254             Some(_) | None => RustfmtConfig::Rustfmt {
1255                 extra_args: self.data.rustfmt_extraArgs.clone(),
1256                 enable_range_formatting: self.data.rustfmt_rangeFormatting_enable,
1257             },
1258         }
1259     }
1260 
flycheck(&self) -> FlycheckConfig1261     pub fn flycheck(&self) -> FlycheckConfig {
1262         match &self.data.check_overrideCommand {
1263             Some(args) if !args.is_empty() => {
1264                 let mut args = args.clone();
1265                 let command = args.remove(0);
1266                 FlycheckConfig::CustomCommand {
1267                     command,
1268                     args,
1269                     extra_env: self.check_extra_env(),
1270                     invocation_strategy: match self.data.check_invocationStrategy {
1271                         InvocationStrategy::Once => flycheck::InvocationStrategy::Once,
1272                         InvocationStrategy::PerWorkspace => {
1273                             flycheck::InvocationStrategy::PerWorkspace
1274                         }
1275                     },
1276                     invocation_location: match self.data.check_invocationLocation {
1277                         InvocationLocation::Root => {
1278                             flycheck::InvocationLocation::Root(self.root_path.clone())
1279                         }
1280                         InvocationLocation::Workspace => flycheck::InvocationLocation::Workspace,
1281                     },
1282                 }
1283             }
1284             Some(_) | None => FlycheckConfig::CargoCommand {
1285                 command: self.data.check_command.clone(),
1286                 target_triples: self
1287                     .data
1288                     .check_targets
1289                     .clone()
1290                     .and_then(|targets| match &targets.0[..] {
1291                         [] => None,
1292                         targets => Some(targets.into()),
1293                     })
1294                     .unwrap_or_else(|| self.data.cargo_target.clone().into_iter().collect()),
1295                 all_targets: self.data.check_allTargets,
1296                 no_default_features: self
1297                     .data
1298                     .check_noDefaultFeatures
1299                     .unwrap_or(self.data.cargo_noDefaultFeatures),
1300                 all_features: matches!(
1301                     self.data.check_features.as_ref().unwrap_or(&self.data.cargo_features),
1302                     CargoFeaturesDef::All
1303                 ),
1304                 features: match self
1305                     .data
1306                     .check_features
1307                     .clone()
1308                     .unwrap_or_else(|| self.data.cargo_features.clone())
1309                 {
1310                     CargoFeaturesDef::All => vec![],
1311                     CargoFeaturesDef::Selected(it) => it,
1312                 },
1313                 extra_args: self.check_extra_args(),
1314                 extra_env: self.check_extra_env(),
1315                 ansi_color_output: self.color_diagnostic_output(),
1316             },
1317         }
1318     }
1319 
check_on_save(&self) -> bool1320     pub fn check_on_save(&self) -> bool {
1321         self.data.checkOnSave
1322     }
1323 
runnables(&self) -> RunnablesConfig1324     pub fn runnables(&self) -> RunnablesConfig {
1325         RunnablesConfig {
1326             override_cargo: self.data.runnables_command.clone(),
1327             cargo_extra_args: self.data.runnables_extraArgs.clone(),
1328         }
1329     }
1330 
inlay_hints(&self) -> InlayHintsConfig1331     pub fn inlay_hints(&self) -> InlayHintsConfig {
1332         InlayHintsConfig {
1333             render_colons: self.data.inlayHints_renderColons,
1334             type_hints: self.data.inlayHints_typeHints_enable,
1335             parameter_hints: self.data.inlayHints_parameterHints_enable,
1336             chaining_hints: self.data.inlayHints_chainingHints_enable,
1337             discriminant_hints: match self.data.inlayHints_discriminantHints_enable {
1338                 DiscriminantHintsDef::Always => ide::DiscriminantHints::Always,
1339                 DiscriminantHintsDef::Never => ide::DiscriminantHints::Never,
1340                 DiscriminantHintsDef::Fieldless => ide::DiscriminantHints::Fieldless,
1341             },
1342             closure_return_type_hints: match self.data.inlayHints_closureReturnTypeHints_enable {
1343                 ClosureReturnTypeHintsDef::Always => ide::ClosureReturnTypeHints::Always,
1344                 ClosureReturnTypeHintsDef::Never => ide::ClosureReturnTypeHints::Never,
1345                 ClosureReturnTypeHintsDef::WithBlock => ide::ClosureReturnTypeHints::WithBlock,
1346             },
1347             lifetime_elision_hints: match self.data.inlayHints_lifetimeElisionHints_enable {
1348                 LifetimeElisionDef::Always => ide::LifetimeElisionHints::Always,
1349                 LifetimeElisionDef::Never => ide::LifetimeElisionHints::Never,
1350                 LifetimeElisionDef::SkipTrivial => ide::LifetimeElisionHints::SkipTrivial,
1351             },
1352             hide_named_constructor_hints: self.data.inlayHints_typeHints_hideNamedConstructor,
1353             hide_closure_initialization_hints: self
1354                 .data
1355                 .inlayHints_typeHints_hideClosureInitialization,
1356             closure_style: match self.data.inlayHints_closureStyle {
1357                 ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn,
1358                 ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation,
1359                 ClosureStyle::WithId => hir::ClosureStyle::ClosureWithId,
1360                 ClosureStyle::Hide => hir::ClosureStyle::Hide,
1361             },
1362             closure_capture_hints: self.data.inlayHints_closureCaptureHints_enable,
1363             adjustment_hints: match self.data.inlayHints_expressionAdjustmentHints_enable {
1364                 AdjustmentHintsDef::Always => ide::AdjustmentHints::Always,
1365                 AdjustmentHintsDef::Never => match self.data.inlayHints_reborrowHints_enable {
1366                     ReborrowHintsDef::Always | ReborrowHintsDef::Mutable => {
1367                         ide::AdjustmentHints::ReborrowOnly
1368                     }
1369                     ReborrowHintsDef::Never => ide::AdjustmentHints::Never,
1370                 },
1371                 AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly,
1372             },
1373             adjustment_hints_mode: match self.data.inlayHints_expressionAdjustmentHints_mode {
1374                 AdjustmentHintsModeDef::Prefix => ide::AdjustmentHintsMode::Prefix,
1375                 AdjustmentHintsModeDef::Postfix => ide::AdjustmentHintsMode::Postfix,
1376                 AdjustmentHintsModeDef::PreferPrefix => ide::AdjustmentHintsMode::PreferPrefix,
1377                 AdjustmentHintsModeDef::PreferPostfix => ide::AdjustmentHintsMode::PreferPostfix,
1378             },
1379             adjustment_hints_hide_outside_unsafe: self
1380                 .data
1381                 .inlayHints_expressionAdjustmentHints_hideOutsideUnsafe,
1382             binding_mode_hints: self.data.inlayHints_bindingModeHints_enable,
1383             param_names_for_lifetime_elision_hints: self
1384                 .data
1385                 .inlayHints_lifetimeElisionHints_useParameterNames,
1386             max_length: self.data.inlayHints_maxLength,
1387             closing_brace_hints_min_lines: if self.data.inlayHints_closingBraceHints_enable {
1388                 Some(self.data.inlayHints_closingBraceHints_minLines)
1389             } else {
1390                 None
1391             },
1392         }
1393     }
1394 
insert_use_config(&self) -> InsertUseConfig1395     fn insert_use_config(&self) -> InsertUseConfig {
1396         InsertUseConfig {
1397             granularity: match self.data.imports_granularity_group {
1398                 ImportGranularityDef::Preserve => ImportGranularity::Preserve,
1399                 ImportGranularityDef::Item => ImportGranularity::Item,
1400                 ImportGranularityDef::Crate => ImportGranularity::Crate,
1401                 ImportGranularityDef::Module => ImportGranularity::Module,
1402             },
1403             enforce_granularity: self.data.imports_granularity_enforce,
1404             prefix_kind: match self.data.imports_prefix {
1405                 ImportPrefixDef::Plain => PrefixKind::Plain,
1406                 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
1407                 ImportPrefixDef::BySelf => PrefixKind::BySelf,
1408             },
1409             group: self.data.imports_group_enable,
1410             skip_glob_imports: !self.data.imports_merge_glob,
1411         }
1412     }
1413 
completion(&self) -> CompletionConfig1414     pub fn completion(&self) -> CompletionConfig {
1415         CompletionConfig {
1416             enable_postfix_completions: self.data.completion_postfix_enable,
1417             enable_imports_on_the_fly: self.data.completion_autoimport_enable
1418                 && completion_item_edit_resolve(&self.caps),
1419             enable_self_on_the_fly: self.data.completion_autoself_enable,
1420             enable_private_editable: self.data.completion_privateEditable_enable,
1421             callable: match self.data.completion_callable_snippets {
1422                 CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments),
1423                 CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses),
1424                 CallableCompletionDef::None => None,
1425             },
1426             insert_use: self.insert_use_config(),
1427             prefer_no_std: self.data.imports_prefer_no_std,
1428             snippet_cap: SnippetCap::new(try_or_def!(
1429                 self.caps
1430                     .text_document
1431                     .as_ref()?
1432                     .completion
1433                     .as_ref()?
1434                     .completion_item
1435                     .as_ref()?
1436                     .snippet_support?
1437             )),
1438             snippets: self.snippets.clone(),
1439             limit: self.data.completion_limit,
1440         }
1441     }
1442 
find_all_refs_exclude_imports(&self) -> bool1443     pub fn find_all_refs_exclude_imports(&self) -> bool {
1444         self.data.references_excludeImports
1445     }
1446 
snippet_cap(&self) -> bool1447     pub fn snippet_cap(&self) -> bool {
1448         self.experimental("snippetTextEdit")
1449     }
1450 
assist(&self) -> AssistConfig1451     pub fn assist(&self) -> AssistConfig {
1452         AssistConfig {
1453             snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
1454             allowed: None,
1455             insert_use: self.insert_use_config(),
1456             prefer_no_std: self.data.imports_prefer_no_std,
1457             assist_emit_must_use: self.data.assist_emitMustUse,
1458         }
1459     }
1460 
join_lines(&self) -> JoinLinesConfig1461     pub fn join_lines(&self) -> JoinLinesConfig {
1462         JoinLinesConfig {
1463             join_else_if: self.data.joinLines_joinElseIf,
1464             remove_trailing_comma: self.data.joinLines_removeTrailingComma,
1465             unwrap_trivial_blocks: self.data.joinLines_unwrapTrivialBlock,
1466             join_assignments: self.data.joinLines_joinAssignments,
1467         }
1468     }
1469 
call_info(&self) -> CallInfoConfig1470     pub fn call_info(&self) -> CallInfoConfig {
1471         CallInfoConfig {
1472             params_only: matches!(self.data.signatureInfo_detail, SignatureDetail::Parameters),
1473             docs: self.data.signatureInfo_documentation_enable,
1474         }
1475     }
1476 
lens(&self) -> LensConfig1477     pub fn lens(&self) -> LensConfig {
1478         LensConfig {
1479             run: self.data.lens_enable && self.data.lens_run_enable,
1480             debug: self.data.lens_enable && self.data.lens_debug_enable,
1481             interpret: self.data.lens_enable
1482                 && self.data.lens_run_enable
1483                 && self.data.interpret_tests,
1484             implementations: self.data.lens_enable && self.data.lens_implementations_enable,
1485             method_refs: self.data.lens_enable && self.data.lens_references_method_enable,
1486             refs_adt: self.data.lens_enable && self.data.lens_references_adt_enable,
1487             refs_trait: self.data.lens_enable && self.data.lens_references_trait_enable,
1488             enum_variant_refs: self.data.lens_enable
1489                 && self.data.lens_references_enumVariant_enable,
1490             location: self.data.lens_location,
1491         }
1492     }
1493 
hover_actions(&self) -> HoverActionsConfig1494     pub fn hover_actions(&self) -> HoverActionsConfig {
1495         let enable = self.experimental("hoverActions") && self.data.hover_actions_enable;
1496         HoverActionsConfig {
1497             implementations: enable && self.data.hover_actions_implementations_enable,
1498             references: enable && self.data.hover_actions_references_enable,
1499             run: enable && self.data.hover_actions_run_enable,
1500             debug: enable && self.data.hover_actions_debug_enable,
1501             goto_type_def: enable && self.data.hover_actions_gotoTypeDef_enable,
1502         }
1503     }
1504 
highlighting_non_standard_tokens(&self) -> bool1505     pub fn highlighting_non_standard_tokens(&self) -> bool {
1506         self.data.semanticHighlighting_nonStandardTokens
1507     }
1508 
highlighting_config(&self) -> HighlightConfig1509     pub fn highlighting_config(&self) -> HighlightConfig {
1510         HighlightConfig {
1511             strings: self.data.semanticHighlighting_strings_enable,
1512             punctuation: self.data.semanticHighlighting_punctuation_enable,
1513             specialize_punctuation: self
1514                 .data
1515                 .semanticHighlighting_punctuation_specialization_enable,
1516             macro_bang: self.data.semanticHighlighting_punctuation_separate_macro_bang,
1517             operator: self.data.semanticHighlighting_operator_enable,
1518             specialize_operator: self.data.semanticHighlighting_operator_specialization_enable,
1519             inject_doc_comment: self.data.semanticHighlighting_doc_comment_inject_enable,
1520             syntactic_name_ref_highlighting: false,
1521         }
1522     }
1523 
hover(&self) -> HoverConfig1524     pub fn hover(&self) -> HoverConfig {
1525         let mem_kind = |kind| match kind {
1526             MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both,
1527             MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal,
1528             MemoryLayoutHoverRenderKindDef::Hexadecimal => MemoryLayoutHoverRenderKind::Hexadecimal,
1529         };
1530         HoverConfig {
1531             links_in_hover: self.data.hover_links_enable,
1532             memory_layout: self.data.hover_memoryLayout_enable.then_some(MemoryLayoutHoverConfig {
1533                 size: self.data.hover_memoryLayout_size.map(mem_kind),
1534                 offset: self.data.hover_memoryLayout_offset.map(mem_kind),
1535                 alignment: self.data.hover_memoryLayout_alignment.map(mem_kind),
1536                 niches: self.data.hover_memoryLayout_niches.unwrap_or_default(),
1537             }),
1538             documentation: self.data.hover_documentation_enable,
1539             format: {
1540                 let is_markdown = try_or_def!(self
1541                     .caps
1542                     .text_document
1543                     .as_ref()?
1544                     .hover
1545                     .as_ref()?
1546                     .content_format
1547                     .as_ref()?
1548                     .as_slice())
1549                 .contains(&MarkupKind::Markdown);
1550                 if is_markdown {
1551                     HoverDocFormat::Markdown
1552                 } else {
1553                     HoverDocFormat::PlainText
1554                 }
1555             },
1556             keywords: self.data.hover_documentation_keywords_enable,
1557         }
1558     }
1559 
workspace_symbol(&self) -> WorkspaceSymbolConfig1560     pub fn workspace_symbol(&self) -> WorkspaceSymbolConfig {
1561         WorkspaceSymbolConfig {
1562             search_scope: match self.data.workspace_symbol_search_scope {
1563                 WorkspaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace,
1564                 WorkspaceSymbolSearchScopeDef::WorkspaceAndDependencies => {
1565                     WorkspaceSymbolSearchScope::WorkspaceAndDependencies
1566                 }
1567             },
1568             search_kind: match self.data.workspace_symbol_search_kind {
1569                 WorkspaceSymbolSearchKindDef::OnlyTypes => WorkspaceSymbolSearchKind::OnlyTypes,
1570                 WorkspaceSymbolSearchKindDef::AllSymbols => WorkspaceSymbolSearchKind::AllSymbols,
1571             },
1572             search_limit: self.data.workspace_symbol_search_limit,
1573         }
1574     }
1575 
semantic_tokens_refresh(&self) -> bool1576     pub fn semantic_tokens_refresh(&self) -> bool {
1577         try_or_def!(self.caps.workspace.as_ref()?.semantic_tokens.as_ref()?.refresh_support?)
1578     }
1579 
code_lens_refresh(&self) -> bool1580     pub fn code_lens_refresh(&self) -> bool {
1581         try_or_def!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?)
1582     }
1583 
inlay_hints_refresh(&self) -> bool1584     pub fn inlay_hints_refresh(&self) -> bool {
1585         try_or_def!(self.caps.workspace.as_ref()?.inlay_hint.as_ref()?.refresh_support?)
1586     }
1587 
insert_replace_support(&self) -> bool1588     pub fn insert_replace_support(&self) -> bool {
1589         try_or_def!(
1590             self.caps
1591                 .text_document
1592                 .as_ref()?
1593                 .completion
1594                 .as_ref()?
1595                 .completion_item
1596                 .as_ref()?
1597                 .insert_replace_support?
1598         )
1599     }
1600 
client_commands(&self) -> ClientCommandsConfig1601     pub fn client_commands(&self) -> ClientCommandsConfig {
1602         let commands =
1603             try_or!(self.caps.experimental.as_ref()?.get("commands")?, &serde_json::Value::Null);
1604         let commands: Option<lsp_ext::ClientCommandOptions> =
1605             serde_json::from_value(commands.clone()).ok();
1606         let force = commands.is_none() && self.data.lens_forceCustomCommands;
1607         let commands = commands.map(|it| it.commands).unwrap_or_default();
1608 
1609         let get = |name: &str| commands.iter().any(|it| it == name) || force;
1610 
1611         ClientCommandsConfig {
1612             run_single: get("rust-analyzer.runSingle"),
1613             debug_single: get("rust-analyzer.debugSingle"),
1614             show_reference: get("rust-analyzer.showReferences"),
1615             goto_location: get("rust-analyzer.gotoLocation"),
1616             trigger_parameter_hints: get("editor.action.triggerParameterHints"),
1617         }
1618     }
1619 
highlight_related(&self) -> HighlightRelatedConfig1620     pub fn highlight_related(&self) -> HighlightRelatedConfig {
1621         HighlightRelatedConfig {
1622             references: self.data.highlightRelated_references_enable,
1623             break_points: self.data.highlightRelated_breakPoints_enable,
1624             exit_points: self.data.highlightRelated_exitPoints_enable,
1625             yield_points: self.data.highlightRelated_yieldPoints_enable,
1626             closure_captures: self.data.highlightRelated_closureCaptures_enable,
1627         }
1628     }
1629 
prime_caches_num_threads(&self) -> u81630     pub fn prime_caches_num_threads(&self) -> u8 {
1631         match self.data.cachePriming_numThreads {
1632             0 => num_cpus::get_physical().try_into().unwrap_or(u8::MAX),
1633             n => n,
1634         }
1635     }
1636 
main_loop_num_threads(&self) -> usize1637     pub fn main_loop_num_threads(&self) -> usize {
1638         self.data.numThreads.unwrap_or(num_cpus::get_physical().try_into().unwrap_or(1))
1639     }
1640 
typing_autoclose_angle(&self) -> bool1641     pub fn typing_autoclose_angle(&self) -> bool {
1642         self.data.typing_autoClosingAngleBrackets_enable
1643     }
1644 }
1645 // Deserialization definitions
1646 
1647 macro_rules! create_bool_or_string_de {
1648     ($ident:ident<$bool:literal, $string:literal>) => {
1649         fn $ident<'de, D>(d: D) -> Result<(), D::Error>
1650         where
1651             D: serde::Deserializer<'de>,
1652         {
1653             struct V;
1654             impl<'de> serde::de::Visitor<'de> for V {
1655                 type Value = ();
1656 
1657                 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1658                     formatter.write_str(concat!(
1659                         stringify!($bool),
1660                         " or \"",
1661                         stringify!($string),
1662                         "\""
1663                     ))
1664                 }
1665 
1666                 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
1667                 where
1668                     E: serde::de::Error,
1669                 {
1670                     match v {
1671                         $bool => Ok(()),
1672                         _ => Err(serde::de::Error::invalid_value(
1673                             serde::de::Unexpected::Bool(v),
1674                             &self,
1675                         )),
1676                     }
1677                 }
1678 
1679                 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
1680                 where
1681                     E: serde::de::Error,
1682                 {
1683                     match v {
1684                         $string => Ok(()),
1685                         _ => Err(serde::de::Error::invalid_value(
1686                             serde::de::Unexpected::Str(v),
1687                             &self,
1688                         )),
1689                     }
1690                 }
1691 
1692                 fn visit_enum<A>(self, a: A) -> Result<Self::Value, A::Error>
1693                 where
1694                     A: serde::de::EnumAccess<'de>,
1695                 {
1696                     use serde::de::VariantAccess;
1697                     let (variant, va) = a.variant::<&'de str>()?;
1698                     va.unit_variant()?;
1699                     match variant {
1700                         $string => Ok(()),
1701                         _ => Err(serde::de::Error::invalid_value(
1702                             serde::de::Unexpected::Str(variant),
1703                             &self,
1704                         )),
1705                     }
1706                 }
1707             }
1708             d.deserialize_any(V)
1709         }
1710     };
1711 }
1712 create_bool_or_string_de!(true_or_always<true, "always">);
1713 create_bool_or_string_de!(false_or_never<false, "never">);
1714 
1715 macro_rules! named_unit_variant {
1716     ($variant:ident) => {
1717         pub(super) fn $variant<'de, D>(deserializer: D) -> Result<(), D::Error>
1718         where
1719             D: serde::Deserializer<'de>,
1720         {
1721             struct V;
1722             impl<'de> serde::de::Visitor<'de> for V {
1723                 type Value = ();
1724                 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1725                     f.write_str(concat!("\"", stringify!($variant), "\""))
1726                 }
1727                 fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
1728                     if value == stringify!($variant) {
1729                         Ok(())
1730                     } else {
1731                         Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
1732                     }
1733                 }
1734             }
1735             deserializer.deserialize_str(V)
1736         }
1737     };
1738 }
1739 
1740 mod de_unit_v {
1741     named_unit_variant!(all);
1742     named_unit_variant!(skip_trivial);
1743     named_unit_variant!(mutable);
1744     named_unit_variant!(reborrow);
1745     named_unit_variant!(fieldless);
1746     named_unit_variant!(with_block);
1747     named_unit_variant!(decimal);
1748     named_unit_variant!(hexadecimal);
1749     named_unit_variant!(both);
1750 }
1751 
1752 #[derive(Deserialize, Debug, Clone, Copy)]
1753 #[serde(rename_all = "snake_case")]
1754 enum SnippetScopeDef {
1755     Expr,
1756     Item,
1757     Type,
1758 }
1759 
1760 impl Default for SnippetScopeDef {
default() -> Self1761     fn default() -> Self {
1762         SnippetScopeDef::Expr
1763     }
1764 }
1765 
1766 #[derive(Deserialize, Debug, Clone, Default)]
1767 #[serde(default)]
1768 struct SnippetDef {
1769     #[serde(deserialize_with = "single_or_array")]
1770     prefix: Vec<String>,
1771     #[serde(deserialize_with = "single_or_array")]
1772     postfix: Vec<String>,
1773     description: Option<String>,
1774     #[serde(deserialize_with = "single_or_array")]
1775     body: Vec<String>,
1776     #[serde(deserialize_with = "single_or_array")]
1777     requires: Vec<String>,
1778     scope: SnippetScopeDef,
1779 }
1780 
single_or_array<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error> where D: serde::Deserializer<'de>,1781 fn single_or_array<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
1782 where
1783     D: serde::Deserializer<'de>,
1784 {
1785     struct SingleOrVec;
1786 
1787     impl<'de> serde::de::Visitor<'de> for SingleOrVec {
1788         type Value = Vec<String>;
1789 
1790         fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1791             formatter.write_str("string or array of strings")
1792         }
1793 
1794         fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
1795         where
1796             E: serde::de::Error,
1797         {
1798             Ok(vec![value.to_owned()])
1799         }
1800 
1801         fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
1802         where
1803             A: serde::de::SeqAccess<'de>,
1804         {
1805             Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(seq))
1806         }
1807     }
1808 
1809     deserializer.deserialize_any(SingleOrVec)
1810 }
1811 
1812 #[derive(Deserialize, Debug, Clone)]
1813 #[serde(untagged)]
1814 enum ManifestOrProjectJson {
1815     Manifest(PathBuf),
1816     ProjectJson(ProjectJsonData),
1817 }
1818 
1819 #[derive(Deserialize, Debug, Clone)]
1820 #[serde(rename_all = "snake_case")]
1821 enum ExprFillDefaultDef {
1822     Todo,
1823     Default,
1824 }
1825 
1826 #[derive(Deserialize, Debug, Clone)]
1827 #[serde(rename_all = "snake_case")]
1828 enum ImportGranularityDef {
1829     Preserve,
1830     Item,
1831     Crate,
1832     Module,
1833 }
1834 
1835 #[derive(Deserialize, Debug, Copy, Clone)]
1836 #[serde(rename_all = "snake_case")]
1837 enum CallableCompletionDef {
1838     FillArguments,
1839     AddParentheses,
1840     None,
1841 }
1842 
1843 #[derive(Deserialize, Debug, Clone)]
1844 #[serde(untagged)]
1845 enum CargoFeaturesDef {
1846     #[serde(deserialize_with = "de_unit_v::all")]
1847     All,
1848     Selected(Vec<String>),
1849 }
1850 
1851 #[derive(Deserialize, Debug, Clone)]
1852 #[serde(rename_all = "snake_case")]
1853 enum InvocationStrategy {
1854     Once,
1855     PerWorkspace,
1856 }
1857 
1858 #[derive(Deserialize, Debug, Clone)]
1859 struct CheckOnSaveTargets(#[serde(deserialize_with = "single_or_array")] Vec<String>);
1860 
1861 #[derive(Deserialize, Debug, Clone)]
1862 #[serde(rename_all = "snake_case")]
1863 enum InvocationLocation {
1864     Root,
1865     Workspace,
1866 }
1867 
1868 #[derive(Deserialize, Debug, Clone)]
1869 #[serde(untagged)]
1870 enum LifetimeElisionDef {
1871     #[serde(deserialize_with = "true_or_always")]
1872     Always,
1873     #[serde(deserialize_with = "false_or_never")]
1874     Never,
1875     #[serde(deserialize_with = "de_unit_v::skip_trivial")]
1876     SkipTrivial,
1877 }
1878 
1879 #[derive(Deserialize, Debug, Clone)]
1880 #[serde(untagged)]
1881 enum ClosureReturnTypeHintsDef {
1882     #[serde(deserialize_with = "true_or_always")]
1883     Always,
1884     #[serde(deserialize_with = "false_or_never")]
1885     Never,
1886     #[serde(deserialize_with = "de_unit_v::with_block")]
1887     WithBlock,
1888 }
1889 
1890 #[derive(Deserialize, Debug, Clone)]
1891 #[serde(rename_all = "snake_case")]
1892 enum ClosureStyle {
1893     ImplFn,
1894     RustAnalyzer,
1895     WithId,
1896     Hide,
1897 }
1898 
1899 #[derive(Deserialize, Debug, Clone)]
1900 #[serde(untagged)]
1901 enum ReborrowHintsDef {
1902     #[serde(deserialize_with = "true_or_always")]
1903     Always,
1904     #[serde(deserialize_with = "false_or_never")]
1905     Never,
1906     #[serde(deserialize_with = "de_unit_v::mutable")]
1907     Mutable,
1908 }
1909 
1910 #[derive(Deserialize, Debug, Clone)]
1911 #[serde(untagged)]
1912 enum AdjustmentHintsDef {
1913     #[serde(deserialize_with = "true_or_always")]
1914     Always,
1915     #[serde(deserialize_with = "false_or_never")]
1916     Never,
1917     #[serde(deserialize_with = "de_unit_v::reborrow")]
1918     Reborrow,
1919 }
1920 
1921 #[derive(Deserialize, Debug, Clone)]
1922 #[serde(untagged)]
1923 enum DiscriminantHintsDef {
1924     #[serde(deserialize_with = "true_or_always")]
1925     Always,
1926     #[serde(deserialize_with = "false_or_never")]
1927     Never,
1928     #[serde(deserialize_with = "de_unit_v::fieldless")]
1929     Fieldless,
1930 }
1931 
1932 #[derive(Deserialize, Debug, Clone)]
1933 #[serde(rename_all = "snake_case")]
1934 enum AdjustmentHintsModeDef {
1935     Prefix,
1936     Postfix,
1937     PreferPrefix,
1938     PreferPostfix,
1939 }
1940 
1941 #[derive(Deserialize, Debug, Clone)]
1942 #[serde(rename_all = "snake_case")]
1943 enum FilesWatcherDef {
1944     Client,
1945     Notify,
1946     Server,
1947 }
1948 
1949 #[derive(Deserialize, Debug, Clone)]
1950 #[serde(rename_all = "snake_case")]
1951 enum ImportPrefixDef {
1952     Plain,
1953     #[serde(alias = "self")]
1954     BySelf,
1955     #[serde(alias = "crate")]
1956     ByCrate,
1957 }
1958 
1959 #[derive(Deserialize, Debug, Clone)]
1960 #[serde(rename_all = "snake_case")]
1961 enum WorkspaceSymbolSearchScopeDef {
1962     Workspace,
1963     WorkspaceAndDependencies,
1964 }
1965 
1966 #[derive(Deserialize, Debug, Clone)]
1967 #[serde(rename_all = "snake_case")]
1968 enum SignatureDetail {
1969     Full,
1970     Parameters,
1971 }
1972 
1973 #[derive(Deserialize, Debug, Clone)]
1974 #[serde(rename_all = "snake_case")]
1975 enum WorkspaceSymbolSearchKindDef {
1976     OnlyTypes,
1977     AllSymbols,
1978 }
1979 
1980 #[derive(Deserialize, Debug, Copy, Clone)]
1981 #[serde(rename_all = "snake_case")]
1982 #[serde(untagged)]
1983 pub enum MemoryLayoutHoverRenderKindDef {
1984     #[serde(deserialize_with = "de_unit_v::decimal")]
1985     Decimal,
1986     #[serde(deserialize_with = "de_unit_v::hexadecimal")]
1987     Hexadecimal,
1988     #[serde(deserialize_with = "de_unit_v::both")]
1989     Both,
1990 }
1991 
1992 macro_rules! _config_data {
1993     (struct $name:ident {
1994         $(
1995             $(#[doc=$doc:literal])*
1996             $field:ident $(| $alias:ident)*: $ty:ty = $default:expr,
1997         )*
1998     }) => {
1999         #[allow(non_snake_case)]
2000         #[derive(Debug, Clone)]
2001         struct $name { $($field: $ty,)* }
2002         impl $name {
2003             fn from_json(mut json: serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>) -> $name {
2004                 $name {$(
2005                     $field: get_field(
2006                         &mut json,
2007                         error_sink,
2008                         stringify!($field),
2009                         None$(.or(Some(stringify!($alias))))*,
2010                         $default,
2011                     ),
2012                 )*}
2013             }
2014 
2015             fn json_schema() -> serde_json::Value {
2016                 schema(&[
2017                     $({
2018                         let field = stringify!($field);
2019                         let ty = stringify!($ty);
2020 
2021                         (field, ty, &[$($doc),*], $default)
2022                     },)*
2023                 ])
2024             }
2025 
2026             #[cfg(test)]
2027             fn manual() -> String {
2028                 manual(&[
2029                     $({
2030                         let field = stringify!($field);
2031                         let ty = stringify!($ty);
2032 
2033                         (field, ty, &[$($doc),*], $default)
2034                     },)*
2035                 ])
2036             }
2037         }
2038 
2039         #[test]
2040         fn fields_are_sorted() {
2041             [$(stringify!($field)),*].windows(2).for_each(|w| assert!(w[0] <= w[1], "{} <= {} does not hold", w[0], w[1]));
2042         }
2043     };
2044 }
2045 use _config_data as config_data;
2046 
get_field<T: DeserializeOwned>( json: &mut serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>, field: &'static str, alias: Option<&'static str>, default: &str, ) -> T2047 fn get_field<T: DeserializeOwned>(
2048     json: &mut serde_json::Value,
2049     error_sink: &mut Vec<(String, serde_json::Error)>,
2050     field: &'static str,
2051     alias: Option<&'static str>,
2052     default: &str,
2053 ) -> T {
2054     // XXX: check alias first, to work around the VS Code where it pre-fills the
2055     // defaults instead of sending an empty object.
2056     alias
2057         .into_iter()
2058         .chain(iter::once(field))
2059         .filter_map(move |field| {
2060             let mut pointer = field.replace('_', "/");
2061             pointer.insert(0, '/');
2062             json.pointer_mut(&pointer)
2063                 .map(|it| serde_json::from_value(it.take()).map_err(|e| (e, pointer)))
2064         })
2065         .find(Result::is_ok)
2066         .and_then(|res| match res {
2067             Ok(it) => Some(it),
2068             Err((e, pointer)) => {
2069                 tracing::warn!("Failed to deserialize config field at {}: {:?}", pointer, e);
2070                 error_sink.push((pointer, e));
2071                 None
2072             }
2073         })
2074         .unwrap_or_else(|| {
2075             serde_json::from_str(default).unwrap_or_else(|e| panic!("{e} on: `{default}`"))
2076         })
2077 }
2078 
schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json::Value2079 fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json::Value {
2080     let map = fields
2081         .iter()
2082         .map(|(field, ty, doc, default)| {
2083             let name = field.replace('_', ".");
2084             let name = format!("rust-analyzer.{name}");
2085             let props = field_props(field, ty, doc, default);
2086             (name, props)
2087         })
2088         .collect::<serde_json::Map<_, _>>();
2089     map.into()
2090 }
2091 
field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value2092 fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value {
2093     let doc = doc_comment_to_string(doc);
2094     let doc = doc.trim_end_matches('\n');
2095     assert!(
2096         doc.ends_with('.') && doc.starts_with(char::is_uppercase),
2097         "bad docs for {field}: {doc:?}"
2098     );
2099     let default = default.parse::<serde_json::Value>().unwrap();
2100 
2101     let mut map = serde_json::Map::default();
2102     macro_rules! set {
2103         ($($key:literal: $value:tt),*$(,)?) => {{$(
2104             map.insert($key.into(), serde_json::json!($value));
2105         )*}};
2106     }
2107     set!("markdownDescription": doc);
2108     set!("default": default);
2109 
2110     match ty {
2111         "bool" => set!("type": "boolean"),
2112         "usize" => set!("type": "integer", "minimum": 0),
2113         "String" => set!("type": "string"),
2114         "Vec<String>" => set! {
2115             "type": "array",
2116             "items": { "type": "string" },
2117         },
2118         "Vec<PathBuf>" => set! {
2119             "type": "array",
2120             "items": { "type": "string" },
2121         },
2122         "FxHashSet<String>" => set! {
2123             "type": "array",
2124             "items": { "type": "string" },
2125             "uniqueItems": true,
2126         },
2127         "FxHashMap<Box<str>, Box<[Box<str>]>>" => set! {
2128             "type": "object",
2129         },
2130         "FxHashMap<String, SnippetDef>" => set! {
2131             "type": "object",
2132         },
2133         "FxHashMap<String, String>" => set! {
2134             "type": "object",
2135         },
2136         "FxHashMap<Box<str>, usize>" => set! {
2137             "type": "object",
2138         },
2139         "Option<usize>" => set! {
2140             "type": ["null", "integer"],
2141             "minimum": 0,
2142         },
2143         "Option<String>" => set! {
2144             "type": ["null", "string"],
2145         },
2146         "Option<PathBuf>" => set! {
2147             "type": ["null", "string"],
2148         },
2149         "Option<bool>" => set! {
2150             "type": ["null", "boolean"],
2151         },
2152         "Option<Vec<String>>" => set! {
2153             "type": ["null", "array"],
2154             "items": { "type": "string" },
2155         },
2156         "ExprFillDefaultDef" => set! {
2157             "type": "string",
2158             "enum": ["todo", "default"],
2159             "enumDescriptions": [
2160                 "Fill missing expressions with the `todo` macro",
2161                 "Fill missing expressions with reasonable defaults, `new` or `default` constructors."
2162             ],
2163         },
2164         "ImportGranularityDef" => set! {
2165             "type": "string",
2166             "enum": ["preserve", "crate", "module", "item"],
2167             "enumDescriptions": [
2168                 "Do not change the granularity of any imports and preserve the original structure written by the developer.",
2169                 "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.",
2170                 "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.",
2171                 "Flatten imports so that each has its own use statement."
2172             ],
2173         },
2174         "ImportPrefixDef" => set! {
2175             "type": "string",
2176             "enum": [
2177                 "plain",
2178                 "self",
2179                 "crate"
2180             ],
2181             "enumDescriptions": [
2182                 "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item.",
2183                 "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.",
2184                 "Force import paths to be absolute by always starting them with `crate` or the extern crate name they come from."
2185             ],
2186         },
2187         "Vec<ManifestOrProjectJson>" => set! {
2188             "type": "array",
2189             "items": { "type": ["string", "object"] },
2190         },
2191         "WorkspaceSymbolSearchScopeDef" => set! {
2192             "type": "string",
2193             "enum": ["workspace", "workspace_and_dependencies"],
2194             "enumDescriptions": [
2195                 "Search in current workspace only.",
2196                 "Search in current workspace and dependencies."
2197             ],
2198         },
2199         "WorkspaceSymbolSearchKindDef" => set! {
2200             "type": "string",
2201             "enum": ["only_types", "all_symbols"],
2202             "enumDescriptions": [
2203                 "Search for types only.",
2204                 "Search for all symbols kinds."
2205             ],
2206         },
2207         "ParallelCachePrimingNumThreads" => set! {
2208             "type": "number",
2209             "minimum": 0,
2210             "maximum": 255
2211         },
2212         "LifetimeElisionDef" => set! {
2213             "type": "string",
2214             "enum": [
2215                 "always",
2216                 "never",
2217                 "skip_trivial"
2218             ],
2219             "enumDescriptions": [
2220                 "Always show lifetime elision hints.",
2221                 "Never show lifetime elision hints.",
2222                 "Only show lifetime elision hints if a return type is involved."
2223             ]
2224         },
2225         "ClosureReturnTypeHintsDef" => set! {
2226             "type": "string",
2227             "enum": [
2228                 "always",
2229                 "never",
2230                 "with_block"
2231             ],
2232             "enumDescriptions": [
2233                 "Always show type hints for return types of closures.",
2234                 "Never show type hints for return types of closures.",
2235                 "Only show type hints for return types of closures with blocks."
2236             ]
2237         },
2238         "ReborrowHintsDef" => set! {
2239             "type": "string",
2240             "enum": [
2241                 "always",
2242                 "never",
2243                 "mutable"
2244             ],
2245             "enumDescriptions": [
2246                 "Always show reborrow hints.",
2247                 "Never show reborrow hints.",
2248                 "Only show mutable reborrow hints."
2249             ]
2250         },
2251         "AdjustmentHintsDef" => set! {
2252             "type": "string",
2253             "enum": [
2254                 "always",
2255                 "never",
2256                 "reborrow"
2257             ],
2258             "enumDescriptions": [
2259                 "Always show all adjustment hints.",
2260                 "Never show adjustment hints.",
2261                 "Only show auto borrow and dereference adjustment hints."
2262             ]
2263         },
2264         "DiscriminantHintsDef" => set! {
2265             "type": "string",
2266             "enum": [
2267                 "always",
2268                 "never",
2269                 "fieldless"
2270             ],
2271             "enumDescriptions": [
2272                 "Always show all discriminant hints.",
2273                 "Never show discriminant hints.",
2274                 "Only show discriminant hints on fieldless enum variants."
2275             ]
2276         },
2277         "AdjustmentHintsModeDef" => set! {
2278             "type": "string",
2279             "enum": [
2280                 "prefix",
2281                 "postfix",
2282                 "prefer_prefix",
2283                 "prefer_postfix",
2284             ],
2285             "enumDescriptions": [
2286                 "Always show adjustment hints as prefix (`*expr`).",
2287                 "Always show adjustment hints as postfix (`expr.*`).",
2288                 "Show prefix or postfix depending on which uses less parenthesis, preferring prefix.",
2289                 "Show prefix or postfix depending on which uses less parenthesis, preferring postfix.",
2290             ]
2291         },
2292         "CargoFeaturesDef" => set! {
2293             "anyOf": [
2294                 {
2295                     "type": "string",
2296                     "enum": [
2297                         "all"
2298                     ],
2299                     "enumDescriptions": [
2300                         "Pass `--all-features` to cargo",
2301                     ]
2302                 },
2303                 {
2304                     "type": "array",
2305                     "items": { "type": "string" }
2306                 }
2307             ],
2308         },
2309         "Option<CargoFeaturesDef>" => set! {
2310             "anyOf": [
2311                 {
2312                     "type": "string",
2313                     "enum": [
2314                         "all"
2315                     ],
2316                     "enumDescriptions": [
2317                         "Pass `--all-features` to cargo",
2318                     ]
2319                 },
2320                 {
2321                     "type": "array",
2322                     "items": { "type": "string" }
2323                 },
2324                 { "type": "null" }
2325             ],
2326         },
2327         "CallableCompletionDef" => set! {
2328             "type": "string",
2329             "enum": [
2330                 "fill_arguments",
2331                 "add_parentheses",
2332                 "none",
2333             ],
2334             "enumDescriptions": [
2335                 "Add call parentheses and pre-fill arguments.",
2336                 "Add call parentheses.",
2337                 "Do no snippet completions for callables."
2338             ]
2339         },
2340         "SignatureDetail" => set! {
2341             "type": "string",
2342             "enum": ["full", "parameters"],
2343             "enumDescriptions": [
2344                 "Show the entire signature.",
2345                 "Show only the parameters."
2346             ],
2347         },
2348         "FilesWatcherDef" => set! {
2349             "type": "string",
2350             "enum": ["client", "server"],
2351             "enumDescriptions": [
2352                 "Use the client (editor) to watch files for changes",
2353                 "Use server-side file watching",
2354             ],
2355         },
2356         "AnnotationLocation" => set! {
2357             "type": "string",
2358             "enum": ["above_name", "above_whole_item"],
2359             "enumDescriptions": [
2360                 "Render annotations above the name of the item.",
2361                 "Render annotations above the whole item, including documentation comments and attributes."
2362             ],
2363         },
2364         "InvocationStrategy" => set! {
2365             "type": "string",
2366             "enum": ["per_workspace", "once"],
2367             "enumDescriptions": [
2368                 "The command will be executed for each workspace.",
2369                 "The command will be executed once."
2370             ],
2371         },
2372         "InvocationLocation" => set! {
2373             "type": "string",
2374             "enum": ["workspace", "root"],
2375             "enumDescriptions": [
2376                 "The command will be executed in the corresponding workspace root.",
2377                 "The command will be executed in the project root."
2378             ],
2379         },
2380         "Option<CheckOnSaveTargets>" => set! {
2381             "anyOf": [
2382                 {
2383                     "type": "null"
2384                 },
2385                 {
2386                     "type": "string",
2387                 },
2388                 {
2389                     "type": "array",
2390                     "items": { "type": "string" }
2391                 },
2392             ],
2393         },
2394         "ClosureStyle" => set! {
2395             "type": "string",
2396             "enum": ["impl_fn", "rust_analyzer", "with_id", "hide"],
2397             "enumDescriptions": [
2398                 "`impl_fn`: `impl FnMut(i32, u64) -> i8`",
2399                 "`rust_analyzer`: `|i32, u64| -> i8`",
2400                 "`with_id`: `{closure#14352}`, where that id is the unique number of the closure in r-a internals",
2401                 "`hide`: Shows `...` for every closure type",
2402             ],
2403         },
2404         "Option<MemoryLayoutHoverRenderKindDef>" => set! {
2405             "anyOf": [
2406                 {
2407                     "type": "null"
2408                 },
2409                 {
2410                     "type": "string",
2411                     "enum": ["both", "decimal", "hexadecimal", ],
2412                     "enumDescriptions": [
2413                         "Render as 12 (0xC)",
2414                         "Render as 12",
2415                         "Render as 0xC"
2416                     ],
2417                 },
2418             ],
2419         },
2420         _ => panic!("missing entry for {ty}: {default}"),
2421     }
2422 
2423     map.into()
2424 }
2425 
2426 #[cfg(test)]
manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String2427 fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String {
2428     fields
2429         .iter()
2430         .map(|(field, _ty, doc, default)| {
2431             let name = format!("rust-analyzer.{}", field.replace('_', "."));
2432             let doc = doc_comment_to_string(doc);
2433             if default.contains('\n') {
2434                 format!(
2435                     r#"[[{name}]]{name}::
2436 +
2437 --
2438 Default:
2439 ----
2440 {default}
2441 ----
2442 {doc}
2443 --
2444 "#
2445                 )
2446             } else {
2447                 format!("[[{name}]]{name} (default: `{default}`)::\n+\n--\n{doc}--\n")
2448             }
2449         })
2450         .collect::<String>()
2451 }
2452 
doc_comment_to_string(doc: &[&str]) -> String2453 fn doc_comment_to_string(doc: &[&str]) -> String {
2454     doc.iter().map(|it| it.strip_prefix(' ').unwrap_or(it)).map(|it| format!("{it}\n")).collect()
2455 }
2456 
2457 #[cfg(test)]
2458 mod tests {
2459     use std::fs;
2460 
2461     use test_utils::{ensure_file_contents, project_root};
2462 
2463     use super::*;
2464 
2465     #[test]
generate_package_json_config()2466     fn generate_package_json_config() {
2467         let s = Config::json_schema();
2468         let schema = format!("{s:#}");
2469         let mut schema = schema
2470             .trim_start_matches('{')
2471             .trim_end_matches('}')
2472             .replace("  ", "    ")
2473             .replace('\n', "\n            ")
2474             .trim_start_matches('\n')
2475             .trim_end()
2476             .to_string();
2477         schema.push_str(",\n");
2478 
2479         // Transform the asciidoc form link to markdown style.
2480         //
2481         // https://link[text] => [text](https://link)
2482         let url_matches = schema.match_indices("https://");
2483         let mut url_offsets = url_matches.map(|(idx, _)| idx).collect::<Vec<usize>>();
2484         url_offsets.reverse();
2485         for idx in url_offsets {
2486             let link = &schema[idx..];
2487             // matching on whitespace to ignore normal links
2488             if let Some(link_end) = link.find(|c| c == ' ' || c == '[') {
2489                 if link.chars().nth(link_end) == Some('[') {
2490                     if let Some(link_text_end) = link.find(']') {
2491                         let link_text = link[link_end..(link_text_end + 1)].to_string();
2492 
2493                         schema.replace_range((idx + link_end)..(idx + link_text_end + 1), "");
2494                         schema.insert(idx, '(');
2495                         schema.insert(idx + link_end + 1, ')');
2496                         schema.insert_str(idx, &link_text);
2497                     }
2498                 }
2499             }
2500         }
2501 
2502         let package_json_path = project_root().join("editors/code/package.json");
2503         let mut package_json = fs::read_to_string(&package_json_path).unwrap();
2504 
2505         let start_marker = "                \"$generated-start\": {},\n";
2506         let end_marker = "                \"$generated-end\": {}\n";
2507 
2508         let start = package_json.find(start_marker).unwrap() + start_marker.len();
2509         let end = package_json.find(end_marker).unwrap();
2510 
2511         let p = remove_ws(&package_json[start..end]);
2512         let s = remove_ws(&schema);
2513         if !p.contains(&s) {
2514             package_json.replace_range(start..end, &schema);
2515             ensure_file_contents(&package_json_path, &package_json)
2516         }
2517     }
2518 
2519     #[test]
generate_config_documentation()2520     fn generate_config_documentation() {
2521         let docs_path = project_root().join("docs/user/generated_config.adoc");
2522         let expected = ConfigData::manual();
2523         ensure_file_contents(&docs_path, &expected);
2524     }
2525 
remove_ws(text: &str) -> String2526     fn remove_ws(text: &str) -> String {
2527         text.replace(char::is_whitespace, "")
2528     }
2529 
2530     #[test]
proc_macro_srv_null()2531     fn proc_macro_srv_null() {
2532         let mut config =
2533             Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]);
2534         config
2535             .update(serde_json::json!({
2536                 "procMacro_server": null,
2537             }))
2538             .unwrap();
2539         assert_eq!(config.proc_macro_srv(), None);
2540     }
2541 
2542     #[test]
proc_macro_srv_abs()2543     fn proc_macro_srv_abs() {
2544         let mut config =
2545             Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]);
2546         config
2547             .update(serde_json::json!({
2548                 "procMacro": {"server": project_root().display().to_string()}
2549             }))
2550             .unwrap();
2551         assert_eq!(config.proc_macro_srv(), Some(AbsPathBuf::try_from(project_root()).unwrap()));
2552     }
2553 
2554     #[test]
proc_macro_srv_rel()2555     fn proc_macro_srv_rel() {
2556         let mut config =
2557             Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]);
2558         config
2559             .update(serde_json::json!({
2560                 "procMacro": {"server": "./server"}
2561             }))
2562             .unwrap();
2563         assert_eq!(
2564             config.proc_macro_srv(),
2565             Some(AbsPathBuf::try_from(project_root().join("./server")).unwrap())
2566         );
2567     }
2568 }
2569