use flycheck::FlycheckConfig;
use ide::{
- AssistConfig, CompletionConfig, DiagnosticsConfig, HighlightRelatedConfig, HoverConfig,
- HoverDocFormat, InlayHintsConfig, JoinLinesConfig, PostfixSnippet,
+ AssistConfig, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, HighlightRelatedConfig,
+ HoverConfig, HoverDocFormat, InlayHintsConfig, JoinLinesConfig, Snippet, SnippetScope,
};
use ide_db::helpers::{
insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
// parsing the old name.
config_data! {
struct ConfigData {
+ /// Placeholder for missing expressions in assists.
+ assist_exprFillDefault: ExprFillDefaultDef = "\"todo\"",
/// How imports should be grouped into use statements.
assist_importGranularity |
assist_importMergeBehavior |
/// 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.
+ // NOTE: Keep this list in sync with the feature docs of user snippets.
+ completion_snippets: FxHashMap<String, SnippetDef> = r#"{
+ "Arc::new": {
+ "postfix": "arc",
+ "body": "Arc::new(${receiver})",
+ "requires": "std::sync::Arc",
+ "description": "Put the expression into an `Arc`",
+ "scope": "expr"
+ },
+ "Rc::new": {
+ "postfix": "rc",
+ "body": "Rc::new(${receiver})",
+ "requires": "std::rc::Rc",
+ "description": "Put the expression into an `Rc`",
+ "scope": "expr"
+ },
+ "Box::pin": {
+ "postfix": "pinbox",
+ "body": "Box::pin(${receiver})",
+ "requires": "std::boxed::Box",
+ "description": "Put the expression into a pinned `Box`",
+ "scope": "expr"
+ },
+ "Ok": {
+ "postfix": "ok",
+ "body": "Ok(${receiver})",
+ "description": "Wrap the expression in a `Result::Ok`",
+ "scope": "expr"
+ },
+ "Err": {
+ "postfix": "err",
+ "body": "Err(${receiver})",
+ "description": "Wrap the expression in a `Result::Err`",
+ "scope": "expr"
+ },
+ "Some": {
+ "postfix": "some",
+ "body": "Some(${receiver})",
+ "description": "Wrap the expression in an `Option::Some`",
+ "scope": "expr"
+ }
+ }"#,
/// Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
completion_postfix_enable: bool = "true",
- /// Custom postfix completions to show.
- completion_postfix_snippets: FxHashMap<String, PostfixSnippetDesc> = "{}",
/// Toggles the additional completions that automatically add imports when completed.
/// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
completion_autoimport_enable: bool = "true",
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",
/// 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",
/// Whether to show `can't find Cargo.toml` error message.
notifications_cargoTomlNotFound: bool = "true",
+ /// How many worker threads to to handle priming caches. The default `0` means to pick automatically.
+ primeCaches_numThreads: ParallelPrimeCachesNumThreads = "0",
+
/// Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScripts#`.
procMacro_enable: bool = "true",
/// Internal config, path to proc-macro server executable (typically,
/// this is rust-analyzer itself, but we override this in tests).
procMacro_server: Option<PathBuf> = "null",
+ /// These proc-macros will be ignored when trying to expand them.
+ ///
+ /// This config takes a map of crate names with the exported proc-macro names to ignore as values.
+ procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>> = "{}",
/// Command to be executed instead of 'cargo' for runnables.
runnables_overrideCargo: Option<String> = "null",
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\"",
}
}
impl Default for ConfigData {
fn default() -> Self {
- ConfigData::from_json(serde_json::Value::Null)
+ ConfigData::from_json(serde_json::Value::Null, &mut Vec::new())
}
}
detached_files: Vec<AbsPathBuf>,
pub discovered_projects: Option<Vec<ProjectManifest>>,
pub root_path: AbsPathBuf,
- postfix_snippets: Vec<ide::PostfixSnippet>,
+ 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
}
}
detached_files: Vec::new(),
discovered_projects: None,
root_path,
- postfix_snippets: Default::default(),
+ snippets: Default::default(),
}
}
- pub fn update(&mut self, mut json: serde_json::Value) {
+ pub fn update(
+ &mut self,
+ mut json: serde_json::Value,
+ ) -> Result<(), Vec<(String, serde_json::Error)>> {
tracing::info!("updating config from JSON: {:#}", json);
if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
- return;
+ return Ok(());
+ }
+ let mut errors = Vec::new();
+ self.detached_files =
+ get_field::<Vec<PathBuf>>(&mut json, &mut errors, "detachedFiles", None, "[]")
+ .into_iter()
+ .map(AbsPathBuf::assert)
+ .collect();
+ self.data = ConfigData::from_json(json, &mut errors);
+ 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),
+ }
+ }
+ if errors.is_empty() {
+ Ok(())
+ } else {
+ Err(errors)
}
- self.detached_files = get_field::<Vec<PathBuf>>(&mut json, "detachedFiles", None, "[]")
- .into_iter()
- .map(AbsPathBuf::assert)
- .collect();
- self.data = ConfigData::from_json(json);
- self.postfix_snippets = self
- .data
- .completion_postfix_snippets
- .iter()
- .flat_map(|(label, desc)| {
- PostfixSnippet::new(label.clone(), &desc.snippet, &desc.description, &desc.requires)
- })
- .collect();
}
pub fn json_schema() -> serde_json::Value {
)
}
+ 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)
}
DiagnosticsConfig {
disable_experimental: !self.data.diagnostics_enableExperimental,
disabled: self.data.diagnostics_disabled.clone(),
+ expr_fill_default: match self.data.assist_exprFillDefault {
+ ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
+ ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
+ },
}
}
pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
};
Some((path, vec!["proc-macro".into()]))
}
+ pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
+ &self.data.procMacro_ignored
+ }
pub fn expand_proc_attr_macros(&self) -> bool {
self.data.experimental_procAttrMacros
}
FilesConfig {
watcher: match self.data.files_watcher.as_str() {
"notify" => FilesWatcher::Notify,
- "client" | _ => FilesWatcher::Client,
+ "client" if self.did_change_watched_files_dynamic_registration() => {
+ FilesWatcher::Client
+ }
+ _ => FilesWatcher::Notify,
},
exclude: self.data.files_excludeDirs.iter().map(|it| self.root_path.join(it)).collect(),
}
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
)),
- postfix_snippets: self.postfix_snippets.clone(),
+ snippets: self.snippets.clone(),
}
}
pub fn assist(&self) -> AssistConfig {
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 {
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,
},
}
}
yield_points: self.data.highlightRelated_yieldPoints,
}
}
+
+ pub fn prime_caches_num_threads(&self) -> u8 {
+ match self.data.primeCaches_numThreads {
+ 0 => num_cpus::get_physical().try_into().unwrap_or(u8::MAX),
+ n => n,
+ }
+ }
}
-#[derive(Deserialize, Debug, Clone)]
-struct PostfixSnippetDesc {
+#[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")]
- description: Vec<String>,
+ prefix: Vec<String>,
#[serde(deserialize_with = "single_or_array")]
- snippet: Vec<String>,
+ 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>
ProjectJson(ProjectJsonData),
}
+#[derive(Deserialize, Debug, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum ExprFillDefaultDef {
+ #[serde(alias = "todo")]
+ Todo,
+ #[serde(alias = "default")]
+ Default,
+}
+
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
enum ImportGranularityDef {
#[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,
}
+type ParallelPrimeCachesNumThreads = u8;
+
macro_rules! _config_data {
(struct $name:ident {
$(
#[derive(Debug, Clone)]
struct $name { $($field: $ty,)* }
impl $name {
- fn from_json(mut json: serde_json::Value) -> $name {
+ fn from_json(mut json: serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>) -> $name {
$name {$(
$field: get_field(
&mut json,
+ error_sink,
stringify!($field),
None$(.or(Some(stringify!($alias))))*,
$default,
fn 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,
.find_map(move |field| {
let mut pointer = field.replace('_', "/");
pointer.insert(0, '/');
- json.pointer_mut(&pointer).and_then(|it| serde_json::from_value(it.take()).ok())
+ json.pointer_mut(&pointer).and_then(|it| match serde_json::from_value(it.take()) {
+ Ok(it) => Some(it),
+ Err(e) => {
+ tracing::warn!("Failed to deserialize config field at {}: {:?}", pointer, e);
+ error_sink.push((pointer, e));
+ None
+ }
+ })
})
.unwrap_or(default)
}
"items": { "type": "string" },
"uniqueItems": true,
},
+ "FxHashMap<Box<str>, Box<[Box<str>]>>" => set! {
+ "type": "object",
+ },
+ "FxHashMap<String, SnippetDef>" => set! {
+ "type": "object",
+ },
"FxHashMap<String, String>" => set! {
"type": "object",
},
"Merge imports from the same module into a single `use` statement."
],
},
+ "ExprFillDefaultDef" => set! {
+ "type": "string",
+ "enum": ["todo", "default"],
+ "enumDescriptions": [
+ "Fill missing expressions with the `todo` macro",
+ "Fill missing expressions with reasonable defaults, `new` or `default` constructors."
+ ],
+ },
"ImportGranularityDef" => set! {
"type": "string",
"enum": ["preserve", "crate", "module", "item"],
"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": [
"Search for all symbols kinds"
],
},
+ "ParallelPrimeCachesNumThreads" => set! {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 255
+ },
_ => panic!("{}: {}", ty, default),
}
.map(|(field, _ty, doc, default)| {
let name = format!("rust-analyzer.{}", field.replace("_", "."));
let doc = doc_comment_to_string(*doc);
- format!("[[{}]]{} (default: `{}`)::\n+\n--\n{}--\n", name, name, default, doc)
+ if default.contains('\n') {
+ format!(
+ r#"[[{}]]{}::
++
+--
+Default:
+----
+{}
+----
+{}
+--
+"#,
+ name, name, default, doc
+ )
+ } else {
+ format!("[[{}]]{} (default: `{}`)::\n+\n--\n{}--\n", name, name, default, doc)
+ }
})
.collect::<String>()
}
.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();