use flycheck::FlycheckConfig;
use ide::{
- AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, HoverDocFormat,
- InlayHintsConfig, JoinLinesConfig,
+ AssistConfig, CompletionConfig, DiagnosticsConfig, HighlightRelatedConfig, HoverConfig,
+ 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
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",
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.
diagnostics_warningsAsInfo: Vec<String> = "[]",
/// Expand attribute macros.
- experimental_procAttrMacros: bool = "false",
+ experimental_procAttrMacros: bool = "true",
/// Controls file watching implementation.
files_watcher: String = "\"client\"",
- /// These directories will be ignored by rust-analyzer.
+ /// These directories will be ignored by rust-analyzer. They are
+ /// relative to the workspace root, and globs are not supported. You may
+ /// also need to add the folders to Code's `files.watcherExclude`.
files_excludeDirs: Vec<PathBuf> = "[]",
+ /// Enables highlighting of related references while hovering your mouse above any identifier.
+ highlightRelated_references: bool = "true",
+ /// Enables highlighting of all exit points while hovering your mouse above any `return`, `?`, or return type arrow (`->`).
+ highlightRelated_exitPoints: bool = "true",
+ /// Enables highlighting of related references while hovering your mouse `break`, `loop`, `while`, or `for` keywords.
+ highlightRelated_breakPoints: bool = "true",
+ /// Enables highlighting of all break points for a loop or block context while hovering your mouse above any `async` or `await` keywords.
+ highlightRelated_yieldPoints: bool = "true",
+
/// Use semantic tokens for strings.
///
/// In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
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",
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.
/// 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.
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`.
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\"",
}
}
detached_files: Vec<AbsPathBuf>,
pub discovered_projects: Option<Vec<ProjectManifest>>,
pub root_path: AbsPathBuf,
+ snippets: Vec<Snippet>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub implementations: bool,
pub method_refs: bool,
pub refs: bool, // for Struct, Enum, Union and Trait
+ pub enum_variant_refs: bool,
}
impl LensConfig {
}
pub fn references(&self) -> bool {
- self.method_refs || self.refs
+ self.method_refs || self.refs || self.enum_variant_refs
}
}
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 {
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;
}
.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 {
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()
}
)
}
+ 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)
}
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")
}
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,
}
}
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,
}
}
.snippet_support?,
false
)),
+ snippets: self.snippets.clone(),
}
}
pub fn assist(&self) -> AssistConfig {
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 {
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 {
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,
},
}
}
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 {
+ references: self.data.highlightRelated_references,
+ break_points: self.data.highlightRelated_breakPoints,
+ exit_points: self.data.highlightRelated_exitPoints,
+ yield_points: self.data.highlightRelated_yieldPoints,
+ }
+ }
+}
+
+#[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)]
#[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,
}
"items": { "type": "string" },
"uniqueItems": true,
},
+ "FxHashMap<String, SnippetDef>" => set! {
+ "type": "object",
+ },
"FxHashMap<String, String>" => set! {
"type": "object",
},
"type": "array",
"items": { "type": ["string", "object"] },
},
- "WorskpaceSymbolSearchScopeDef" => set! {
+ "WorkspaceSymbolSearchScopeDef" => set! {
"type": "string",
"enum": ["workspace", "workspace_and_dependencies"],
"enumDescriptions": [
"Search in current workspace and dependencies"
],
},
- "WorskpaceSymbolSearchKindDef" => set! {
+ "WorkspaceSymbolSearchKindDef" => set! {
"type": "string",
"enum": ["only_types", "all_symbols"],
"enumDescriptions": [
.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();