X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=crates%2Frust-analyzer%2Fsrc%2Fconfig.rs;h=5826b6b114c2b455e68eab0d2a08e4b5eacf5f74;hb=a68ce62f6a33d3e5777c1d64c946e0f1d9c7f457;hp=a4caccbe63c6e7266224a0bf33196e5a7c784beb;hpb=6494193adb5524e3df729b1a8b4043d25ff9db36;p=rust.git diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index a4caccbe63c..5826b6b114c 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -12,22 +12,27 @@ 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 = "{}", /// 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 = "[]", /// 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 = "25", + inlayHints_maxLength: Option = "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 = "[]", /// 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 = "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, pub discovered_projects: Option>, pub root_path: AbsPathBuf, + snippets: Vec, } #[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 { 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 = + 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, + #[serde(deserialize_with = "single_or_array")] + postfix: Vec, + description: Option, + #[serde(deserialize_with = "single_or_array")] + body: Vec, + #[serde(deserialize_with = "single_or_array")] + requires: Vec, + scope: SnippetScopeDef, +} + +fn single_or_array<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + struct SingleOrVec; + + impl<'de> serde::de::Visitor<'de> for SingleOrVec { + type Value = Vec; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("string or array of strings") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + Ok(vec![value.to_owned()]) + } + + fn visit_seq(self, seq: A) -> Result + 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" => set! { + "type": "object", + }, "FxHashMap" => 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::>(); + 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();