use flycheck::FlycheckConfig;
use ide::{
- AssistConfig, CompletionConfig, DiagnosticsConfig, HighlightRelatedConfig, HoverConfig,
- HoverDocFormat, InlayHintsConfig, JoinLinesConfig, Snippet, SnippetScope,
+ 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",
/// Whether to add parenthesis when completing functions.
completion_addCallParenthesis: bool = "true",
/// Custom completion snippets.
- completion_snippets: FxHashMap<String, SnippetDef> = "{}",
+ // 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",
/// Toggles the additional completions that automatically add imports when completed.
/// 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",
impl Default for ConfigData {
fn default() -> Self {
- ConfigData::from_json(serde_json::Value::Null)
+ ConfigData::from_json(serde_json::Value::Null, &mut Vec::new())
}
}
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(());
}
- self.detached_files = get_field::<Vec<PathBuf>>(&mut json, "detachedFiles", None, "[]")
- .into_iter()
- .map(AbsPathBuf::assert)
- .collect();
- self.data = ConfigData::from_json(json);
+ 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() {
None => tracing::info!("Invalid snippet {}", name),
}
}
+ if errors.is_empty() {
+ Ok(())
+ } else {
+ Err(errors)
+ }
}
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(),
}
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, Copy)]
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 {
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",
},
"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"],
"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>()
}