]> git.lizzy.rs Git - rust.git/blobdiff - crates/rust-analyzer/src/config.rs
Add toggle to disable cache priming
[rust.git] / crates / rust-analyzer / src / config.rs
index a4caccbe63c6e7266224a0bf33196e5a7c784beb..5826b6b114c2b455e68eab0d2a08e4b5eacf5f74 100644 (file)
 use flycheck::FlycheckConfig;
 use ide::{
     AssistConfig, CompletionConfig, DiagnosticsConfig, HighlightRelatedConfig, HoverConfig,
-    HoverDocFormat, InlayHintsConfig, JoinLinesConfig,
+    HoverDocFormat, InlayHintsConfig, JoinLinesConfig, Snippet, SnippetScope,
 };
 use ide_db::helpers::{
     insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
     SnippetCap,
 };
 use lsp_types::{ClientCapabilities, MarkupKind};
-use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
+use project_model::{
+    CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource, UnsetTestCrates,
+};
 use rustc_hash::{FxHashMap, FxHashSet};
 use serde::{de::DeserializeOwned, Deserialize};
 use vfs::AbsPathBuf;
 
 use crate::{
-    caps::completion_item_edit_resolve, diagnostics::DiagnosticsMapConfig,
-    line_index::OffsetEncoding, lsp_ext::supports_utf8, lsp_ext::WorkspaceSymbolSearchKind,
+    caps::completion_item_edit_resolve,
+    diagnostics::DiagnosticsMapConfig,
+    line_index::OffsetEncoding,
+    lsp_ext::supports_utf8,
     lsp_ext::WorkspaceSymbolSearchScope,
+    lsp_ext::{self, WorkspaceSymbolSearchKind},
 };
 
 // Defines the server-side configuration of the rust-analyzer. We generate
@@ -48,11 +53,14 @@ struct ConfigData {
         assist_importEnforceGranularity: bool              = "false",
         /// The path structure for newly inserted paths to use.
         assist_importPrefix: ImportPrefixDef               = "\"plain\"",
-        /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
+        /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.
         assist_importGroup: bool                           = "true",
         /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
         assist_allowMergingIntoGlobImports: bool           = "true",
 
+        /// Warm up caches on project load.
+        cache_warmup: bool = "true",
+
         /// Show function name and docs in parameter hints.
         callInfo_full: bool                                = "true",
 
@@ -107,6 +115,8 @@ struct ConfigData {
         completion_addCallArgumentSnippets: bool = "true",
         /// Whether to add parenthesis when completing functions.
         completion_addCallParenthesis: bool      = "true",
+        /// Custom completion snippets.
+        completion_snippets: FxHashMap<String, SnippetDef> = "{}",
         /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
         completion_postfix_enable: bool          = "true",
         /// Toggles the additional completions that automatically add imports when completed.
@@ -138,7 +148,7 @@ struct ConfigData {
         diagnostics_warningsAsInfo: Vec<String> = "[]",
 
         /// Expand attribute macros.
-        experimental_procAttrMacros: bool = "false",
+        experimental_procAttrMacros: bool = "true",
 
         /// Controls file watching implementation.
         files_watcher: String = "\"client\"",
@@ -188,14 +198,16 @@ struct ConfigData {
         hoverActions_run: bool             = "true",
 
         /// Whether to show inlay type hints for method chains.
-        inlayHints_chainingHints: bool      = "true",
+        inlayHints_chainingHints: bool              = "true",
         /// Maximum length for inlay hints. Set to null to have an unlimited length.
-        inlayHints_maxLength: Option<usize> = "25",
+        inlayHints_maxLength: Option<usize>         = "25",
         /// Whether to show function parameter name inlay hints at the call
         /// site.
-        inlayHints_parameterHints: bool     = "true",
+        inlayHints_parameterHints: bool             = "true",
         /// Whether to show inlay type hints for variables.
-        inlayHints_typeHints: bool          = "true",
+        inlayHints_typeHints: bool                  = "true",
+        /// Whether to hide inlay hints for constructors.
+        inlayHints_hideNamedConstructorHints: bool  = "false",
 
         /// Join lines inserts else between consecutive ifs.
         joinLines_joinElseIf: bool = "true",
@@ -203,6 +215,8 @@ struct ConfigData {
         joinLines_removeTrailingComma: bool = "true",
         /// Join lines unwraps trivial blocks.
         joinLines_unwrapTrivialBlock: bool = "true",
+        /// Join lines merges consecutive declaration and initialization of an assignment.
+        joinLines_joinAssignments: bool = "true",
 
         /// Whether to show `Debug` lens. Only applies when
         /// `#rust-analyzer.lens.enable#` is set.
@@ -218,9 +232,15 @@ struct ConfigData {
         /// Whether to show `Method References` lens. Only applies when
         /// `#rust-analyzer.lens.enable#` is set.
         lens_methodReferences: bool = "false",
-        /// Whether to show `References` lens. Only applies when
-        /// `#rust-analyzer.lens.enable#` is set.
+        /// Whether to show `References` lens for Struct, Enum, Union and Trait.
+        /// Only applies when `#rust-analyzer.lens.enable#` is set.
         lens_references: bool = "false",
+        /// Whether to show `References` lens for Enum Variants.
+        /// Only applies when `#rust-analyzer.lens.enable#` is set.
+        lens_enumVariantReferences: bool = "false",
+        /// Internal config: use custom client-side commands even when the
+        /// client doesn't set the corresponding capability.
+        lens_forceCustomCommands: bool = "true",
 
         /// Disable project auto-discovery in favor of explicitly specified set
         /// of projects.
@@ -248,12 +268,13 @@ struct ConfigData {
         runnables_cargoExtraArgs: Vec<String>   = "[]",
 
         /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
-        /// projects, or "discover" to try to automatically find it.
+        /// projects, or "discover" to try to automatically find it if the `rustc-dev` component
+        /// is installed.
         ///
         /// Any project which uses rust-analyzer with the rustcPrivate
         /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
         ///
-        /// This option is not reloaded automatically; you must restart rust-analyzer for it to take effect.
+        /// This option does not take effect until rust-analyzer is restarted.
         rustcSource: Option<String> = "null",
 
         /// Additional arguments to `rustfmt`.
@@ -267,9 +288,9 @@ struct ConfigData {
         rustfmt_enableRangeFormatting: bool = "false",
 
         /// Workspace symbol search scope.
-        workspace_symbol_search_scope: WorskpaceSymbolSearchScopeDef = "\"workspace\"",
+        workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = "\"workspace\"",
         /// Workspace symbol search kind.
-        workspace_symbol_search_kind: WorskpaceSymbolSearchKindDef = "\"only_types\"",
+        workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = "\"only_types\"",
     }
 }
 
@@ -286,6 +307,7 @@ pub struct Config {
     detached_files: Vec<AbsPathBuf>,
     pub discovered_projects: Option<Vec<ProjectManifest>>,
     pub root_path: AbsPathBuf,
+    snippets: Vec<Snippet>,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
@@ -313,6 +335,7 @@ pub struct LensConfig {
     pub implementations: bool,
     pub method_refs: bool,
     pub refs: bool, // for Struct, Enum, Union and Trait
+    pub enum_variant_refs: bool,
 }
 
 impl LensConfig {
@@ -329,7 +352,7 @@ pub fn runnable(&self) -> bool {
     }
 
     pub fn references(&self) -> bool {
-        self.method_refs || self.refs
+        self.method_refs || self.refs || self.enum_variant_refs
     }
 }
 
@@ -405,6 +428,14 @@ pub struct WorkspaceSymbolConfig {
     pub search_kind: WorkspaceSymbolSearchKind,
 }
 
+pub struct ClientCommandsConfig {
+    pub run_single: bool,
+    pub debug_single: bool,
+    pub show_reference: bool,
+    pub goto_location: bool,
+    pub trigger_parameter_hints: bool,
+}
+
 impl Config {
     pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self {
         Config {
@@ -413,10 +444,11 @@ pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self {
             detached_files: Vec::new(),
             discovered_projects: None,
             root_path,
+            snippets: Default::default(),
         }
     }
     pub fn update(&mut self, mut json: serde_json::Value) {
-        log::info!("updating config from JSON: {:#}", json);
+        tracing::info!("updating config from JSON: {:#}", json);
         if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
             return;
         }
@@ -425,6 +457,28 @@ pub fn update(&mut self, mut json: serde_json::Value) {
             .map(AbsPathBuf::assert)
             .collect();
         self.data = ConfigData::from_json(json);
+        self.snippets.clear();
+        for (name, def) in self.data.completion_snippets.iter() {
+            if def.prefix.is_empty() && def.postfix.is_empty() {
+                continue;
+            }
+            let scope = match def.scope {
+                SnippetScopeDef::Expr => SnippetScope::Expr,
+                SnippetScopeDef::Type => SnippetScope::Type,
+                SnippetScopeDef::Item => SnippetScope::Item,
+            };
+            match Snippet::new(
+                &def.prefix,
+                &def.postfix,
+                &def.body,
+                def.description.as_ref().unwrap_or(name),
+                &def.requires,
+                scope,
+            ) {
+                Some(snippet) => self.snippets.push(snippet),
+                None => tracing::info!("Invalid snippet {}", name),
+            }
+        }
     }
 
     pub fn json_schema() -> serde_json::Value {
@@ -462,7 +516,9 @@ pub fn linked_projects(&self) -> Vec<LinkedProject> {
                         ManifestOrProjectJson::Manifest(it) => {
                             let path = self.root_path.join(it);
                             ProjectManifest::from_manifest_file(path)
-                                .map_err(|e| log::error!("failed to load linked project: {}", e))
+                                .map_err(|e| {
+                                    tracing::error!("failed to load linked project: {}", e)
+                                })
                                 .ok()?
                                 .into()
                         }
@@ -492,6 +548,10 @@ pub fn did_change_watched_files_dynamic_registration(&self) -> bool {
         )
     }
 
+    pub fn prefill_caches(&self) -> bool {
+        self.data.cache_warmup
+    }
+
     pub fn location_link(&self) -> bool {
         try_or!(self.caps.text_document.as_ref()?.definition?.link_support?, false)
     }
@@ -582,9 +642,6 @@ fn experimental(&self, index: &'static str) -> bool {
     pub fn code_action_group(&self) -> bool {
         self.experimental("codeActionGroup")
     }
-    pub fn experimental_hover_actions(&self) -> bool {
-        self.experimental("hoverActions")
-    }
     pub fn server_status_notification(&self) -> bool {
         self.experimental("serverStatusNotification")
     }
@@ -653,9 +710,9 @@ pub fn cargo(&self) -> CargoConfig {
             all_features: self.data.cargo_allFeatures,
             features: self.data.cargo_features.clone(),
             target: self.data.cargo_target.clone(),
-            rustc_source,
             no_sysroot: self.data.cargo_noSysroot,
-            unset_test_crates: self.data.cargo_unsetTest.clone(),
+            rustc_source,
+            unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()),
             wrap_rustc_in_build_scripts: self.data.cargo_useRustcWrapperForBuildScripts,
         }
     }
@@ -720,6 +777,7 @@ pub fn inlay_hints(&self) -> InlayHintsConfig {
             type_hints: self.data.inlayHints_typeHints,
             parameter_hints: self.data.inlayHints_parameterHints,
             chaining_hints: self.data.inlayHints_chainingHints,
+            hide_named_constructor_hints: self.data.inlayHints_hideNamedConstructorHints,
             max_length: self.data.inlayHints_maxLength,
         }
     }
@@ -761,6 +819,7 @@ pub fn completion(&self) -> CompletionConfig {
                     .snippet_support?,
                 false
             )),
+            snippets: self.snippets.clone(),
         }
     }
     pub fn assist(&self) -> AssistConfig {
@@ -775,6 +834,7 @@ pub fn join_lines(&self) -> JoinLinesConfig {
             join_else_if: self.data.joinLines_joinElseIf,
             remove_trailing_comma: self.data.joinLines_removeTrailingComma,
             unwrap_trivial_blocks: self.data.joinLines_unwrapTrivialBlock,
+            join_assignments: self.data.joinLines_joinAssignments,
         }
     }
     pub fn call_info_full(&self) -> bool {
@@ -787,16 +847,17 @@ pub fn lens(&self) -> LensConfig {
             implementations: self.data.lens_enable && self.data.lens_implementations,
             method_refs: self.data.lens_enable && self.data.lens_methodReferences,
             refs: self.data.lens_enable && self.data.lens_references,
+            enum_variant_refs: self.data.lens_enable && self.data.lens_enumVariantReferences,
         }
     }
     pub fn hover_actions(&self) -> HoverActionsConfig {
+        let enable = self.experimental("hoverActions") && self.data.hoverActions_enable;
         HoverActionsConfig {
-            implementations: self.data.hoverActions_enable
-                && self.data.hoverActions_implementations,
-            references: self.data.hoverActions_enable && self.data.hoverActions_references,
-            run: self.data.hoverActions_enable && self.data.hoverActions_run,
-            debug: self.data.hoverActions_enable && self.data.hoverActions_debug,
-            goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef,
+            implementations: enable && self.data.hoverActions_implementations,
+            references: enable && self.data.hoverActions_references,
+            run: enable && self.data.hoverActions_run,
+            debug: enable && self.data.hoverActions_debug,
+            goto_type_def: enable && self.data.hoverActions_gotoTypeDef,
         }
     }
     pub fn highlighting_strings(&self) -> bool {
@@ -830,14 +891,14 @@ pub fn hover(&self) -> HoverConfig {
     pub fn workspace_symbol(&self) -> WorkspaceSymbolConfig {
         WorkspaceSymbolConfig {
             search_scope: match self.data.workspace_symbol_search_scope {
-                WorskpaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace,
-                WorskpaceSymbolSearchScopeDef::WorkspaceAndDependencies => {
+                WorkspaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace,
+                WorkspaceSymbolSearchScopeDef::WorkspaceAndDependencies => {
                     WorkspaceSymbolSearchScope::WorkspaceAndDependencies
                 }
             },
             search_kind: match self.data.workspace_symbol_search_kind {
-                WorskpaceSymbolSearchKindDef::OnlyTypes => WorkspaceSymbolSearchKind::OnlyTypes,
-                WorskpaceSymbolSearchKindDef::AllSymbols => WorkspaceSymbolSearchKind::AllSymbols,
+                WorkspaceSymbolSearchKindDef::OnlyTypes => WorkspaceSymbolSearchKind::OnlyTypes,
+                WorkspaceSymbolSearchKindDef::AllSymbols => WorkspaceSymbolSearchKind::AllSymbols,
             },
         }
     }
@@ -861,6 +922,24 @@ pub fn insert_replace_support(&self) -> bool {
             false
         )
     }
+    pub fn client_commands(&self) -> ClientCommandsConfig {
+        let commands =
+            try_or!(self.caps.experimental.as_ref()?.get("commands")?, &serde_json::Value::Null);
+        let commands: Option<lsp_ext::ClientCommandOptions> =
+            serde_json::from_value(commands.clone()).ok();
+        let force = commands.is_none() && self.data.lens_forceCustomCommands;
+        let commands = commands.map(|it| it.commands).unwrap_or_default();
+
+        let get = |name: &str| commands.iter().any(|it| it == name) || force;
+
+        ClientCommandsConfig {
+            run_single: get("rust-analyzer.runSingle"),
+            debug_single: get("rust-analyzer.debugSingle"),
+            show_reference: get("rust-analyzer.showReferences"),
+            goto_location: get("rust-analyzer.gotoLocation"),
+            trigger_parameter_hints: get("editor.action.triggerParameterHints"),
+        }
+    }
 
     pub fn highlight_related(&self) -> HighlightRelatedConfig {
         HighlightRelatedConfig {
@@ -872,6 +951,66 @@ pub fn highlight_related(&self) -> HighlightRelatedConfig {
     }
 }
 
+#[derive(Deserialize, Debug, Clone, Copy)]
+#[serde(rename_all = "snake_case")]
+enum SnippetScopeDef {
+    Expr,
+    Item,
+    Type,
+}
+
+impl Default for SnippetScopeDef {
+    fn default() -> Self {
+        SnippetScopeDef::Expr
+    }
+}
+
+#[derive(Deserialize, Debug, Clone, Default)]
+#[serde(default)]
+struct SnippetDef {
+    #[serde(deserialize_with = "single_or_array")]
+    prefix: Vec<String>,
+    #[serde(deserialize_with = "single_or_array")]
+    postfix: Vec<String>,
+    description: Option<String>,
+    #[serde(deserialize_with = "single_or_array")]
+    body: Vec<String>,
+    #[serde(deserialize_with = "single_or_array")]
+    requires: Vec<String>,
+    scope: SnippetScopeDef,
+}
+
+fn single_or_array<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
+where
+    D: serde::Deserializer<'de>,
+{
+    struct SingleOrVec;
+
+    impl<'de> serde::de::Visitor<'de> for SingleOrVec {
+        type Value = Vec<String>;
+
+        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+            formatter.write_str("string or array of strings")
+        }
+
+        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
+        where
+            E: serde::de::Error,
+        {
+            Ok(vec![value.to_owned()])
+        }
+
+        fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
+        where
+            A: serde::de::SeqAccess<'de>,
+        {
+            Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(seq))
+        }
+    }
+
+    deserializer.deserialize_any(SingleOrVec)
+}
+
 #[derive(Deserialize, Debug, Clone)]
 #[serde(untagged)]
 enum ManifestOrProjectJson {
@@ -903,14 +1042,14 @@ enum ImportPrefixDef {
 
 #[derive(Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
-enum WorskpaceSymbolSearchScopeDef {
+enum WorkspaceSymbolSearchScopeDef {
     Workspace,
     WorkspaceAndDependencies,
 }
 
 #[derive(Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
-enum WorskpaceSymbolSearchKindDef {
+enum WorkspaceSymbolSearchKindDef {
     OnlyTypes,
     AllSymbols,
 }
@@ -1041,6 +1180,9 @@ macro_rules! set {
             "items": { "type": "string" },
             "uniqueItems": true,
         },
+        "FxHashMap<String, SnippetDef>" => set! {
+            "type": "object",
+        },
         "FxHashMap<String, String>" => set! {
             "type": "object",
         },
@@ -1097,7 +1239,7 @@ macro_rules! set {
             "type": "array",
             "items": { "type": ["string", "object"] },
         },
-        "WorskpaceSymbolSearchScopeDef" => set! {
+        "WorkspaceSymbolSearchScopeDef" => set! {
             "type": "string",
             "enum": ["workspace", "workspace_and_dependencies"],
             "enumDescriptions": [
@@ -1105,7 +1247,7 @@ macro_rules! set {
                 "Search in current workspace and dependencies"
             ],
         },
-        "WorskpaceSymbolSearchKindDef" => set! {
+        "WorkspaceSymbolSearchKindDef" => set! {
             "type": "string",
             "enum": ["only_types", "all_symbols"],
             "enumDescriptions": [
@@ -1157,6 +1299,29 @@ fn generate_package_json_config() {
             .to_string();
         schema.push_str(",\n");
 
+        // Transform the asciidoc form link to markdown style.
+        //
+        // https://link[text] => [text](https://link)
+        let url_matches = schema.match_indices("https://");
+        let mut url_offsets = url_matches.map(|(idx, _)| idx).collect::<Vec<usize>>();
+        url_offsets.reverse();
+        for idx in url_offsets {
+            let link = &schema[idx..];
+            // matching on whitespace to ignore normal links
+            if let Some(link_end) = link.find(|c| c == ' ' || c == '[') {
+                if link.chars().nth(link_end) == Some('[') {
+                    if let Some(link_text_end) = link.find(']') {
+                        let link_text = link[link_end..(link_text_end + 1)].to_string();
+
+                        schema.replace_range((idx + link_end)..(idx + link_text_end + 1), "");
+                        schema.insert(idx, '(');
+                        schema.insert(idx + link_end + 1, ')');
+                        schema.insert_str(idx, &link_text);
+                    }
+                }
+            }
+        }
+
         let package_json_path = project_root().join("editors/code/package.json");
         let mut package_json = fs::read_to_string(&package_json_path).unwrap();