Make ConfigData Ser and TOML De

This commit makes rust-analyzer::config module TOML ser and de.

Co-Authored-By: Cormac Relf <web@cormacrelf.net>
This commit is contained in:
Ali Bektas
2023-10-22 14:52:43 +02:00
committed by Lukas Wirth
parent 657b33b0cb
commit 67d8d2d4a0
14 changed files with 1398 additions and 951 deletions
Generated
+55
View File
@@ -781,6 +781,7 @@ checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4"
dependencies = [
"equivalent",
"hashbrown",
"serde",
]
[[package]]
@@ -1594,6 +1595,7 @@ dependencies = [
"ide",
"ide-db",
"ide-ssr",
"indexmap",
"itertools",
"load-cargo",
"lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1622,6 +1624,7 @@ dependencies = [
"test-fixture",
"test-utils",
"tikv-jemallocator",
"toml",
"toolchain",
"tracing",
"tracing-subscriber",
@@ -1775,6 +1778,15 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_spanned"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
dependencies = [
"serde",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
@@ -2025,6 +2037,40 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "toolchain"
version = "0.0.0"
@@ -2401,6 +2447,15 @@ version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]]
name = "winnow"
version = "0.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6"
dependencies = [
"memchr",
]
[[package]]
name = "write-json"
version = "0.1.4"
+4 -3
View File
@@ -19,6 +19,10 @@
// Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`,
// then the crate for the proc-macro hasn't been build yet as the build data is missing.
pub type ProcMacroPaths = FxHashMap<CrateId, Result<(Option<String>, AbsPathBuf), String>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SourceRootId(pub u32);
/// Files are grouped into source roots. A source root is a directory on the
/// file systems which is watched for changes. Typically it corresponds to a
/// Rust crate. Source roots *might* be nested: in this case, a file belongs to
@@ -26,9 +30,6 @@
/// source root, and the analyzer does not know the root path of the source root at
/// all. So, a file from one source root can't refer to a file in another source
/// root by path.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SourceRootId(pub u32);
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SourceRoot {
/// Sysroot or crates.io library.
+6 -3
View File
@@ -64,7 +64,7 @@
use ide_db::{
base_db::{
salsa::{self, ParallelDatabase},
CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, SourceDatabaseExt, VfsPath,
},
prime_caches, symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase,
};
@@ -271,6 +271,10 @@ pub fn status(&self, file_id: Option<FileId>) -> Cancellable<String> {
self.with_db(|db| status::status(db, file_id))
}
pub fn source_root(&self, file_id: FileId) -> Cancellable<SourceRootId> {
self.with_db(|db| db.file_source_root(file_id))
}
pub fn parallel_prime_caches<F>(&self, num_worker_threads: u8, cb: F) -> Cancellable<()>
where
F: Fn(ParallelPrimeCachesProgress) + Sync + std::panic::UnwindSafe,
@@ -280,7 +284,7 @@ pub fn parallel_prime_caches<F>(&self, num_worker_threads: u8, cb: F) -> Cancell
/// Gets the text of the source file.
pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<str>> {
self.with_db(|db| db.file_text(file_id))
self.with_db(|db| SourceDatabaseExt::file_text(db, file_id))
}
/// Gets the syntax tree of the file.
@@ -290,7 +294,6 @@ pub fn parse(&self, file_id: FileId) -> Cancellable<SourceFile> {
/// Returns true if this file belongs to an immutable library.
pub fn is_library_file(&self, file_id: FileId) -> Cancellable<bool> {
use ide_db::base_db::SourceDatabaseExt;
self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library)
}
+2 -1
View File
@@ -4,8 +4,9 @@
use std::{fmt, str::FromStr};
use cfg::CfgOptions;
use serde::Serialize;
#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(Clone, Eq, PartialEq, Debug, Serialize)]
pub enum CfgFlag {
Atom(String),
KeyValue { key: String, value: String },
+15 -7
View File
@@ -52,7 +52,7 @@
use base_db::{CrateDisplayName, CrateName};
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
use rustc_hash::FxHashMap;
use serde::{de, Deserialize};
use serde::{de, Deserialize, Serialize};
use span::Edition;
use crate::cfg_flag::CfgFlag;
@@ -161,14 +161,14 @@ pub fn path(&self) -> &AbsPath {
}
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ProjectJsonData {
sysroot: Option<Utf8PathBuf>,
sysroot_src: Option<Utf8PathBuf>,
crates: Vec<CrateData>,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
struct CrateData {
display_name: Option<String>,
root_module: Utf8PathBuf,
@@ -190,7 +190,7 @@ struct CrateData {
repository: Option<String>,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename = "edition")]
enum EditionData {
#[serde(rename = "2015")]
@@ -218,20 +218,21 @@ fn from(data: EditionData) -> Self {
///
/// This will differ from `CrateId` when multiple `ProjectJson`
/// workspaces are loaded.
#[derive(Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[serde(transparent)]
pub struct CrateArrayIdx(pub usize);
#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub(crate) struct Dep {
/// Identifies a crate by position in the crates array.
#[serde(rename = "crate")]
pub(crate) krate: CrateArrayIdx,
#[serde(serialize_with = "serialize_crate_name")]
#[serde(deserialize_with = "deserialize_crate_name")]
pub(crate) name: CrateName,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
struct CrateSource {
include_dirs: Vec<Utf8PathBuf>,
exclude_dirs: Vec<Utf8PathBuf>,
@@ -244,3 +245,10 @@ fn deserialize_crate_name<'de, D>(de: D) -> std::result::Result<CrateName, D::Er
let name = String::deserialize(de)?;
CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {err:?}")))
}
fn serialize_crate_name<S>(name: &CrateName, se: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
se.serialize_str(name)
}
+2
View File
@@ -39,11 +39,13 @@ tracing.workspace = true
tracing-subscriber.workspace = true
tracing-tree.workspace = true
triomphe.workspace = true
toml = "0.8.8"
nohash-hasher.workspace = true
always-assert = "0.2.0"
walkdir = "2.3.2"
semver.workspace = true
memchr = "2.7.1"
indexmap = { version = "2.0.0", features = ["serde"] }
cfg.workspace = true
flycheck.workspace = true
+1217 -866
View File
@@ -6,7 +6,7 @@
//! Of particular interest is the `feature_flags` hash map: while other fields
//! configure the server itself, feature flags are passed into analysis, and
//! tweak things like automatic insertion of `()` in completions.
#![allow(dead_code)]
use std::{fmt, iter, ops::Not};
use cfg::{CfgAtom, CfgDiff};
@@ -15,12 +15,13 @@
AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve,
InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
Snippet, SnippetScope,
Snippet, SnippetScope, SourceRootId,
};
use ide_db::{
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
SnippetCap,
};
use indexmap::IndexMap;
use itertools::Itertools;
use lsp_types::{ClientCapabilities, MarkupKind};
use paths::{Utf8Path, Utf8PathBuf};
@@ -29,7 +30,7 @@
};
use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
use serde::{de::DeserializeOwned, Deserialize};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use stdx::format_to_acc;
use vfs::{AbsPath, AbsPathBuf};
@@ -59,38 +60,45 @@
// To deprecate an option by replacing it with another name use `new_name | `old_name` so that we keep
// parsing the old name.
config_data! {
struct ConfigData {
/// Configs that apply on a workspace-wide scope. There are 3 levels on which a global configuration can be configured
///
/// 1. `rust-analyzer.toml` file under user's config directory (e.g ~/.config/rust-analyzer.toml)
/// 2. Client's own configurations (e.g `settings.json` on VS Code)
/// 3. `rust-analyzer.toml` file located at the workspace root
///
/// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle.
global: struct GlobalConfigData <- GlobalConfigInput -> {
/// Whether to insert #[must_use] when generating `as_` methods
/// for enum variants.
assist_emitMustUse: bool = "false",
assist_emitMustUse: bool = false,
/// Placeholder expression to use for missing expressions in assists.
assist_expressionFillDefault: ExprFillDefaultDef = "\"todo\"",
assist_expressionFillDefault: ExprFillDefaultDef = ExprFillDefaultDef::Todo,
/// Warm up caches on project load.
cachePriming_enable: bool = "true",
cachePriming_enable: bool = true,
/// How many worker threads to handle priming caches. The default `0` means to pick automatically.
cachePriming_numThreads: ParallelCachePrimingNumThreads = "0",
cachePriming_numThreads: ParallelCachePrimingNumThreads = 0u8,
/// Pass `--all-targets` to cargo invocation.
cargo_allTargets: bool = "true",
cargo_allTargets: bool = true,
/// Automatically refresh project info via `cargo metadata` on
/// `Cargo.toml` or `.cargo/config.toml` changes.
cargo_autoreload: bool = "true",
cargo_autoreload: bool = true,
/// Run build scripts (`build.rs`) for more precise code analysis.
cargo_buildScripts_enable: bool = "true",
cargo_buildScripts_enable: bool = true,
/// Specifies the working directory for running build scripts.
/// - "workspace": run build scripts for a workspace in the workspace's root directory.
/// This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.
/// - "root": run build scripts in the project's root directory.
/// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
/// is set.
cargo_buildScripts_invocationLocation: InvocationLocation = "\"workspace\"",
cargo_buildScripts_invocationLocation: InvocationLocation = InvocationLocation::Workspace,
/// Specifies the invocation strategy to use when running the build scripts command.
/// If `per_workspace` is set, the command will be executed for each workspace.
/// If `once` is set, the command will be executed once.
/// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
/// is set.
cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
cargo_buildScripts_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace,
/// Override the command rust-analyzer uses to run build scripts and
/// build procedural macros. The command is required to output json
/// and should therefore include `--message-format=json` or a similar
@@ -109,81 +117,81 @@ struct ConfigData {
/// cargo check --quiet --workspace --message-format=json --all-targets
/// ```
/// .
cargo_buildScripts_overrideCommand: Option<Vec<String>> = "null",
cargo_buildScripts_overrideCommand: Option<Vec<String>> = None,
/// Rerun proc-macros building/build-scripts running when proc-macro
/// or build-script sources change and are saved.
cargo_buildScripts_rebuildOnSave: bool = "true",
cargo_buildScripts_rebuildOnSave: bool = true,
/// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
/// avoid checking unnecessary things.
cargo_buildScripts_useRustcWrapper: bool = "true",
cargo_buildScripts_useRustcWrapper: bool = true,
/// List of cfg options to enable with the given values.
cargo_cfgs: FxHashMap<String, String> = "{}",
cargo_cfgs: FxHashMap<String, String> = FxHashMap::default(),
/// Extra arguments that are passed to every cargo invocation.
cargo_extraArgs: Vec<String> = "[]",
cargo_extraArgs: Vec<String> = vec![],
/// Extra environment variables that will be set when running cargo, rustc
/// or other commands within the workspace. Useful for setting RUSTFLAGS.
cargo_extraEnv: FxHashMap<String, String> = "{}",
cargo_extraEnv: FxHashMap<String, String> = FxHashMap::default(),
/// List of features to activate.
///
/// Set this to `"all"` to pass `--all-features` to cargo.
cargo_features: CargoFeaturesDef = "[]",
cargo_features: CargoFeaturesDef = CargoFeaturesDef::Selected(vec![]),
/// Whether to pass `--no-default-features` to cargo.
cargo_noDefaultFeatures: bool = "false",
cargo_noDefaultFeatures: bool = false,
/// Relative path to the sysroot, or "discover" to try to automatically find it via
/// "rustc --print sysroot".
///
/// Unsetting this disables sysroot loading.
///
/// This option does not take effect until rust-analyzer is restarted.
cargo_sysroot: Option<String> = "\"discover\"",
cargo_sysroot: Option<String> = Some("discover".to_owned()),
/// Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze
/// third-party dependencies of the standard libraries.
///
/// This will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer
/// will attempt to clean up afterwards, but nevertheless requires the location to be
/// writable to.
cargo_sysrootQueryMetadata: bool = "false",
cargo_sysrootQueryMetadata: bool = false,
/// Relative path to the sysroot library sources. If left unset, this will default to
/// `{cargo.sysroot}/lib/rustlib/src/rust/library`.
///
/// This option does not take effect until rust-analyzer is restarted.
cargo_sysrootSrc: Option<String> = "null",
cargo_sysrootSrc: Option<String> = None,
/// Compilation target override (target triple).
// FIXME(@poliorcetics): move to multiple targets here too, but this will need more work
// than `checkOnSave_target`
cargo_target: Option<String> = "null",
cargo_target: Option<String> = None,
/// Optional path to a rust-analyzer specific target directory.
/// This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro
/// building from locking the `Cargo.lock` at the expense of duplicating build artifacts.
///
/// Set to `true` to use a subdirectory of the existing target directory or
/// set to a path relative to the workspace to use that path.
cargo_targetDir | rust_analyzerTargetDir: Option<TargetDirectory> = "null",
cargo_targetDir | rust_analyzerTargetDir: Option<TargetDirectory> = None,
/// Unsets the implicit `#[cfg(test)]` for the specified crates.
cargo_unsetTest: Vec<String> = "[\"core\"]",
cargo_unsetTest: Vec<String> = vec!["core".to_owned()],
/// Run the check command for diagnostics on save.
checkOnSave | checkOnSave_enable: bool = "true",
checkOnSave | checkOnSave_enable: bool = true,
/// Check all targets and tests (`--all-targets`). Defaults to
/// `#rust-analyzer.cargo.allTargets#`.
check_allTargets | checkOnSave_allTargets: Option<bool> = "null",
check_allTargets | checkOnSave_allTargets: Option<bool> = None,
/// Cargo command to use for `cargo check`.
check_command | checkOnSave_command: String = "\"check\"",
check_command | checkOnSave_command: String = "check".to_owned(),
/// Extra arguments for `cargo check`.
check_extraArgs | checkOnSave_extraArgs: Vec<String> = "[]",
check_extraArgs | checkOnSave_extraArgs: Vec<String> = vec![],
/// Extra environment variables that will be set when running `cargo check`.
/// Extends `#rust-analyzer.cargo.extraEnv#`.
check_extraEnv | checkOnSave_extraEnv: FxHashMap<String, String> = "{}",
check_extraEnv | checkOnSave_extraEnv: FxHashMap<String, String> = FxHashMap::default(),
/// List of features to activate. Defaults to
/// `#rust-analyzer.cargo.features#`.
///
/// Set to `"all"` to pass `--all-features` to Cargo.
check_features | checkOnSave_features: Option<CargoFeaturesDef> = "null",
check_features | checkOnSave_features: Option<CargoFeaturesDef> = None,
/// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.
///
/// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...
check_ignore: FxHashSet<String> = "[]",
check_ignore: FxHashSet<String> = FxHashSet::default(),
/// Specifies the working directory for running checks.
/// - "workspace": run checks for workspaces in the corresponding workspaces' root directories.
// FIXME: Ideally we would support this in some way
@@ -191,16 +199,16 @@ struct ConfigData {
/// - "root": run checks in the project's root directory.
/// This config only has an effect when `#rust-analyzer.check.overrideCommand#`
/// is set.
check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"",
check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = InvocationLocation::Workspace,
/// Specifies the invocation strategy to use when running the check command.
/// If `per_workspace` is set, the command will be executed for each workspace.
/// If `once` is set, the command will be executed once.
/// This config only has an effect when `#rust-analyzer.check.overrideCommand#`
/// is set.
check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace,
/// Whether to pass `--no-default-features` to Cargo. Defaults to
/// `#rust-analyzer.cargo.noDefaultFeatures#`.
check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option<bool> = "null",
check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option<bool> = None,
/// Override the command rust-analyzer uses instead of `cargo check` for
/// diagnostics on save. The command is required to output json and
/// should therefore include `--message-format=json` or a similar option
@@ -228,37 +236,230 @@ struct ConfigData {
/// cargo check --workspace --message-format=json --all-targets
/// ```
/// .
check_overrideCommand | checkOnSave_overrideCommand: Option<Vec<String>> = "null",
check_overrideCommand | checkOnSave_overrideCommand: Option<Vec<String>> = None,
/// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.
///
/// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g.
/// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`.
///
/// Aliased as `"checkOnSave.targets"`.
check_targets | checkOnSave_targets | checkOnSave_target: Option<CheckOnSaveTargets> = "null",
check_targets | checkOnSave_targets | checkOnSave_target: Option<CheckOnSaveTargets> = None,
/// Whether `--workspace` should be passed to `cargo check`.
/// If false, `-p <package>` will be passed instead.
check_workspace: bool = "true",
check_workspace: bool = true,
/// List of rust-analyzer diagnostics to disable.
diagnostics_disabled: FxHashSet<String> = FxHashSet::default(),
/// Whether to show native rust-analyzer diagnostics.
diagnostics_enable: bool = true,
/// Whether to show experimental rust-analyzer diagnostics that might
/// have more false positives than usual.
diagnostics_experimental_enable: bool = false,
/// Map of prefixes to be substituted when parsing diagnostic file paths.
/// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
diagnostics_remapPrefix: FxHashMap<String, String> = FxHashMap::default(),
/// Whether to run additional style lints.
diagnostics_styleLints_enable: bool = false,
/// List of warnings that should be displayed with hint severity.
///
/// The warnings will be indicated by faded text or three dots in code
/// and will not show up in the `Problems Panel`.
diagnostics_warningsAsHint: Vec<String> = vec![],
/// List of warnings that should be displayed with info severity.
///
/// The warnings will be indicated by a blue squiggly underline in code
/// and a blue icon in the `Problems Panel`.
diagnostics_warningsAsInfo: Vec<String> = vec![],
/// 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<Utf8PathBuf> = vec![],
/// Controls file watching implementation.
files_watcher: FilesWatcherDef = FilesWatcherDef::Client,
/// Whether to show `Debug` action. Only applies when
/// `#rust-analyzer.hover.actions.enable#` is set.
hover_actions_debug_enable: bool = true,
/// Whether to show HoverActions in Rust files.
hover_actions_enable: bool = true,
/// Whether to show `Go to Type Definition` action. Only applies when
/// `#rust-analyzer.hover.actions.enable#` is set.
hover_actions_gotoTypeDef_enable: bool = true,
/// Whether to show `Implementations` action. Only applies when
/// `#rust-analyzer.hover.actions.enable#` is set.
hover_actions_implementations_enable: bool = true,
/// Whether to show `References` action. Only applies when
/// `#rust-analyzer.hover.actions.enable#` is set.
hover_actions_references_enable: bool = false,
/// Whether to show `Run` action. Only applies when
/// `#rust-analyzer.hover.actions.enable#` is set.
hover_actions_run_enable: bool = true,
/// Whether to show documentation on hover.
hover_documentation_enable: bool = true,
/// Whether to show keyword hover popups. Only applies when
/// `#rust-analyzer.hover.documentation.enable#` is set.
hover_documentation_keywords_enable: bool = true,
/// Use markdown syntax for links on hover.
hover_links_enable: bool = true,
/// How to render the align information in a memory layout hover.
hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal),
/// Whether to show memory layout data on hover.
hover_memoryLayout_enable: bool = true,
/// How to render the niche information in a memory layout hover.
hover_memoryLayout_niches: Option<bool> = Some(false),
/// How to render the offset information in a memory layout hover.
hover_memoryLayout_offset: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal),
/// How to render the size information in a memory layout hover.
hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Both),
/// How many fields of a struct to display when hovering a struct.
hover_show_structFields: Option<usize> = None,
/// How many associated items of a trait to display when hovering a trait.
hover_show_traitAssocItems: Option<usize> = None,
/// Enables the experimental support for interpreting tests.
interpret_tests: bool = false,
/// Whether to show `Debug` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_debug_enable: bool = true,
/// Whether to show CodeLens in Rust files.
lens_enable: bool = true,
/// Internal config: use custom client-side commands even when the
/// client doesn't set the corresponding capability.
lens_forceCustomCommands: bool = true,
/// Whether to show `Implementations` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_implementations_enable: bool = true,
/// Where to render annotations.
lens_location: AnnotationLocation = AnnotationLocation::AboveName,
/// Whether to show `References` lens for Struct, Enum, and Union.
/// Only applies when `#rust-analyzer.lens.enable#` is set.
lens_references_adt_enable: bool = false,
/// Whether to show `References` lens for Enum Variants.
/// Only applies when `#rust-analyzer.lens.enable#` is set.
lens_references_enumVariant_enable: bool = false,
/// Whether to show `Method References` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_references_method_enable: bool = false,
/// Whether to show `References` lens for Trait.
/// Only applies when `#rust-analyzer.lens.enable#` is set.
lens_references_trait_enable: bool = false,
/// Whether to show `Run` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_run_enable: bool = true,
/// Disable project auto-discovery in favor of explicitly specified set
/// of projects.
///
/// Elements must be paths pointing to `Cargo.toml`,
/// `rust-project.json`, or JSON objects in `rust-project.json` format.
linkedProjects: Vec<ManifestOrProjectJson> = vec![],
/// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
lru_capacity: Option<usize> = None,
/// Sets the LRU capacity of the specified queries.
lru_query_capacities: FxHashMap<Box<str>, usize> = FxHashMap::default(),
/// Whether to show `can't find Cargo.toml` error message.
notifications_cargoTomlNotFound: bool = true,
/// Whether to send an UnindexedProject notification to the client.
notifications_unindexedProject: bool = false,
/// How many worker threads in the main loop. The default `null` means to pick automatically.
numThreads: Option<usize> = None,
/// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
procMacro_attributes_enable: bool = true,
/// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
procMacro_enable: bool = true,
/// 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>]>> = FxHashMap::default(),
/// Internal config, path to proc-macro server executable.
procMacro_server: Option<Utf8PathBuf> = None,
/// Exclude imports from find-all-references.
references_excludeImports: bool = false,
/// Exclude tests from find-all-references.
references_excludeTests: bool = false,
/// Command to be executed instead of 'cargo' for runnables.
runnables_command: Option<String> = None,
/// Additional arguments to be passed to cargo for runnables such as
/// tests or binaries. For example, it may be `--release`.
runnables_extraArgs: Vec<String> = 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 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 does not take effect until rust-analyzer is restarted.
rustc_source: Option<String> = None,
/// Additional arguments to `rustfmt`.
rustfmt_extraArgs: Vec<String> = vec![],
/// Advanced option, fully override the command rust-analyzer uses for
/// formatting. This should be the equivalent of `rustfmt` here, and
/// not that of `cargo fmt`. The file contents will be passed on the
/// standard input and the formatted result will be read from the
/// standard output.
rustfmt_overrideCommand: Option<Vec<String>> = None,
/// Enables the use of rustfmt's unstable range formatting command for the
/// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
/// available on a nightly build.
rustfmt_rangeFormatting_enable: bool = false,
/// Show full signature of the callable. Only shows parameters if disabled.
signatureInfo_detail: SignatureDetail = SignatureDetail::Full,
/// Show documentation.
signatureInfo_documentation_enable: bool = true,
/// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.
typing_autoClosingAngleBrackets_enable: bool = false,
/// Workspace symbol search kind.
workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes,
/// Limits the number of items returned from a workspace symbol search (Defaults to 128).
/// Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.
/// Other clients requires all results upfront and might require a higher limit.
workspace_symbol_search_limit: usize = 128,
/// Workspace symbol search scope.
workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = WorkspaceSymbolSearchScopeDef::Workspace,
}
}
config_data! {
/// Local configurations can be overridden for every crate by placing a `rust-analyzer.toml` on crate root.
/// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle.
local: struct LocalConfigData <- LocalConfigInput -> {
/// 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",
completion_autoimport_enable: bool = true,
/// Toggles the additional completions that automatically show method calls and field accesses
/// with `self` prefixed to them when inside a method.
completion_autoself_enable: bool = "true",
completion_autoself_enable: bool = true,
/// Whether to add parenthesis and argument snippets when completing function.
completion_callable_snippets: CallableCompletionDef = "\"fill_arguments\"",
completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments,
/// Whether to show full function/method signatures in completion docs.
completion_fullFunctionSignatures_enable: bool = "false",
completion_fullFunctionSignatures_enable: bool = false,
/// Maximum number of completions to return. If `None`, the limit is infinite.
completion_limit: Option<usize> = "null",
completion_limit: Option<usize> = None,
/// Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
completion_postfix_enable: bool = "true",
completion_postfix_enable: bool = true,
/// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.
completion_privateEditable_enable: bool = "false",
completion_privateEditable_enable: bool = false,
/// Custom completion snippets.
// NOTE: Keep this list in sync with the feature docs of user snippets.
completion_snippets_custom: FxHashMap<String, SnippetDef> = r#"{
// NOTE: we use IndexMap for deterministic serialization ordering
completion_snippets_custom: IndexMap<String, SnippetDef> = serde_json::from_str(r#"{
"Arc::new": {
"postfix": "arc",
"body": "Arc::new(${receiver})",
@@ -298,323 +499,139 @@ struct ConfigData {
"description": "Wrap the expression in an `Option::Some`",
"scope": "expr"
}
}"#,
}"#).unwrap(),
/// Whether to enable term search based snippets like `Some(foo.bar().baz())`.
completion_termSearch_enable: bool = "false",
/// List of rust-analyzer diagnostics to disable.
diagnostics_disabled: FxHashSet<String> = "[]",
/// Whether to show native rust-analyzer diagnostics.
diagnostics_enable: bool = "true",
/// Whether to show experimental rust-analyzer diagnostics that might
/// have more false positives than usual.
diagnostics_experimental_enable: bool = "false",
/// Map of prefixes to be substituted when parsing diagnostic file paths.
/// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
diagnostics_remapPrefix: FxHashMap<String, String> = "{}",
/// Whether to run additional style lints.
diagnostics_styleLints_enable: bool = "false",
/// List of warnings that should be displayed with hint severity.
///
/// The warnings will be indicated by faded text or three dots in code
/// and will not show up in the `Problems Panel`.
diagnostics_warningsAsHint: Vec<String> = "[]",
/// List of warnings that should be displayed with info severity.
///
/// The warnings will be indicated by a blue squiggly underline in code
/// and a blue icon in the `Problems Panel`.
diagnostics_warningsAsInfo: Vec<String> = "[]",
/// 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<Utf8PathBuf> = "[]",
/// Controls file watching implementation.
files_watcher: FilesWatcherDef = "\"client\"",
completion_termSearch_enable: bool = false,
/// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
highlightRelated_breakPoints_enable: bool = "true",
highlightRelated_breakPoints_enable: bool = true,
/// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure.
highlightRelated_closureCaptures_enable: bool = "true",
highlightRelated_closureCaptures_enable: bool = true,
/// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
highlightRelated_exitPoints_enable: bool = "true",
highlightRelated_exitPoints_enable: bool = true,
/// Enables highlighting of related references while the cursor is on any identifier.
highlightRelated_references_enable: bool = "true",
highlightRelated_references_enable: bool = true,
/// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords.
highlightRelated_yieldPoints_enable: bool = "true",
/// Whether to show `Debug` action. Only applies when
/// `#rust-analyzer.hover.actions.enable#` is set.
hover_actions_debug_enable: bool = "true",
/// Whether to show HoverActions in Rust files.
hover_actions_enable: bool = "true",
/// Whether to show `Go to Type Definition` action. Only applies when
/// `#rust-analyzer.hover.actions.enable#` is set.
hover_actions_gotoTypeDef_enable: bool = "true",
/// Whether to show `Implementations` action. Only applies when
/// `#rust-analyzer.hover.actions.enable#` is set.
hover_actions_implementations_enable: bool = "true",
/// Whether to show `References` action. Only applies when
/// `#rust-analyzer.hover.actions.enable#` is set.
hover_actions_references_enable: bool = "false",
/// Whether to show `Run` action. Only applies when
/// `#rust-analyzer.hover.actions.enable#` is set.
hover_actions_run_enable: bool = "true",
/// Whether to show documentation on hover.
hover_documentation_enable: bool = "true",
/// Whether to show keyword hover popups. Only applies when
/// `#rust-analyzer.hover.documentation.enable#` is set.
hover_documentation_keywords_enable: bool = "true",
/// Use markdown syntax for links on hover.
hover_links_enable: bool = "true",
/// How to render the align information in a memory layout hover.
hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = "\"hexadecimal\"",
/// Whether to show memory layout data on hover.
hover_memoryLayout_enable: bool = "true",
/// How to render the niche information in a memory layout hover.
hover_memoryLayout_niches: Option<bool> = "false",
/// How to render the offset information in a memory layout hover.
hover_memoryLayout_offset: Option<MemoryLayoutHoverRenderKindDef> = "\"hexadecimal\"",
/// How to render the size information in a memory layout hover.
hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = "\"both\"",
/// How many fields of a struct to display when hovering a struct.
hover_show_structFields: Option<usize> = "null",
/// How many associated items of a trait to display when hovering a trait.
hover_show_traitAssocItems: Option<usize> = "null",
highlightRelated_yieldPoints_enable: bool = true,
/// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.
imports_granularity_enforce: bool = "false",
imports_granularity_enforce: bool = false,
/// How imports should be grouped into use statements.
imports_granularity_group: ImportGranularityDef = "\"crate\"",
imports_granularity_group: ImportGranularityDef = ImportGranularityDef::Crate,
/// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.
imports_group_enable: bool = "true",
imports_group_enable: bool = true,
/// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
imports_merge_glob: bool = "true",
imports_merge_glob: bool = true,
/// Prefer to unconditionally use imports of the core and alloc crate, over the std crate.
imports_preferNoStd | imports_prefer_no_std: bool = "false",
/// Whether to prefer import paths containing a `prelude` module.
imports_preferPrelude: bool = "false",
imports_preferNoStd | imports_prefer_no_std: bool = false,
/// Whether to prefer import paths containing a `prelude` module.
imports_preferPrelude: bool = false,
/// The path structure for newly inserted paths to use.
imports_prefix: ImportPrefixDef = "\"plain\"",
imports_prefix: ImportPrefixDef = ImportPrefixDef::Plain,
/// Whether to show inlay type hints for binding modes.
inlayHints_bindingModeHints_enable: bool = "false",
inlayHints_bindingModeHints_enable: bool = false,
/// Whether to show inlay type hints for method chains.
inlayHints_chainingHints_enable: bool = "true",
inlayHints_chainingHints_enable: bool = true,
/// Whether to show inlay hints after a closing `}` to indicate what item it belongs to.
inlayHints_closingBraceHints_enable: bool = "true",
inlayHints_closingBraceHints_enable: bool = true,
/// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
/// to always show them).
inlayHints_closingBraceHints_minLines: usize = "25",
inlayHints_closingBraceHints_minLines: usize = 25,
/// Whether to show inlay hints for closure captures.
inlayHints_closureCaptureHints_enable: bool = "false",
inlayHints_closureCaptureHints_enable: bool = false,
/// Whether to show inlay type hints for return types of closures.
inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = "\"never\"",
inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = ClosureReturnTypeHintsDef::Never,
/// Closure notation in type and chaining inlay hints.
inlayHints_closureStyle: ClosureStyle = "\"impl_fn\"",
inlayHints_closureStyle: ClosureStyle = ClosureStyle::ImplFn,
/// Whether to show enum variant discriminant hints.
inlayHints_discriminantHints_enable: DiscriminantHintsDef = "\"never\"",
inlayHints_discriminantHints_enable: DiscriminantHintsDef = DiscriminantHintsDef::Never,
/// Whether to show inlay hints for type adjustments.
inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = "\"never\"",
inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = AdjustmentHintsDef::Never,
/// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.
inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = "false",
inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false,
/// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = "\"prefix\"",
inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = AdjustmentHintsModeDef::Prefix,
/// Whether to show implicit drop hints.
inlayHints_implicitDrops_enable: bool = "false",
inlayHints_implicitDrops_enable: bool = false,
/// Whether to show inlay type hints for elided lifetimes in function signatures.
inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never,
/// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
inlayHints_lifetimeElisionHints_useParameterNames: bool = "false",
inlayHints_lifetimeElisionHints_useParameterNames: bool = false,
/// Maximum length for inlay hints. Set to null to have an unlimited length.
inlayHints_maxLength: Option<usize> = "25",
inlayHints_maxLength: Option<usize> = Some(25),
/// Whether to show function parameter name inlay hints at the call
/// site.
inlayHints_parameterHints_enable: bool = "true",
inlayHints_parameterHints_enable: bool = true,
/// Whether to show exclusive range inlay hints.
inlayHints_rangeExclusiveHints_enable: bool = "false",
inlayHints_rangeExclusiveHints_enable: bool = false,
/// Whether to show inlay hints for compiler inserted reborrows.
/// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.
inlayHints_reborrowHints_enable: ReborrowHintsDef = "\"never\"",
inlayHints_reborrowHints_enable: ReborrowHintsDef = ReborrowHintsDef::Never,
/// Whether to render leading colons for type hints, and trailing colons for parameter hints.
inlayHints_renderColons: bool = "true",
inlayHints_renderColons: bool = true,
/// Whether to show inlay type hints for variables.
inlayHints_typeHints_enable: bool = "true",
inlayHints_typeHints_enable: bool = true,
/// Whether to hide inlay type hints for `let` statements that initialize to a closure.
/// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.
inlayHints_typeHints_hideClosureInitialization: bool = "false",
inlayHints_typeHints_hideClosureInitialization: bool = false,
/// Whether to hide inlay type hints for constructors.
inlayHints_typeHints_hideNamedConstructor: bool = "false",
/// Enables the experimental support for interpreting tests.
interpret_tests: bool = "false",
inlayHints_typeHints_hideNamedConstructor: bool = false,
/// Join lines merges consecutive declaration and initialization of an assignment.
joinLines_joinAssignments: bool = "true",
joinLines_joinAssignments: bool = true,
/// Join lines inserts else between consecutive ifs.
joinLines_joinElseIf: bool = "true",
joinLines_joinElseIf: bool = true,
/// Join lines removes trailing commas.
joinLines_removeTrailingComma: bool = "true",
joinLines_removeTrailingComma: bool = true,
/// Join lines unwraps trivial blocks.
joinLines_unwrapTrivialBlock: bool = "true",
/// Whether to show `Debug` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_debug_enable: bool = "true",
/// Whether to show CodeLens in Rust files.
lens_enable: bool = "true",
/// Internal config: use custom client-side commands even when the
/// client doesn't set the corresponding capability.
lens_forceCustomCommands: bool = "true",
/// Whether to show `Implementations` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_implementations_enable: bool = "true",
/// Where to render annotations.
lens_location: AnnotationLocation = "\"above_name\"",
/// Whether to show `References` lens for Struct, Enum, and Union.
/// Only applies when `#rust-analyzer.lens.enable#` is set.
lens_references_adt_enable: bool = "false",
/// Whether to show `References` lens for Enum Variants.
/// Only applies when `#rust-analyzer.lens.enable#` is set.
lens_references_enumVariant_enable: bool = "false",
/// Whether to show `Method References` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_references_method_enable: bool = "false",
/// Whether to show `References` lens for Trait.
/// Only applies when `#rust-analyzer.lens.enable#` is set.
lens_references_trait_enable: bool = "false",
/// Whether to show `Run` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_run_enable: bool = "true",
/// Disable project auto-discovery in favor of explicitly specified set
/// of projects.
///
/// Elements must be paths pointing to `Cargo.toml`,
/// `rust-project.json`, or JSON objects in `rust-project.json` format.
linkedProjects: Vec<ManifestOrProjectJson> = "[]",
/// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
lru_capacity: Option<usize> = "null",
/// Sets the LRU capacity of the specified queries.
lru_query_capacities: FxHashMap<Box<str>, usize> = "{}",
/// Whether to show `can't find Cargo.toml` error message.
notifications_cargoTomlNotFound: bool = "true",
/// Whether to send an UnindexedProject notification to the client.
notifications_unindexedProject: bool = "false",
/// How many worker threads in the main loop. The default `null` means to pick automatically.
numThreads: Option<usize> = "null",
/// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
procMacro_attributes_enable: bool = "true",
/// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
procMacro_enable: bool = "true",
/// 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>]>> = "{}",
/// Internal config, path to proc-macro server executable.
procMacro_server: Option<Utf8PathBuf> = "null",
/// Exclude imports from find-all-references.
references_excludeImports: bool = "false",
/// Exclude tests from find-all-references.
references_excludeTests: bool = "false",
/// Command to be executed instead of 'cargo' for runnables.
runnables_command: Option<String> = "null",
/// Additional arguments to be passed to cargo for runnables such as
/// tests or binaries. For example, it may be `--release`.
runnables_extraArgs: 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 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 does not take effect until rust-analyzer is restarted.
rustc_source: Option<String> = "null",
/// Additional arguments to `rustfmt`.
rustfmt_extraArgs: Vec<String> = "[]",
/// Advanced option, fully override the command rust-analyzer uses for
/// formatting. This should be the equivalent of `rustfmt` here, and
/// not that of `cargo fmt`. The file contents will be passed on the
/// standard input and the formatted result will be read from the
/// standard output.
rustfmt_overrideCommand: Option<Vec<String>> = "null",
/// Enables the use of rustfmt's unstable range formatting command for the
/// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
/// available on a nightly build.
rustfmt_rangeFormatting_enable: bool = "false",
joinLines_unwrapTrivialBlock: bool = true,
/// Inject additional highlighting into doc comments.
///
/// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
/// doc links.
semanticHighlighting_doc_comment_inject_enable: bool = "true",
semanticHighlighting_doc_comment_inject_enable: bool = true,
/// Whether the server is allowed to emit non-standard tokens and modifiers.
semanticHighlighting_nonStandardTokens: bool = "true",
semanticHighlighting_nonStandardTokens: bool = true,
/// Use semantic tokens for operators.
///
/// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
/// they are tagged with modifiers.
semanticHighlighting_operator_enable: bool = "true",
semanticHighlighting_operator_enable: bool = true,
/// Use specialized semantic tokens for operators.
///
/// When enabled, rust-analyzer will emit special token types for operator tokens instead
/// of the generic `operator` token type.
semanticHighlighting_operator_specialization_enable: bool = "false",
semanticHighlighting_operator_specialization_enable: bool = false,
/// Use semantic tokens for punctuation.
///
/// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
/// they are tagged with modifiers or have a special role.
semanticHighlighting_punctuation_enable: bool = "false",
semanticHighlighting_punctuation_enable: bool = false,
/// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
/// calls.
semanticHighlighting_punctuation_separate_macro_bang: bool = "false",
semanticHighlighting_punctuation_separate_macro_bang: bool = false,
/// Use specialized semantic tokens for punctuation.
///
/// When enabled, rust-analyzer will emit special token types for punctuation tokens instead
/// of the generic `punctuation` token type.
semanticHighlighting_punctuation_specialization_enable: bool = "false",
semanticHighlighting_punctuation_specialization_enable: bool = false,
/// Use semantic tokens for strings.
///
/// In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
/// By disabling semantic tokens for strings, other grammars can be used to highlight
/// their contents.
semanticHighlighting_strings_enable: bool = "true",
/// Show full signature of the callable. Only shows parameters if disabled.
signatureInfo_detail: SignatureDetail = "\"full\"",
/// Show documentation.
signatureInfo_documentation_enable: bool = "true",
/// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.
typing_autoClosingAngleBrackets_enable: bool = "false",
/// Workspace symbol search kind.
workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = "\"only_types\"",
/// Limits the number of items returned from a workspace symbol search (Defaults to 128).
/// Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.
/// Other clients requires all results upfront and might require a higher limit.
workspace_symbol_search_limit: usize = "128",
/// Workspace symbol search scope.
workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = "\"workspace\"",
semanticHighlighting_strings_enable: bool = true,
}
}
impl Default for ConfigData {
fn default() -> Self {
ConfigData::from_json(serde_json::Value::Null, &mut Vec::new())
}
config_data! {
/// Configs that only make sense when they are set by a client. As such they can only be defined
/// by setting them using client's settings (e.g `settings.json` on VS Code).
client: struct ClientConfigData <- ClientConfigInput -> {}
}
#[derive(Debug, Clone)]
@@ -624,10 +641,37 @@ pub struct Config {
workspace_roots: Vec<AbsPathBuf>,
caps: lsp_types::ClientCapabilities,
root_path: AbsPathBuf,
data: ConfigData,
detached_files: Vec<AbsPathBuf>,
snippets: Vec<Snippet>,
visual_studio_code_version: Option<Version>,
default_config: ConfigData,
client_config: ConfigInput,
user_config: ConfigInput,
ratoml_files: FxHashMap<SourceRootId, RatomlNode>,
}
#[derive(Clone, Debug)]
struct RatomlNode {
node: ConfigInput,
parent: Option<SourceRootId>,
}
macro_rules! try_ {
($expr:expr) => {
|| -> _ { Some($expr) }()
};
}
macro_rules! try_or {
($expr:expr, $or:expr) => {
try_!($expr).unwrap_or($or)
};
}
macro_rules! try_or_def {
($expr:expr) => {
try_!($expr).unwrap_or_default()
};
}
type ParallelCachePrimingNumThreads = u8;
@@ -675,7 +719,7 @@ pub struct LensConfig {
pub location: AnnotationLocation,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AnnotationLocation {
AboveName,
@@ -830,13 +874,16 @@ pub fn new(
) -> Self {
Config {
caps,
data: ConfigData::default(),
detached_files: Vec::new(),
discovered_projects: Vec::new(),
root_path,
snippets: Default::default(),
workspace_roots,
visual_studio_code_version,
client_config: ConfigInput::default(),
user_config: ConfigInput::default(),
ratoml_files: FxHashMap::default(),
default_config: ConfigData::default(),
}
}
@@ -866,15 +913,19 @@ pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigError>
}
let mut errors = Vec::new();
self.detached_files =
get_field::<Vec<Utf8PathBuf>>(&mut json, &mut errors, "detachedFiles", None, "[]")
get_field::<Vec<Utf8PathBuf>>(&mut json, &mut errors, "detachedFiles", None)
.unwrap_or_default()
.into_iter()
.map(AbsPathBuf::assert)
.collect();
patch_old_style::patch_json_for_outdated_configs(&mut json);
self.data = ConfigData::from_json(json, &mut errors);
tracing::debug!("deserialized config data: {:#?}", self.data);
self.client_config = ConfigInput::from_json(json, &mut errors);
tracing::debug!(?self.client_config, "deserialized config data");
self.snippets.clear();
for (name, def) in self.data.completion_snippets_custom.iter() {
let snips = self.completion_snippets_custom(None).to_owned();
for (name, def) in snips.iter() {
if def.prefix.is_empty() && def.postfix.is_empty() {
continue;
}
@@ -912,7 +963,7 @@ pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigError>
fn validate(&self, error_sink: &mut Vec<(String, serde_json::Error)>) {
use serde::de::Error;
if self.data.check_command.is_empty() {
if self.check_command().is_empty() {
error_sink.push((
"/check/command".to_owned(),
serde_json::Error::custom("expected a non-empty string"),
@@ -921,7 +972,7 @@ fn validate(&self, error_sink: &mut Vec<(String, serde_json::Error)>) {
}
pub fn json_schema() -> serde_json::Value {
ConfigData::json_schema()
ConfigInput::json_schema()
}
pub fn root_path(&self) -> &AbsPathBuf {
@@ -937,44 +988,302 @@ pub fn detached_files(&self) -> &[AbsPathBuf] {
}
}
macro_rules! try_ {
($expr:expr) => {
|| -> _ { Some($expr) }()
};
}
macro_rules! try_or {
($expr:expr, $or:expr) => {
try_!($expr).unwrap_or($or)
};
}
macro_rules! try_or_def {
($expr:expr) => {
try_!($expr).unwrap_or_default()
};
}
impl Config {
pub fn assist(&self, source_root: Option<SourceRootId>) -> AssistConfig {
AssistConfig {
snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
allowed: None,
insert_use: self.insert_use_config(source_root),
prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
assist_emit_must_use: self.assist_emitMustUse().to_owned(),
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
}
}
pub fn completion(&self, source_root: Option<SourceRootId>) -> CompletionConfig {
CompletionConfig {
enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(),
enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned()
&& completion_item_edit_resolve(&self.caps),
enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(),
enable_private_editable: self.completion_privateEditable_enable(source_root).to_owned(),
full_function_signatures: self
.completion_fullFunctionSignatures_enable(source_root)
.to_owned(),
callable: match self.completion_callable_snippets(source_root) {
CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments),
CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses),
CallableCompletionDef::None => None,
},
insert_use: self.insert_use_config(source_root),
prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
snippet_cap: SnippetCap::new(try_or_def!(
self.caps
.text_document
.as_ref()?
.completion
.as_ref()?
.completion_item
.as_ref()?
.snippet_support?
)),
snippets: self.snippets.clone().to_vec(),
limit: self.completion_limit(source_root).to_owned(),
enable_term_search: self.completion_termSearch_enable(source_root).to_owned(),
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
}
}
pub fn diagnostics(&self, source_root: Option<SourceRootId>) -> DiagnosticsConfig {
DiagnosticsConfig {
enabled: *self.diagnostics_enable(),
proc_attr_macros_enabled: self.expand_proc_attr_macros(),
proc_macros_enabled: *self.procMacro_enable(),
disable_experimental: !self.diagnostics_experimental_enable(),
disabled: self.diagnostics_disabled().clone(),
expr_fill_default: match self.assist_expressionFillDefault() {
ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
},
insert_use: self.insert_use_config(source_root),
prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
style_lints: self.diagnostics_styleLints_enable().to_owned(),
}
}
pub fn expand_proc_attr_macros(&self) -> bool {
self.procMacro_enable().to_owned() && self.procMacro_attributes_enable().to_owned()
}
pub fn highlight_related(&self, source_root: Option<SourceRootId>) -> HighlightRelatedConfig {
HighlightRelatedConfig {
references: self.highlightRelated_references_enable(source_root).to_owned(),
break_points: self.highlightRelated_breakPoints_enable(source_root).to_owned(),
exit_points: self.highlightRelated_exitPoints_enable(source_root).to_owned(),
yield_points: self.highlightRelated_yieldPoints_enable(source_root).to_owned(),
closure_captures: self.highlightRelated_closureCaptures_enable(source_root).to_owned(),
}
}
pub fn hover_actions(&self) -> HoverActionsConfig {
let enable = self.experimental("hoverActions") && self.hover_actions_enable().to_owned();
HoverActionsConfig {
implementations: enable && self.hover_actions_implementations_enable().to_owned(),
references: enable && self.hover_actions_references_enable().to_owned(),
run: enable && self.hover_actions_run_enable().to_owned(),
debug: enable && self.hover_actions_debug_enable().to_owned(),
goto_type_def: enable && self.hover_actions_gotoTypeDef_enable().to_owned(),
}
}
pub fn hover(&self) -> HoverConfig {
let mem_kind = |kind| match kind {
MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both,
MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal,
MemoryLayoutHoverRenderKindDef::Hexadecimal => MemoryLayoutHoverRenderKind::Hexadecimal,
};
HoverConfig {
links_in_hover: self.hover_links_enable().to_owned(),
memory_layout: self.hover_memoryLayout_enable().then_some(MemoryLayoutHoverConfig {
size: self.hover_memoryLayout_size().map(mem_kind),
offset: self.hover_memoryLayout_offset().map(mem_kind),
alignment: self.hover_memoryLayout_alignment().map(mem_kind),
niches: self.hover_memoryLayout_niches().unwrap_or_default(),
}),
documentation: self.hover_documentation_enable().to_owned(),
format: {
let is_markdown = try_or_def!(self
.caps
.text_document
.as_ref()?
.hover
.as_ref()?
.content_format
.as_ref()?
.as_slice())
.contains(&MarkupKind::Markdown);
if is_markdown {
HoverDocFormat::Markdown
} else {
HoverDocFormat::PlainText
}
},
keywords: self.hover_documentation_keywords_enable().to_owned(),
max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(),
max_struct_field_count: self.hover_show_structFields().to_owned(),
}
}
pub fn inlay_hints(&self, source_root: Option<SourceRootId>) -> InlayHintsConfig {
let client_capability_fields = self
.caps
.text_document
.as_ref()
.and_then(|text| text.inlay_hint.as_ref())
.and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref())
.map(|inlay_resolve| inlay_resolve.properties.iter())
.into_iter()
.flatten()
.cloned()
.collect::<FxHashSet<_>>();
InlayHintsConfig {
render_colons: self.inlayHints_renderColons(source_root).to_owned(),
type_hints: self.inlayHints_typeHints_enable(source_root).to_owned(),
parameter_hints: self.inlayHints_parameterHints_enable(source_root).to_owned(),
chaining_hints: self.inlayHints_chainingHints_enable(source_root).to_owned(),
discriminant_hints: match self.inlayHints_discriminantHints_enable(source_root) {
DiscriminantHintsDef::Always => ide::DiscriminantHints::Always,
DiscriminantHintsDef::Never => ide::DiscriminantHints::Never,
DiscriminantHintsDef::Fieldless => ide::DiscriminantHints::Fieldless,
},
closure_return_type_hints: match self
.inlayHints_closureReturnTypeHints_enable(source_root)
{
ClosureReturnTypeHintsDef::Always => ide::ClosureReturnTypeHints::Always,
ClosureReturnTypeHintsDef::Never => ide::ClosureReturnTypeHints::Never,
ClosureReturnTypeHintsDef::WithBlock => ide::ClosureReturnTypeHints::WithBlock,
},
lifetime_elision_hints: match self.inlayHints_lifetimeElisionHints_enable(source_root) {
LifetimeElisionDef::Always => ide::LifetimeElisionHints::Always,
LifetimeElisionDef::Never => ide::LifetimeElisionHints::Never,
LifetimeElisionDef::SkipTrivial => ide::LifetimeElisionHints::SkipTrivial,
},
hide_named_constructor_hints: self
.inlayHints_typeHints_hideNamedConstructor(source_root)
.to_owned(),
hide_closure_initialization_hints: self
.inlayHints_typeHints_hideClosureInitialization(source_root)
.to_owned(),
closure_style: match self.inlayHints_closureStyle(source_root) {
ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn,
ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation,
ClosureStyle::WithId => hir::ClosureStyle::ClosureWithId,
ClosureStyle::Hide => hir::ClosureStyle::Hide,
},
closure_capture_hints: self
.inlayHints_closureCaptureHints_enable(source_root)
.to_owned(),
adjustment_hints: match self.inlayHints_expressionAdjustmentHints_enable(source_root) {
AdjustmentHintsDef::Always => ide::AdjustmentHints::Always,
AdjustmentHintsDef::Never => {
match self.inlayHints_reborrowHints_enable(source_root) {
ReborrowHintsDef::Always | ReborrowHintsDef::Mutable => {
ide::AdjustmentHints::ReborrowOnly
}
ReborrowHintsDef::Never => ide::AdjustmentHints::Never,
}
}
AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly,
},
adjustment_hints_mode: match self.inlayHints_expressionAdjustmentHints_mode(source_root)
{
AdjustmentHintsModeDef::Prefix => ide::AdjustmentHintsMode::Prefix,
AdjustmentHintsModeDef::Postfix => ide::AdjustmentHintsMode::Postfix,
AdjustmentHintsModeDef::PreferPrefix => ide::AdjustmentHintsMode::PreferPrefix,
AdjustmentHintsModeDef::PreferPostfix => ide::AdjustmentHintsMode::PreferPostfix,
},
adjustment_hints_hide_outside_unsafe: self
.inlayHints_expressionAdjustmentHints_hideOutsideUnsafe(source_root)
.to_owned(),
binding_mode_hints: self.inlayHints_bindingModeHints_enable(source_root).to_owned(),
param_names_for_lifetime_elision_hints: self
.inlayHints_lifetimeElisionHints_useParameterNames(source_root)
.to_owned(),
max_length: self.inlayHints_maxLength(source_root).to_owned(),
closing_brace_hints_min_lines: if self
.inlayHints_closingBraceHints_enable(source_root)
.to_owned()
{
Some(self.inlayHints_closingBraceHints_minLines(source_root).to_owned())
} else {
None
},
fields_to_resolve: InlayFieldsToResolve {
resolve_text_edits: client_capability_fields.contains("textEdits"),
resolve_hint_tooltip: client_capability_fields.contains("tooltip"),
resolve_label_tooltip: client_capability_fields.contains("label.tooltip"),
resolve_label_location: client_capability_fields.contains("label.location"),
resolve_label_command: client_capability_fields.contains("label.command"),
},
implicit_drop_hints: self.inlayHints_implicitDrops_enable(source_root).to_owned(),
range_exclusive_hints: self
.inlayHints_rangeExclusiveHints_enable(source_root)
.to_owned(),
}
}
fn insert_use_config(&self, source_root: Option<SourceRootId>) -> InsertUseConfig {
InsertUseConfig {
granularity: match self.imports_granularity_group(source_root) {
ImportGranularityDef::Preserve => ImportGranularity::Preserve,
ImportGranularityDef::Item => ImportGranularity::Item,
ImportGranularityDef::Crate => ImportGranularity::Crate,
ImportGranularityDef::Module => ImportGranularity::Module,
ImportGranularityDef::One => ImportGranularity::One,
},
enforce_granularity: self.imports_granularity_enforce(source_root).to_owned(),
prefix_kind: match self.imports_prefix(source_root) {
ImportPrefixDef::Plain => PrefixKind::Plain,
ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
ImportPrefixDef::BySelf => PrefixKind::BySelf,
},
group: self.imports_group_enable(source_root).to_owned(),
skip_glob_imports: !self.imports_merge_glob(source_root),
}
}
pub fn join_lines(&self, source_root: Option<SourceRootId>) -> JoinLinesConfig {
JoinLinesConfig {
join_else_if: self.joinLines_joinElseIf(source_root).to_owned(),
remove_trailing_comma: self.joinLines_removeTrailingComma(source_root).to_owned(),
unwrap_trivial_blocks: self.joinLines_unwrapTrivialBlock(source_root).to_owned(),
join_assignments: self.joinLines_joinAssignments(source_root).to_owned(),
}
}
pub fn highlighting_non_standard_tokens(&self, source_root: Option<SourceRootId>) -> bool {
self.semanticHighlighting_nonStandardTokens(source_root).to_owned()
}
pub fn highlighting_config(&self, source_root: Option<SourceRootId>) -> HighlightConfig {
HighlightConfig {
strings: self.semanticHighlighting_strings_enable(source_root).to_owned(),
punctuation: self.semanticHighlighting_punctuation_enable(source_root).to_owned(),
specialize_punctuation: self
.semanticHighlighting_punctuation_specialization_enable(source_root)
.to_owned(),
macro_bang: self
.semanticHighlighting_punctuation_separate_macro_bang(source_root)
.to_owned(),
operator: self.semanticHighlighting_operator_enable(source_root).to_owned(),
specialize_operator: self
.semanticHighlighting_operator_specialization_enable(source_root)
.to_owned(),
inject_doc_comment: self
.semanticHighlighting_doc_comment_inject_enable(source_root)
.to_owned(),
syntactic_name_ref_highlighting: false,
}
}
pub fn has_linked_projects(&self) -> bool {
!self.data.linkedProjects.is_empty()
!self.linkedProjects().is_empty()
}
pub fn linked_manifests(&self) -> impl Iterator<Item = &Utf8Path> + '_ {
self.data.linkedProjects.iter().filter_map(|it| match it {
self.linkedProjects().iter().filter_map(|it| match it {
ManifestOrProjectJson::Manifest(p) => Some(&**p),
ManifestOrProjectJson::ProjectJson(_) => None,
})
}
pub fn has_linked_project_jsons(&self) -> bool {
self.data
.linkedProjects
.iter()
.any(|it| matches!(it, ManifestOrProjectJson::ProjectJson(_)))
self.linkedProjects().iter().any(|it| matches!(it, ManifestOrProjectJson::ProjectJson(_)))
}
pub fn linked_or_discovered_projects(&self) -> Vec<LinkedProject> {
match self.data.linkedProjects.as_slice() {
match self.linkedProjects().as_slice() {
[] => {
let exclude_dirs: Vec<_> =
self.data.files_excludeDirs.iter().map(|p| self.root_path.join(p)).collect();
self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect();
self.discovered_projects
.iter()
.filter(
@@ -1028,7 +1337,7 @@ pub fn did_change_watched_files_relative_pattern_support(&self) -> bool {
}
pub fn prefill_caches(&self) -> bool {
self.data.cachePriming_enable
self.cachePriming_enable().to_owned()
}
pub fn location_link(&self) -> bool {
@@ -1165,117 +1474,95 @@ pub fn test_explorer(&self) -> bool {
}
pub fn publish_diagnostics(&self) -> bool {
self.data.diagnostics_enable
}
pub fn diagnostics(&self) -> DiagnosticsConfig {
DiagnosticsConfig {
enabled: self.data.diagnostics_enable,
proc_attr_macros_enabled: self.expand_proc_attr_macros(),
proc_macros_enabled: self.data.procMacro_enable,
disable_experimental: !self.data.diagnostics_experimental_enable,
disabled: self.data.diagnostics_disabled.clone(),
expr_fill_default: match self.data.assist_expressionFillDefault {
ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
},
insert_use: self.insert_use_config(),
prefer_no_std: self.data.imports_preferNoStd,
prefer_prelude: self.data.imports_preferPrelude,
style_lints: self.data.diagnostics_styleLints_enable,
}
self.diagnostics_enable().to_owned()
}
pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
DiagnosticsMapConfig {
remap_prefix: self.data.diagnostics_remapPrefix.clone(),
warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
check_ignore: self.data.check_ignore.clone(),
remap_prefix: self.diagnostics_remapPrefix().clone(),
warnings_as_info: self.diagnostics_warningsAsInfo().clone(),
warnings_as_hint: self.diagnostics_warningsAsHint().clone(),
check_ignore: self.check_ignore().clone(),
}
}
pub fn extra_args(&self) -> &Vec<String> {
&self.data.cargo_extraArgs
self.cargo_extraArgs()
}
pub fn extra_env(&self) -> &FxHashMap<String, String> {
&self.data.cargo_extraEnv
self.cargo_extraEnv()
}
pub fn check_extra_args(&self) -> Vec<String> {
let mut extra_args = self.extra_args().clone();
extra_args.extend_from_slice(&self.data.check_extraArgs);
extra_args.extend_from_slice(self.check_extraArgs());
extra_args
}
pub fn check_extra_env(&self) -> FxHashMap<String, String> {
let mut extra_env = self.data.cargo_extraEnv.clone();
extra_env.extend(self.data.check_extraEnv.clone());
let mut extra_env = self.cargo_extraEnv().clone();
extra_env.extend(self.check_extraEnv().clone());
extra_env
}
pub fn lru_parse_query_capacity(&self) -> Option<usize> {
self.data.lru_capacity
self.lru_capacity().to_owned()
}
pub fn lru_query_capacities(&self) -> Option<&FxHashMap<Box<str>, usize>> {
self.data.lru_query_capacities.is_empty().not().then_some(&self.data.lru_query_capacities)
pub fn lru_query_capacities_config(&self) -> Option<&FxHashMap<Box<str>, usize>> {
self.lru_query_capacities().is_empty().not().then(|| self.lru_query_capacities())
}
pub fn proc_macro_srv(&self) -> Option<AbsPathBuf> {
let path = self.data.procMacro_server.clone()?;
let path = self.procMacro_server().clone()?;
Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path)))
}
pub fn ignored_proc_macros(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
&self.data.procMacro_ignored
self.procMacro_ignored()
}
pub fn expand_proc_macros(&self) -> bool {
self.data.procMacro_enable
}
pub fn expand_proc_attr_macros(&self) -> bool {
self.data.procMacro_enable && self.data.procMacro_attributes_enable
self.procMacro_enable().to_owned()
}
pub fn files(&self) -> FilesConfig {
FilesConfig {
watcher: match self.data.files_watcher {
watcher: match self.files_watcher() {
FilesWatcherDef::Client if self.did_change_watched_files_dynamic_registration() => {
FilesWatcher::Client
}
_ => FilesWatcher::Server,
},
exclude: self.data.files_excludeDirs.iter().map(|it| self.root_path.join(it)).collect(),
exclude: self.files_excludeDirs().iter().map(|it| self.root_path.join(it)).collect(),
}
}
pub fn notifications(&self) -> NotificationsConfig {
NotificationsConfig {
cargo_toml_not_found: self.data.notifications_cargoTomlNotFound,
unindexed_project: self.data.notifications_unindexedProject,
cargo_toml_not_found: self.notifications_cargoTomlNotFound().to_owned(),
unindexed_project: self.notifications_unindexedProject().to_owned(),
}
}
pub fn cargo_autoreload(&self) -> bool {
self.data.cargo_autoreload
pub fn cargo_autoreload_config(&self) -> bool {
self.cargo_autoreload().to_owned()
}
pub fn run_build_scripts(&self) -> bool {
self.data.cargo_buildScripts_enable || self.data.procMacro_enable
self.cargo_buildScripts_enable().to_owned() || self.procMacro_enable().to_owned()
}
pub fn cargo(&self) -> CargoConfig {
let rustc_source = self.data.rustc_source.as_ref().map(|rustc_src| {
let rustc_source = self.rustc_source().as_ref().map(|rustc_src| {
if rustc_src == "discover" {
RustLibSource::Discover
} else {
RustLibSource::Path(self.root_path.join(rustc_src))
}
});
let sysroot = self.data.cargo_sysroot.as_ref().map(|sysroot| {
let sysroot = self.cargo_sysroot().as_ref().map(|sysroot| {
if sysroot == "discover" {
RustLibSource::Discover
} else {
@@ -1283,27 +1570,26 @@ pub fn cargo(&self) -> CargoConfig {
}
});
let sysroot_src =
self.data.cargo_sysrootSrc.as_ref().map(|sysroot| self.root_path.join(sysroot));
let sysroot_query_metadata = self.data.cargo_sysrootQueryMetadata;
self.cargo_sysrootSrc().as_ref().map(|sysroot| self.root_path.join(sysroot));
let sysroot_query_metadata = self.cargo_sysrootQueryMetadata();
CargoConfig {
all_targets: self.data.cargo_allTargets,
features: match &self.data.cargo_features {
all_targets: *self.cargo_allTargets(),
features: match &self.cargo_features() {
CargoFeaturesDef::All => CargoFeatures::All,
CargoFeaturesDef::Selected(features) => CargoFeatures::Selected {
features: features.clone(),
no_default_features: self.data.cargo_noDefaultFeatures,
no_default_features: self.cargo_noDefaultFeatures().to_owned(),
},
},
target: self.data.cargo_target.clone(),
target: self.cargo_target().clone(),
sysroot,
sysroot_query_metadata,
sysroot_query_metadata: *sysroot_query_metadata,
sysroot_src,
rustc_source,
cfg_overrides: project_model::CfgOverrides {
global: CfgDiff::new(
self.data
.cargo_cfgs
self.cargo_cfgs()
.iter()
.map(|(key, val)| {
if val.is_empty() {
@@ -1317,8 +1603,7 @@ pub fn cargo(&self) -> CargoConfig {
)
.unwrap(),
selective: self
.data
.cargo_unsetTest
.cargo_unsetTest()
.iter()
.map(|it| {
(
@@ -1328,49 +1613,49 @@ pub fn cargo(&self) -> CargoConfig {
})
.collect(),
},
wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper,
invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy {
wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(),
invocation_strategy: match self.cargo_buildScripts_invocationStrategy() {
InvocationStrategy::Once => project_model::InvocationStrategy::Once,
InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace,
},
invocation_location: match self.data.cargo_buildScripts_invocationLocation {
invocation_location: match self.cargo_buildScripts_invocationLocation() {
InvocationLocation::Root => {
project_model::InvocationLocation::Root(self.root_path.clone())
}
InvocationLocation::Workspace => project_model::InvocationLocation::Workspace,
},
run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(),
extra_args: self.data.cargo_extraArgs.clone(),
extra_env: self.data.cargo_extraEnv.clone(),
run_build_script_command: self.cargo_buildScripts_overrideCommand().clone(),
extra_args: self.cargo_extraArgs().clone(),
extra_env: self.cargo_extraEnv().clone(),
target_dir: self.target_dir_from_config(),
}
}
pub fn rustfmt(&self) -> RustfmtConfig {
match &self.data.rustfmt_overrideCommand {
match &self.rustfmt_overrideCommand() {
Some(args) if !args.is_empty() => {
let mut args = args.clone();
let command = args.remove(0);
RustfmtConfig::CustomCommand { command, args }
}
Some(_) | None => RustfmtConfig::Rustfmt {
extra_args: self.data.rustfmt_extraArgs.clone(),
enable_range_formatting: self.data.rustfmt_rangeFormatting_enable,
extra_args: self.rustfmt_extraArgs().clone(),
enable_range_formatting: *self.rustfmt_rangeFormatting_enable(),
},
}
}
pub fn flycheck_workspace(&self) -> bool {
self.data.check_workspace
*self.check_workspace()
}
pub fn cargo_test_options(&self) -> CargoOptions {
CargoOptions {
target_triples: self.data.cargo_target.clone().into_iter().collect(),
target_triples: self.cargo_target().clone().into_iter().collect(),
all_targets: false,
no_default_features: self.data.cargo_noDefaultFeatures,
all_features: matches!(self.data.cargo_features, CargoFeaturesDef::All),
features: match self.data.cargo_features.clone() {
no_default_features: *self.cargo_noDefaultFeatures(),
all_features: matches!(self.cargo_features(), CargoFeaturesDef::All),
features: match self.cargo_features().clone() {
CargoFeaturesDef::All => vec![],
CargoFeaturesDef::Selected(it) => it,
},
@@ -1381,7 +1666,7 @@ pub fn cargo_test_options(&self) -> CargoOptions {
}
pub fn flycheck(&self) -> FlycheckConfig {
match &self.data.check_overrideCommand {
match &self.check_overrideCommand() {
Some(args) if !args.is_empty() => {
let mut args = args.clone();
let command = args.remove(0);
@@ -1389,13 +1674,13 @@ pub fn flycheck(&self) -> FlycheckConfig {
command,
args,
extra_env: self.check_extra_env(),
invocation_strategy: match self.data.check_invocationStrategy {
invocation_strategy: match self.check_invocationStrategy() {
InvocationStrategy::Once => flycheck::InvocationStrategy::Once,
InvocationStrategy::PerWorkspace => {
flycheck::InvocationStrategy::PerWorkspace
}
},
invocation_location: match self.data.check_invocationLocation {
invocation_location: match self.check_invocationLocation() {
InvocationLocation::Root => {
flycheck::InvocationLocation::Root(self.root_path.clone())
}
@@ -1404,31 +1689,28 @@ pub fn flycheck(&self) -> FlycheckConfig {
}
}
Some(_) | None => FlycheckConfig::CargoCommand {
command: self.data.check_command.clone(),
command: self.check_command().clone(),
options: CargoOptions {
target_triples: self
.data
.check_targets
.check_targets()
.clone()
.and_then(|targets| match &targets.0[..] {
[] => None,
targets => Some(targets.into()),
})
.unwrap_or_else(|| self.data.cargo_target.clone().into_iter().collect()),
all_targets: self.data.check_allTargets.unwrap_or(self.data.cargo_allTargets),
.unwrap_or_else(|| self.cargo_target().clone().into_iter().collect()),
all_targets: self.check_allTargets().unwrap_or(*self.cargo_allTargets()),
no_default_features: self
.data
.check_noDefaultFeatures
.unwrap_or(self.data.cargo_noDefaultFeatures),
.check_noDefaultFeatures()
.unwrap_or(*self.cargo_noDefaultFeatures()),
all_features: matches!(
self.data.check_features.as_ref().unwrap_or(&self.data.cargo_features),
self.check_features().as_ref().unwrap_or(self.cargo_features()),
CargoFeaturesDef::All
),
features: match self
.data
.check_features
.check_features()
.clone()
.unwrap_or_else(|| self.data.cargo_features.clone())
.unwrap_or_else(|| self.cargo_features().clone())
{
CargoFeaturesDef::All => vec![],
CargoFeaturesDef::Selected(it) => it,
@@ -1443,7 +1725,7 @@ pub fn flycheck(&self) -> FlycheckConfig {
}
fn target_dir_from_config(&self) -> Option<Utf8PathBuf> {
self.data.cargo_targetDir.as_ref().and_then(|target_dir| match target_dir {
self.cargo_targetDir().as_ref().and_then(|target_dir| match target_dir {
TargetDirectory::UseSubdirectory(true) => {
Some(Utf8PathBuf::from("target/rust-analyzer"))
}
@@ -1454,294 +1736,66 @@ fn target_dir_from_config(&self) -> Option<Utf8PathBuf> {
}
pub fn check_on_save(&self) -> bool {
self.data.checkOnSave
*self.checkOnSave()
}
pub fn script_rebuild_on_save(&self) -> bool {
self.data.cargo_buildScripts_rebuildOnSave
*self.cargo_buildScripts_rebuildOnSave()
}
pub fn runnables(&self) -> RunnablesConfig {
RunnablesConfig {
override_cargo: self.data.runnables_command.clone(),
cargo_extra_args: self.data.runnables_extraArgs.clone(),
}
}
pub fn inlay_hints(&self) -> InlayHintsConfig {
let client_capability_fields = self
.caps
.text_document
.as_ref()
.and_then(|text| text.inlay_hint.as_ref())
.and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref())
.map(|inlay_resolve| inlay_resolve.properties.iter())
.into_iter()
.flatten()
.cloned()
.collect::<FxHashSet<_>>();
InlayHintsConfig {
render_colons: self.data.inlayHints_renderColons,
type_hints: self.data.inlayHints_typeHints_enable,
parameter_hints: self.data.inlayHints_parameterHints_enable,
chaining_hints: self.data.inlayHints_chainingHints_enable,
implicit_drop_hints: self.data.inlayHints_implicitDrops_enable,
discriminant_hints: match self.data.inlayHints_discriminantHints_enable {
DiscriminantHintsDef::Always => ide::DiscriminantHints::Always,
DiscriminantHintsDef::Never => ide::DiscriminantHints::Never,
DiscriminantHintsDef::Fieldless => ide::DiscriminantHints::Fieldless,
},
closure_return_type_hints: match self.data.inlayHints_closureReturnTypeHints_enable {
ClosureReturnTypeHintsDef::Always => ide::ClosureReturnTypeHints::Always,
ClosureReturnTypeHintsDef::Never => ide::ClosureReturnTypeHints::Never,
ClosureReturnTypeHintsDef::WithBlock => ide::ClosureReturnTypeHints::WithBlock,
},
lifetime_elision_hints: match self.data.inlayHints_lifetimeElisionHints_enable {
LifetimeElisionDef::Always => ide::LifetimeElisionHints::Always,
LifetimeElisionDef::Never => ide::LifetimeElisionHints::Never,
LifetimeElisionDef::SkipTrivial => ide::LifetimeElisionHints::SkipTrivial,
},
hide_named_constructor_hints: self.data.inlayHints_typeHints_hideNamedConstructor,
hide_closure_initialization_hints: self
.data
.inlayHints_typeHints_hideClosureInitialization,
closure_style: match self.data.inlayHints_closureStyle {
ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn,
ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation,
ClosureStyle::WithId => hir::ClosureStyle::ClosureWithId,
ClosureStyle::Hide => hir::ClosureStyle::Hide,
},
closure_capture_hints: self.data.inlayHints_closureCaptureHints_enable,
adjustment_hints: match self.data.inlayHints_expressionAdjustmentHints_enable {
AdjustmentHintsDef::Always => ide::AdjustmentHints::Always,
AdjustmentHintsDef::Never => match self.data.inlayHints_reborrowHints_enable {
ReborrowHintsDef::Always | ReborrowHintsDef::Mutable => {
ide::AdjustmentHints::ReborrowOnly
}
ReborrowHintsDef::Never => ide::AdjustmentHints::Never,
},
AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly,
},
adjustment_hints_mode: match self.data.inlayHints_expressionAdjustmentHints_mode {
AdjustmentHintsModeDef::Prefix => ide::AdjustmentHintsMode::Prefix,
AdjustmentHintsModeDef::Postfix => ide::AdjustmentHintsMode::Postfix,
AdjustmentHintsModeDef::PreferPrefix => ide::AdjustmentHintsMode::PreferPrefix,
AdjustmentHintsModeDef::PreferPostfix => ide::AdjustmentHintsMode::PreferPostfix,
},
adjustment_hints_hide_outside_unsafe: self
.data
.inlayHints_expressionAdjustmentHints_hideOutsideUnsafe,
binding_mode_hints: self.data.inlayHints_bindingModeHints_enable,
param_names_for_lifetime_elision_hints: self
.data
.inlayHints_lifetimeElisionHints_useParameterNames,
max_length: self.data.inlayHints_maxLength,
closing_brace_hints_min_lines: if self.data.inlayHints_closingBraceHints_enable {
Some(self.data.inlayHints_closingBraceHints_minLines)
} else {
None
},
range_exclusive_hints: self.data.inlayHints_rangeExclusiveHints_enable,
fields_to_resolve: InlayFieldsToResolve {
resolve_text_edits: client_capability_fields.contains("textEdits"),
resolve_hint_tooltip: client_capability_fields.contains("tooltip"),
resolve_label_tooltip: client_capability_fields.contains("label.tooltip"),
resolve_label_location: client_capability_fields.contains("label.location"),
resolve_label_command: client_capability_fields.contains("label.command"),
},
}
}
fn insert_use_config(&self) -> InsertUseConfig {
InsertUseConfig {
granularity: match self.data.imports_granularity_group {
ImportGranularityDef::Preserve => ImportGranularity::Preserve,
ImportGranularityDef::Item => ImportGranularity::Item,
ImportGranularityDef::Crate => ImportGranularity::Crate,
ImportGranularityDef::Module => ImportGranularity::Module,
ImportGranularityDef::One => ImportGranularity::One,
},
enforce_granularity: self.data.imports_granularity_enforce,
prefix_kind: match self.data.imports_prefix {
ImportPrefixDef::Plain => PrefixKind::Plain,
ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
ImportPrefixDef::BySelf => PrefixKind::BySelf,
},
group: self.data.imports_group_enable,
skip_glob_imports: !self.data.imports_merge_glob,
}
}
pub fn completion(&self) -> CompletionConfig {
CompletionConfig {
enable_postfix_completions: self.data.completion_postfix_enable,
enable_imports_on_the_fly: self.data.completion_autoimport_enable
&& completion_item_edit_resolve(&self.caps),
enable_self_on_the_fly: self.data.completion_autoself_enable,
enable_private_editable: self.data.completion_privateEditable_enable,
enable_term_search: self.data.completion_termSearch_enable,
full_function_signatures: self.data.completion_fullFunctionSignatures_enable,
callable: match self.data.completion_callable_snippets {
CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments),
CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses),
CallableCompletionDef::None => None,
},
insert_use: self.insert_use_config(),
prefer_no_std: self.data.imports_preferNoStd,
prefer_prelude: self.data.imports_preferPrelude,
snippet_cap: SnippetCap::new(try_or_def!(
self.caps
.text_document
.as_ref()?
.completion
.as_ref()?
.completion_item
.as_ref()?
.snippet_support?
)),
snippets: self.snippets.clone(),
limit: self.data.completion_limit,
override_cargo: self.runnables_command().clone(),
cargo_extra_args: self.runnables_extraArgs().clone(),
}
}
pub fn find_all_refs_exclude_imports(&self) -> bool {
self.data.references_excludeImports
*self.references_excludeImports()
}
pub fn find_all_refs_exclude_tests(&self) -> bool {
self.data.references_excludeTests
*self.references_excludeTests()
}
pub fn snippet_cap(&self) -> bool {
self.experimental("snippetTextEdit")
}
pub fn assist(&self) -> AssistConfig {
AssistConfig {
snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
allowed: None,
insert_use: self.insert_use_config(),
prefer_no_std: self.data.imports_preferNoStd,
prefer_prelude: self.data.imports_preferPrelude,
assist_emit_must_use: self.data.assist_emitMustUse,
}
}
pub fn join_lines(&self) -> JoinLinesConfig {
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(&self) -> CallInfoConfig {
CallInfoConfig {
params_only: matches!(self.data.signatureInfo_detail, SignatureDetail::Parameters),
docs: self.data.signatureInfo_documentation_enable,
params_only: matches!(self.signatureInfo_detail(), SignatureDetail::Parameters),
docs: *self.signatureInfo_documentation_enable(),
}
}
pub fn lens(&self) -> LensConfig {
LensConfig {
run: self.data.lens_enable && self.data.lens_run_enable,
debug: self.data.lens_enable && self.data.lens_debug_enable,
interpret: self.data.lens_enable
&& self.data.lens_run_enable
&& self.data.interpret_tests,
implementations: self.data.lens_enable && self.data.lens_implementations_enable,
method_refs: self.data.lens_enable && self.data.lens_references_method_enable,
refs_adt: self.data.lens_enable && self.data.lens_references_adt_enable,
refs_trait: self.data.lens_enable && self.data.lens_references_trait_enable,
enum_variant_refs: self.data.lens_enable
&& self.data.lens_references_enumVariant_enable,
location: self.data.lens_location,
}
}
pub fn hover_actions(&self) -> HoverActionsConfig {
let enable = self.experimental("hoverActions") && self.data.hover_actions_enable;
HoverActionsConfig {
implementations: enable && self.data.hover_actions_implementations_enable,
references: enable && self.data.hover_actions_references_enable,
run: enable && self.data.hover_actions_run_enable,
debug: enable && self.data.hover_actions_debug_enable,
goto_type_def: enable && self.data.hover_actions_gotoTypeDef_enable,
}
}
pub fn highlighting_non_standard_tokens(&self) -> bool {
self.data.semanticHighlighting_nonStandardTokens
}
pub fn highlighting_config(&self) -> HighlightConfig {
HighlightConfig {
strings: self.data.semanticHighlighting_strings_enable,
punctuation: self.data.semanticHighlighting_punctuation_enable,
specialize_punctuation: self
.data
.semanticHighlighting_punctuation_specialization_enable,
macro_bang: self.data.semanticHighlighting_punctuation_separate_macro_bang,
operator: self.data.semanticHighlighting_operator_enable,
specialize_operator: self.data.semanticHighlighting_operator_specialization_enable,
inject_doc_comment: self.data.semanticHighlighting_doc_comment_inject_enable,
syntactic_name_ref_highlighting: false,
}
}
pub fn hover(&self) -> HoverConfig {
let mem_kind = |kind| match kind {
MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both,
MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal,
MemoryLayoutHoverRenderKindDef::Hexadecimal => MemoryLayoutHoverRenderKind::Hexadecimal,
};
HoverConfig {
links_in_hover: self.data.hover_links_enable,
memory_layout: self.data.hover_memoryLayout_enable.then_some(MemoryLayoutHoverConfig {
size: self.data.hover_memoryLayout_size.map(mem_kind),
offset: self.data.hover_memoryLayout_offset.map(mem_kind),
alignment: self.data.hover_memoryLayout_alignment.map(mem_kind),
niches: self.data.hover_memoryLayout_niches.unwrap_or_default(),
}),
documentation: self.data.hover_documentation_enable,
format: {
let is_markdown = try_or_def!(self
.caps
.text_document
.as_ref()?
.hover
.as_ref()?
.content_format
.as_ref()?
.as_slice())
.contains(&MarkupKind::Markdown);
if is_markdown {
HoverDocFormat::Markdown
} else {
HoverDocFormat::PlainText
}
},
keywords: self.data.hover_documentation_keywords_enable,
max_trait_assoc_items_count: self.data.hover_show_traitAssocItems,
max_struct_field_count: self.data.hover_show_structFields,
run: *self.lens_run_enable(),
debug: *self.lens_enable() && *self.lens_debug_enable(),
interpret: *self.lens_enable() && *self.lens_run_enable() && *self.interpret_tests(),
implementations: *self.lens_enable() && *self.lens_implementations_enable(),
method_refs: *self.lens_enable() && *self.lens_references_method_enable(),
refs_adt: *self.lens_enable() && *self.lens_references_adt_enable(),
refs_trait: *self.lens_enable() && *self.lens_references_trait_enable(),
enum_variant_refs: *self.lens_enable() && *self.lens_references_enumVariant_enable(),
location: *self.lens_location(),
}
}
pub fn workspace_symbol(&self) -> WorkspaceSymbolConfig {
WorkspaceSymbolConfig {
search_scope: match self.data.workspace_symbol_search_scope {
search_scope: match self.workspace_symbol_search_scope() {
WorkspaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace,
WorkspaceSymbolSearchScopeDef::WorkspaceAndDependencies => {
WorkspaceSymbolSearchScope::WorkspaceAndDependencies
}
},
search_kind: match self.data.workspace_symbol_search_kind {
search_kind: match self.workspace_symbol_search_kind() {
WorkspaceSymbolSearchKindDef::OnlyTypes => WorkspaceSymbolSearchKind::OnlyTypes,
WorkspaceSymbolSearchKindDef::AllSymbols => WorkspaceSymbolSearchKind::AllSymbols,
},
search_limit: self.data.workspace_symbol_search_limit,
search_limit: *self.workspace_symbol_search_limit(),
}
}
@@ -1775,7 +1829,7 @@ pub fn client_commands(&self) -> ClientCommandsConfig {
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 force = commands.is_none() && *self.lens_forceCustomCommands();
let commands = commands.map(|it| it.commands).unwrap_or_default();
let get = |name: &str| commands.iter().any(|it| it == name) || force;
@@ -1789,29 +1843,19 @@ pub fn client_commands(&self) -> ClientCommandsConfig {
}
}
pub fn highlight_related(&self) -> HighlightRelatedConfig {
HighlightRelatedConfig {
references: self.data.highlightRelated_references_enable,
break_points: self.data.highlightRelated_breakPoints_enable,
exit_points: self.data.highlightRelated_exitPoints_enable,
yield_points: self.data.highlightRelated_yieldPoints_enable,
closure_captures: self.data.highlightRelated_closureCaptures_enable,
}
}
pub fn prime_caches_num_threads(&self) -> u8 {
match self.data.cachePriming_numThreads {
match *self.cachePriming_numThreads() {
0 => num_cpus::get_physical().try_into().unwrap_or(u8::MAX),
n => n,
}
}
pub fn main_loop_num_threads(&self) -> usize {
self.data.numThreads.unwrap_or(num_cpus::get_physical())
self.numThreads().unwrap_or(num_cpus::get_physical())
}
pub fn typing_autoclose_angle(&self) -> bool {
self.data.typing_autoClosingAngleBrackets_enable
*self.typing_autoClosingAngleBrackets_enable()
}
// VSCode is our reference implementation, so we allow ourselves to work around issues by
@@ -1822,100 +1866,120 @@ pub fn visual_studio_code_version(&self) -> Option<&Version> {
}
// Deserialization definitions
macro_rules! create_bool_or_string_de {
macro_rules! create_bool_or_string_serde {
($ident:ident<$bool:literal, $string:literal>) => {
fn $ident<'de, D>(d: D) -> Result<(), D::Error>
where
D: serde::Deserializer<'de>,
{
struct V;
impl<'de> serde::de::Visitor<'de> for V {
type Value = ();
mod $ident {
pub(super) fn deserialize<'de, D>(d: D) -> Result<(), D::Error>
where
D: serde::Deserializer<'de>,
{
struct V;
impl<'de> serde::de::Visitor<'de> for V {
type Value = ();
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(concat!(
stringify!($bool),
" or \"",
stringify!($string),
"\""
))
}
fn expecting(
&self,
formatter: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
formatter.write_str(concat!(
stringify!($bool),
" or \"",
stringify!($string),
"\""
))
}
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match v {
$bool => Ok(()),
_ => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Bool(v),
&self,
)),
}
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match v {
$string => Ok(()),
_ => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(v),
&self,
)),
}
}
fn visit_enum<A>(self, a: A) -> Result<Self::Value, A::Error>
where
A: serde::de::EnumAccess<'de>,
{
use serde::de::VariantAccess;
let (variant, va) = a.variant::<&'de str>()?;
va.unit_variant()?;
match variant {
$string => Ok(()),
_ => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(variant),
&self,
)),
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match v {
$bool => Ok(()),
_ => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Bool(v),
&self,
)),
}
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match v {
$string => Ok(()),
_ => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(v),
&self,
)),
}
}
fn visit_enum<A>(self, a: A) -> Result<Self::Value, A::Error>
where
A: serde::de::EnumAccess<'de>,
{
use serde::de::VariantAccess;
let (variant, va) = a.variant::<&'de str>()?;
va.unit_variant()?;
match variant {
$string => Ok(()),
_ => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(variant),
&self,
)),
}
}
}
d.deserialize_any(V)
}
pub(super) fn serialize<S>(serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str($string)
}
d.deserialize_any(V)
}
};
}
create_bool_or_string_de!(true_or_always<true, "always">);
create_bool_or_string_de!(false_or_never<false, "never">);
create_bool_or_string_serde!(true_or_always<true, "always">);
create_bool_or_string_serde!(false_or_never<false, "never">);
macro_rules! named_unit_variant {
($variant:ident) => {
pub(super) fn $variant<'de, D>(deserializer: D) -> Result<(), D::Error>
where
D: serde::Deserializer<'de>,
{
struct V;
impl<'de> serde::de::Visitor<'de> for V {
type Value = ();
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(concat!("\"", stringify!($variant), "\""))
}
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
if value == stringify!($variant) {
Ok(())
} else {
Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
pub(super) mod $variant {
pub(in super::super) fn deserialize<'de, D>(deserializer: D) -> Result<(), D::Error>
where
D: serde::Deserializer<'de>,
{
struct V;
impl<'de> serde::de::Visitor<'de> for V {
type Value = ();
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(concat!("\"", stringify!($variant), "\""))
}
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
if value == stringify!($variant) {
Ok(())
} else {
Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
}
}
}
deserializer.deserialize_str(V)
}
pub(in super::super) fn serialize<S>(serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(stringify!($variant))
}
deserializer.deserialize_str(V)
}
};
}
mod de_unit_v {
mod unit_v {
named_unit_variant!(all);
named_unit_variant!(skip_trivial);
named_unit_variant!(mutable);
@@ -1927,7 +1991,7 @@ mod de_unit_v {
named_unit_variant!(both);
}
#[derive(Deserialize, Debug, Clone, Copy)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
enum SnippetScopeDef {
@@ -1937,67 +2001,92 @@ enum SnippetScopeDef {
Type,
}
#[derive(Deserialize, Debug, Clone, Default)]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(default)]
struct SnippetDef {
#[serde(deserialize_with = "single_or_array")]
#[serde(with = "single_or_array")]
#[serde(skip_serializing_if = "Vec::is_empty")]
prefix: Vec<String>,
#[serde(deserialize_with = "single_or_array")]
#[serde(with = "single_or_array")]
#[serde(skip_serializing_if = "Vec::is_empty")]
postfix: Vec<String>,
description: Option<String>,
#[serde(deserialize_with = "single_or_array")]
#[serde(with = "single_or_array")]
#[serde(skip_serializing_if = "Vec::is_empty")]
body: Vec<String>,
#[serde(deserialize_with = "single_or_array")]
#[serde(with = "single_or_array")]
#[serde(skip_serializing_if = "Vec::is_empty")]
requires: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
scope: SnippetScopeDef,
}
fn single_or_array<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: serde::Deserializer<'de>,
{
struct SingleOrVec;
mod single_or_array {
use serde::{Deserialize, Serialize};
impl<'de> serde::de::Visitor<'de> for SingleOrVec {
type Value = Vec<String>;
pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: serde::Deserializer<'de>,
{
struct SingleOrVec;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("string or array of strings")
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))
}
}
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)
}
deserializer.deserialize_any(SingleOrVec)
pub(super) fn serialize<S>(vec: &[String], serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match vec {
// [] case is handled by skip_serializing_if
[single] => serializer.serialize_str(single),
slice => slice.serialize(serializer),
}
}
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
enum ManifestOrProjectJson {
pub(crate) enum ManifestOrProjectJson {
Manifest(Utf8PathBuf),
ProjectJson(ProjectJsonData),
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
enum ExprFillDefaultDef {
pub(crate) enum ExprFillDefaultDef {
Todo,
Default,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
enum ImportGranularityDef {
Preserve,
@@ -2007,7 +2096,7 @@ enum ImportGranularityDef {
One,
}
#[derive(Deserialize, Debug, Copy, Clone)]
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
#[serde(rename_all = "snake_case")]
enum CallableCompletionDef {
FillArguments,
@@ -2015,54 +2104,54 @@ enum CallableCompletionDef {
None,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
enum CargoFeaturesDef {
#[serde(deserialize_with = "de_unit_v::all")]
pub(crate) enum CargoFeaturesDef {
#[serde(with = "unit_v::all")]
All,
Selected(Vec<String>),
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
enum InvocationStrategy {
pub(crate) enum InvocationStrategy {
Once,
PerWorkspace,
}
#[derive(Deserialize, Debug, Clone)]
struct CheckOnSaveTargets(#[serde(deserialize_with = "single_or_array")] Vec<String>);
#[derive(Serialize, Deserialize, Debug, Clone)]
pub(crate) struct CheckOnSaveTargets(#[serde(with = "single_or_array")] Vec<String>);
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
enum InvocationLocation {
pub(crate) enum InvocationLocation {
Root,
Workspace,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
enum LifetimeElisionDef {
#[serde(deserialize_with = "true_or_always")]
#[serde(with = "true_or_always")]
Always,
#[serde(deserialize_with = "false_or_never")]
#[serde(with = "false_or_never")]
Never,
#[serde(deserialize_with = "de_unit_v::skip_trivial")]
#[serde(with = "unit_v::skip_trivial")]
SkipTrivial,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
enum ClosureReturnTypeHintsDef {
#[serde(deserialize_with = "true_or_always")]
#[serde(with = "true_or_always")]
Always,
#[serde(deserialize_with = "false_or_never")]
#[serde(with = "false_or_never")]
Never,
#[serde(deserialize_with = "de_unit_v::with_block")]
#[serde(with = "unit_v::with_block")]
WithBlock,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
enum ClosureStyle {
ImplFn,
@@ -2071,40 +2160,40 @@ enum ClosureStyle {
Hide,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
enum ReborrowHintsDef {
#[serde(deserialize_with = "true_or_always")]
#[serde(with = "true_or_always")]
Always,
#[serde(deserialize_with = "false_or_never")]
#[serde(with = "false_or_never")]
Never,
#[serde(deserialize_with = "de_unit_v::mutable")]
#[serde(with = "unit_v::mutable")]
Mutable,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
enum AdjustmentHintsDef {
#[serde(deserialize_with = "true_or_always")]
#[serde(with = "true_or_always")]
Always,
#[serde(deserialize_with = "false_or_never")]
#[serde(with = "false_or_never")]
Never,
#[serde(deserialize_with = "de_unit_v::reborrow")]
#[serde(with = "unit_v::reborrow")]
Reborrow,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
enum DiscriminantHintsDef {
#[serde(deserialize_with = "true_or_always")]
#[serde(with = "true_or_always")]
Always,
#[serde(deserialize_with = "false_or_never")]
#[serde(with = "false_or_never")]
Never,
#[serde(deserialize_with = "de_unit_v::fieldless")]
#[serde(with = "unit_v::fieldless")]
Fieldless,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
enum AdjustmentHintsModeDef {
Prefix,
@@ -2113,15 +2202,15 @@ enum AdjustmentHintsModeDef {
PreferPostfix,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
enum FilesWatcherDef {
pub(crate) enum FilesWatcherDef {
Client,
Notify,
Server,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
enum ImportPrefixDef {
Plain,
@@ -2131,40 +2220,51 @@ enum ImportPrefixDef {
ByCrate,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
enum WorkspaceSymbolSearchScopeDef {
pub(crate) enum WorkspaceSymbolSearchScopeDef {
Workspace,
WorkspaceAndDependencies,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
enum SignatureDetail {
pub(crate) enum SignatureDetail {
Full,
Parameters,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
enum WorkspaceSymbolSearchKindDef {
pub(crate) enum WorkspaceSymbolSearchKindDef {
OnlyTypes,
AllSymbols,
}
#[derive(Deserialize, Debug, Copy, Clone)]
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
#[serde(untagged)]
pub enum MemoryLayoutHoverRenderKindDef {
#[serde(deserialize_with = "de_unit_v::decimal")]
pub(crate) enum MemoryLayoutHoverRenderKindDef {
#[serde(with = "unit_v::decimal")]
Decimal,
#[serde(deserialize_with = "de_unit_v::hexadecimal")]
#[serde(with = "unit_v::hexadecimal")]
Hexadecimal,
#[serde(deserialize_with = "de_unit_v::both")]
#[serde(with = "unit_v::both")]
Both,
}
#[derive(Deserialize, Debug, Clone, PartialEq)]
#[test]
fn untagged_option_hover_render_kind() {
let hex = MemoryLayoutHoverRenderKindDef::Hexadecimal;
let ser = serde_json::to_string(&Some(hex)).unwrap();
assert_eq!(&ser, "\"hexadecimal\"");
let opt: Option<_> = serde_json::from_str("\"hexadecimal\"").unwrap();
assert_eq!(opt, Some(hex));
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
#[serde(untagged)]
pub enum TargetDirectory {
@@ -2172,68 +2272,320 @@ pub enum TargetDirectory {
Directory(Utf8PathBuf),
}
macro_rules! _default_val {
(@verbatim: $s:literal, $ty:ty) => {{
let default_: $ty = serde_json::from_str(&$s).unwrap();
default_
}};
($default:expr, $ty:ty) => {{
let default_: $ty = $default;
default_
}};
}
macro_rules! _default_str {
(@verbatim: $s:literal, $_ty:ty) => {
$s.to_owned()
};
($default:expr, $ty:ty) => {{
let val = default_val!($default, $ty);
serde_json::to_string_pretty(&val).unwrap()
}};
}
macro_rules! _impl_for_config_data {
(local, $(
$(#[doc=$doc:literal])*
$field:ident : $ty:ty = $default:expr,
)*
) => {
impl Config {
$(
$($doc)*
#[allow(non_snake_case)]
fn $field(&self, _source_root: Option<SourceRootId>) -> &$ty {
if let Some(v) = self.client_config.local.$field.as_ref() {
return &v;
}
if let Some(v) = self.user_config.local.$field.as_ref() {
return &v;
}
&self.default_config.local.$field
}
)*
}
};
(global, $(
$(#[doc=$doc:literal])*
$field:ident : $ty:ty = $default:expr,
)*
) => {
impl Config {
$(
$($doc)*
#[allow(non_snake_case)]
pub(crate) fn $field(&self) -> &$ty {
if let Some(v) = self.client_config.global.$field.as_ref() {
return &v;
}
if let Some(v) = self.user_config.global.$field.as_ref() {
return &v;
}
&self.default_config.global.$field
}
)*
}
};
(client, $(
$(#[doc=$doc:literal])*
$field:ident : $ty:ty = $default:expr,
)*
) => {
impl Config {
$(
$($doc)*
#[allow(non_snake_case)]
fn $field(&self) -> &$ty {
if let Some(v) = self.client_config.global.$field.as_ref() {
return &v;
}
&self.default_config.client.$field
}
)*
}
};
}
macro_rules! _config_data {
(struct $name:ident {
// modname is for the tests
($(#[doc=$dox:literal])* $modname:ident: struct $name:ident <- $input:ident -> {
$(
$(#[doc=$doc:literal])*
$field:ident $(| $alias:ident)*: $ty:ty = $default:expr,
$field:ident $(| $alias:ident)*: $ty:ty = $(@$marker:ident: )? $default:expr,
)*
}) => {
/// All fields raw `T`, representing either a root config, or a root config + overrides from
/// some distal configuration blob(s).
#[allow(non_snake_case)]
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize)]
struct $name { $($field: $ty,)* }
impl $name {
fn from_json(mut json: serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>) -> $name {
impl_for_config_data!{
$modname,
$(
$field : $ty = $default,
)*
}
/// All fields `Option<T>`, `None` representing fields not set in a particular JSON/TOML blob.
#[allow(non_snake_case)]
#[derive(Clone, Serialize, Default)]
struct $input { $(
#[serde(skip_serializing_if = "Option::is_none")]
$field: Option<$ty>,
)* }
impl std::fmt::Debug for $input {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut s = f.debug_struct(stringify!($input));
$(
if let Some(val) = self.$field.as_ref() {
s.field(stringify!($field), val);
}
)*
s.finish()
}
}
impl Default for $name {
fn default() -> Self {
$name {$(
$field: default_val!($(@$marker:)? $default, $ty),
)*}
}
}
impl $name {
/// Applies overrides from some more local config blob, to self.
#[allow(unused)]
fn apply_input(&mut self, input: $input) {
$(
if let Some(value) = input.$field {
self.$field = value;
}
)*
}
#[allow(unused)]
fn clone_with_overrides(&self, input: $input) -> Self {
Self {$(
$field: input.$field.unwrap_or_else(|| self.$field.clone()),
)*}
}
}
impl $input {
#[allow(unused, clippy::ptr_arg)]
fn from_json(json: &mut serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>) -> Self {
Self {$(
$field: get_field(
&mut json,
json,
error_sink,
stringify!($field),
None$(.or(Some(stringify!($alias))))*,
$default,
),
)*}
}
fn json_schema() -> serde_json::Value {
schema(&[
$({
let field = stringify!($field);
let ty = stringify!($ty);
(field, ty, &[$($doc),*], $default)
},)*
])
#[allow(unused, clippy::ptr_arg)]
fn from_toml(toml: &mut toml::Table , error_sink: &mut Vec<(String, toml::de::Error)>) -> Self {
Self {$(
$field: get_field_toml::<$ty>(
toml,
error_sink,
stringify!($field),
None$(.or(Some(stringify!($alias))))*,
),
)*}
}
#[cfg(test)]
fn manual() -> String {
manual(&[
fn schema_fields(sink: &mut Vec<SchemaField>) {
sink.extend_from_slice(&[
$({
let field = stringify!($field);
let ty = stringify!($ty);
let default = default_str!($(@$marker:)? $default, $ty);
(field, ty, &[$($doc),*], $default)
(field, ty, &[$($doc),*], default)
},)*
])
}
}
#[test]
fn fields_are_sorted() {
[$(stringify!($field)),*].windows(2).for_each(|w| assert!(w[0] <= w[1], "{} <= {} does not hold", w[0], w[1]));
mod $modname {
#[test]
fn fields_are_sorted() {
let field_names: &'static [&'static str] = &[$(stringify!($field)),*];
field_names.windows(2).for_each(|w| assert!(w[0] <= w[1], "{} <= {} does not hold", w[0], w[1]));
}
}
};
}
use _config_data as config_data;
use _default_str as default_str;
use _default_val as default_val;
use _impl_for_config_data as impl_for_config_data;
#[derive(Default, Debug, Clone)]
struct ConfigData {
global: GlobalConfigData,
local: LocalConfigData,
client: ClientConfigData,
}
/// All of the config levels, all fields `Option<T>`, to describe fields that are actually set by
/// some rust-analyzer.toml file or JSON blob. An empty rust-analyzer.toml corresponds to
/// all fields being None.
#[derive(Debug, Clone, Serialize, Default)]
struct ConfigInput {
#[serde(flatten)]
global: GlobalConfigInput,
#[serde(flatten)]
local: LocalConfigInput,
#[serde(flatten)]
client: ClientConfigInput,
}
impl ConfigInput {
fn from_json(
mut json: serde_json::Value,
error_sink: &mut Vec<(String, serde_json::Error)>,
) -> ConfigInput {
ConfigInput {
global: GlobalConfigInput::from_json(&mut json, error_sink),
local: LocalConfigInput::from_json(&mut json, error_sink),
client: ClientConfigInput::from_json(&mut json, error_sink),
}
}
fn from_toml(
mut toml: toml::Table,
error_sink: &mut Vec<(String, toml::de::Error)>,
) -> ConfigInput {
ConfigInput {
global: GlobalConfigInput::from_toml(&mut toml, error_sink),
local: LocalConfigInput::from_toml(&mut toml, error_sink),
client: ClientConfigInput::from_toml(&mut toml, error_sink),
}
}
fn schema_fields() -> Vec<SchemaField> {
let mut fields = Vec::new();
GlobalConfigInput::schema_fields(&mut fields);
LocalConfigInput::schema_fields(&mut fields);
ClientConfigInput::schema_fields(&mut fields);
// HACK: sort the fields, so the diffs on the generated docs/schema are smaller
fields.sort_by_key(|&(x, ..)| x);
fields
}
fn json_schema() -> serde_json::Value {
schema(&Self::schema_fields())
}
#[cfg(test)]
fn manual() -> String {
manual(&Self::schema_fields())
}
}
fn get_field_toml<T: DeserializeOwned>(
val: &toml::Table,
error_sink: &mut Vec<(String, toml::de::Error)>,
field: &'static str,
alias: Option<&'static str>,
) -> Option<T> {
alias
.into_iter()
.chain(iter::once(field))
.filter_map(move |field| {
let subkeys = field.split('_');
let mut v = val;
for subkey in subkeys {
if let Some(val) = v.get(subkey) {
if let Some(map) = val.as_table() {
v = map;
} else {
return Some(toml::Value::try_into(val.clone()).map_err(|e| (e, v)));
}
} else {
return None;
}
}
None
})
.find(Result::is_ok)
.and_then(|res| match res {
Ok(it) => Some(it),
Err((e, pointer)) => {
error_sink.push((pointer.to_string(), e));
None
}
})
}
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,
) -> T {
) -> Option<T> {
// XXX: check alias first, to work around the VS Code where it pre-fills the
// defaults instead of sending an empty object.
alias
@@ -2254,12 +2606,11 @@ fn get_field<T: DeserializeOwned>(
None
}
})
.unwrap_or_else(|| {
serde_json::from_str(default).unwrap_or_else(|e| panic!("{e} on: `{default}`"))
})
}
fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json::Value {
type SchemaField = (&'static str, &'static str, &'static [&'static str], String);
fn schema(fields: &[SchemaField]) -> serde_json::Value {
let map = fields
.iter()
.map(|(field, ty, doc, default)| {
@@ -2310,7 +2661,7 @@ macro_rules! set {
"FxHashMap<Box<str>, Box<[Box<str>]>>" => set! {
"type": "object",
},
"FxHashMap<String, SnippetDef>" => set! {
"IndexMap<String, SnippetDef>" => set! {
"type": "object",
},
"FxHashMap<String, String>" => set! {
@@ -2621,7 +2972,7 @@ macro_rules! set {
}
#[cfg(test)]
fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String {
fn manual(fields: &[SchemaField]) -> String {
fields.iter().fold(String::new(), |mut acc, (field, _ty, doc, default)| {
let name = format!("rust-analyzer.{}", field.replace('_', "."));
let doc = doc_comment_to_string(doc);
@@ -2716,7 +3067,7 @@ fn generate_package_json_config() {
#[test]
fn generate_config_documentation() {
let docs_path = project_root().join("docs/user/generated_config.adoc");
let expected = ConfigData::manual();
let expected = ConfigInput::manual();
ensure_file_contents(&docs_path, &expected);
}
@@ -2788,7 +3139,7 @@ fn cargo_target_dir_unset() {
"rust": { "analyzerTargetDir": null }
}))
.unwrap();
assert_eq!(config.data.cargo_targetDir, None);
assert_eq!(config.cargo_targetDir(), &None);
assert!(
matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none())
);
@@ -2807,7 +3158,7 @@ fn cargo_target_dir_subdir() {
"rust": { "analyzerTargetDir": true }
}))
.unwrap();
assert_eq!(config.data.cargo_targetDir, Some(TargetDirectory::UseSubdirectory(true)));
assert_eq!(config.cargo_targetDir(), &Some(TargetDirectory::UseSubdirectory(true)));
assert!(
matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer")))
);
@@ -2827,8 +3178,8 @@ fn cargo_target_dir_relative_dir() {
}))
.unwrap();
assert_eq!(
config.data.cargo_targetDir,
Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder")))
config.cargo_targetDir(),
&Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder")))
);
assert!(
matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("other_folder")))
+3 -1
View File
@@ -154,10 +154,12 @@ pub(crate) fn fetch_native_diagnostics(
.copied()
.filter_map(|file_id| {
let line_index = snapshot.file_line_index(file_id).ok()?;
let source_root = snapshot.analysis.source_root(file_id).ok()?;
let diagnostics = snapshot
.analysis
.diagnostics(
&snapshot.config.diagnostics(),
&snapshot.config.diagnostics(Some(source_root)),
ide::AssistResolveStrategy::None,
file_id,
)
+1 -1
View File
@@ -187,7 +187,7 @@ pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> Global
};
let mut analysis_host = AnalysisHost::new(config.lru_parse_query_capacity());
if let Some(capacities) = config.lru_query_capacities() {
if let Some(capacities) = config.lru_query_capacities_config() {
analysis_host.update_lru_capacities(capacities);
}
let (flycheck_sender, flycheck_receiver) = unbounded();
+40 -23
View File
@@ -355,8 +355,9 @@ pub(crate) fn handle_join_lines(
) -> anyhow::Result<Vec<lsp_types::TextEdit>> {
let _p = tracing::span!(tracing::Level::INFO, "handle_join_lines").entered();
let config = snap.config.join_lines();
let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
let source_root = snap.analysis.source_root(file_id)?;
let config = snap.config.join_lines(Some(source_root));
let line_index = snap.file_line_index(file_id)?;
let mut res = TextEdit::default();
@@ -923,7 +924,8 @@ pub(crate) fn handle_completion(
let completion_trigger_character =
params.context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next());
let completion_config = &snap.config.completion();
let source_root = snap.analysis.source_root(position.file_id)?;
let completion_config = &snap.config.completion(Some(source_root));
let items = match snap.analysis.completions(
completion_config,
position,
@@ -964,11 +966,12 @@ pub(crate) fn handle_completion_resolve(
let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?;
let line_index = snap.file_line_index(file_id)?;
let offset = from_proto::offset(&line_index, resolve_data.position.position)?;
let source_root = snap.analysis.source_root(file_id)?;
let additional_edits = snap
.analysis
.resolve_completion_edits(
&snap.config.completion(),
&snap.config.completion(Some(source_root)),
FilePosition { file_id, offset },
resolve_data
.imports
@@ -1038,16 +1041,17 @@ pub(crate) fn handle_hover(
PositionOrRange::Position(position) => Range::new(position, position),
PositionOrRange::Range(range) => range,
};
let file_range = from_proto::file_range(&snap, &params.text_document, range)?;
let info = match snap.analysis.hover(&snap.config.hover(), file_range)? {
let hover = snap.config.hover();
let info = match snap.analysis.hover(&hover, file_range)? {
None => return Ok(None),
Some(info) => info,
};
let line_index = snap.file_line_index(file_range.file_id)?;
let range = to_proto::range(&line_index, info.range);
let markup_kind = snap.config.hover().format;
let markup_kind = hover.format;
let hover = lsp_ext::Hover {
hover: lsp_types::Hover {
contents: HoverContents::Markup(to_proto::markup_content(
@@ -1191,11 +1195,12 @@ pub(crate) fn handle_code_action(
return Ok(None);
}
let line_index =
snap.file_line_index(from_proto::file_id(&snap, &params.text_document.uri)?)?;
let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
let line_index = snap.file_line_index(file_id)?;
let frange = from_proto::file_range(&snap, &params.text_document, params.range)?;
let source_root = snap.analysis.source_root(file_id)?;
let mut assists_config = snap.config.assist();
let mut assists_config = snap.config.assist(Some(source_root));
assists_config.allowed = params
.context
.only
@@ -1212,7 +1217,7 @@ pub(crate) fn handle_code_action(
};
let assists = snap.analysis.assists_with_fixes(
&assists_config,
&snap.config.diagnostics(),
&snap.config.diagnostics(Some(source_root)),
resolve,
frange,
)?;
@@ -1266,8 +1271,9 @@ pub(crate) fn handle_code_action_resolve(
let line_index = snap.file_line_index(file_id)?;
let range = from_proto::text_range(&line_index, params.code_action_params.range)?;
let frange = FileRange { file_id, range };
let source_root = snap.analysis.source_root(file_id)?;
let mut assists_config = snap.config.assist();
let mut assists_config = snap.config.assist(Some(source_root));
assists_config.allowed = params
.code_action_params
.context
@@ -1290,7 +1296,7 @@ pub(crate) fn handle_code_action_resolve(
let assists = snap.analysis.assists_with_fixes(
&assists_config,
&snap.config.diagnostics(),
&snap.config.diagnostics(Some(source_root)),
AssistResolveStrategy::Single(assist_resolve),
frange,
)?;
@@ -1419,8 +1425,12 @@ pub(crate) fn handle_document_highlight(
let _p = tracing::span!(tracing::Level::INFO, "handle_document_highlight").entered();
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
let line_index = snap.file_line_index(position.file_id)?;
let source_root = snap.analysis.source_root(position.file_id)?;
let refs = match snap.analysis.highlight_related(snap.config.highlight_related(), position)? {
let refs = match snap
.analysis
.highlight_related(snap.config.highlight_related(Some(source_root)), position)?
{
None => return Ok(None),
Some(refs) => refs,
};
@@ -1466,7 +1476,9 @@ pub(crate) fn handle_inlay_hints(
params.range,
)?;
let line_index = snap.file_line_index(file_id)?;
let inlay_hints_config = snap.config.inlay_hints();
let source_root = snap.analysis.source_root(file_id)?;
let inlay_hints_config = snap.config.inlay_hints(Some(source_root));
Ok(Some(
snap.analysis
.inlay_hints(&inlay_hints_config, file_id, Some(range))?
@@ -1501,7 +1513,9 @@ pub(crate) fn handle_inlay_hints_resolve(
let line_index = snap.file_line_index(file_id)?;
let hint_position = from_proto::offset(&line_index, original_hint.position)?;
let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints();
let source_root = snap.analysis.source_root(file_id)?;
let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(Some(source_root));
forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty();
let resolve_hints = snap.analysis.inlay_hints_resolve(
&forced_resolve_inlay_hints_config,
@@ -1633,8 +1647,9 @@ pub(crate) fn handle_semantic_tokens_full(
let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
let text = snap.analysis.file_text(file_id)?;
let line_index = snap.file_line_index(file_id)?;
let source_root = snap.analysis.source_root(file_id)?;
let mut highlight_config = snap.config.highlighting_config();
let mut highlight_config = snap.config.highlighting_config(Some(source_root));
// Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
highlight_config.syntactic_name_ref_highlighting =
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
@@ -1645,7 +1660,7 @@ pub(crate) fn handle_semantic_tokens_full(
&line_index,
highlights,
snap.config.semantics_tokens_augments_syntax_tokens(),
snap.config.highlighting_non_standard_tokens(),
snap.config.highlighting_non_standard_tokens(Some(source_root)),
);
// Unconditionally cache the tokens
@@ -1663,8 +1678,9 @@ pub(crate) fn handle_semantic_tokens_full_delta(
let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
let text = snap.analysis.file_text(file_id)?;
let line_index = snap.file_line_index(file_id)?;
let source_root = snap.analysis.source_root(file_id)?;
let mut highlight_config = snap.config.highlighting_config();
let mut highlight_config = snap.config.highlighting_config(Some(source_root));
// Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
highlight_config.syntactic_name_ref_highlighting =
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
@@ -1675,7 +1691,7 @@ pub(crate) fn handle_semantic_tokens_full_delta(
&line_index,
highlights,
snap.config.semantics_tokens_augments_syntax_tokens(),
snap.config.highlighting_non_standard_tokens(),
snap.config.highlighting_non_standard_tokens(Some(source_root)),
);
let cached_tokens = snap.semantic_tokens_cache.lock().remove(&params.text_document.uri);
@@ -1706,8 +1722,9 @@ pub(crate) fn handle_semantic_tokens_range(
let frange = from_proto::file_range(&snap, &params.text_document, params.range)?;
let text = snap.analysis.file_text(frange.file_id)?;
let line_index = snap.file_line_index(frange.file_id)?;
let source_root = snap.analysis.source_root(frange.file_id)?;
let mut highlight_config = snap.config.highlighting_config();
let mut highlight_config = snap.config.highlighting_config(Some(source_root));
// Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
highlight_config.syntactic_name_ref_highlighting =
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
@@ -1718,7 +1735,7 @@ pub(crate) fn handle_semantic_tokens_range(
&line_index,
highlights,
snap.config.semantics_tokens_augments_syntax_tokens(),
snap.config.highlighting_non_standard_tokens(),
snap.config.highlighting_non_standard_tokens(Some(source_root)),
);
Ok(Some(semantic_tokens.into()))
}
@@ -1931,8 +1948,8 @@ fn goto_type_action_links(
snap: &GlobalStateSnapshot,
nav_targets: &[HoverGotoTypeData],
) -> Option<lsp_ext::CommandLinkGroup> {
if !snap.config.hover_actions().goto_type_def
|| nav_targets.is_empty()
if nav_targets.is_empty()
|| !snap.config.hover_actions().goto_type_def
|| !snap.config.client_commands().goto_location
{
return None;
+2 -2
View File
@@ -233,7 +233,7 @@ pub(crate) fn completion_items(
completion_item(&mut res, config, line_index, &tdpp, max_relevance, item);
}
if let Some(limit) = config.completion().limit {
if let Some(limit) = config.completion(None).limit {
res.sort_by(|item1, item2| item1.sort_text.cmp(&item2.sort_text));
res.truncate(limit);
}
@@ -317,7 +317,7 @@ fn completion_item(
set_score(&mut lsp_item, max_relevance, item.relevance);
if config.completion().enable_imports_on_the_fly && !item.import_to_add.is_empty() {
if config.completion(None).enable_imports_on_the_fly && !item.import_to_add.is_empty() {
let imports = item
.import_to_add
.into_iter()
+1 -1
View File
@@ -434,7 +434,7 @@ fn handle_event(&mut self, event: Event) -> anyhow::Result<()> {
}
}
if self.config.cargo_autoreload() {
if self.config.cargo_autoreload_config() {
if let Some((cause, force_crate_graph_reload)) =
self.fetch_workspaces_queue.should_start_op()
{
+2 -2
View File
@@ -76,9 +76,9 @@ pub(crate) fn update_configuration(&mut self, config: Config) {
if self.config.lru_parse_query_capacity() != old_config.lru_parse_query_capacity() {
self.analysis_host.update_lru_capacity(self.config.lru_parse_query_capacity());
}
if self.config.lru_query_capacities() != old_config.lru_query_capacities() {
if self.config.lru_query_capacities_config() != old_config.lru_query_capacities_config() {
self.analysis_host.update_lru_capacities(
&self.config.lru_query_capacities().cloned().unwrap_or_default(),
&self.config.lru_query_capacities_config().cloned().unwrap_or_default(),
);
}
if self.config.linked_or_discovered_projects() != old_config.linked_or_discovered_projects()
+48 -41
View File
@@ -159,10 +159,17 @@ building from locking the `Cargo.lock` at the expense of duplicating build artif
Set to `true` to use a subdirectory of the existing target directory or
set to a path relative to the workspace to use that path.
--
[[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest (default: `["core"]`)::
[[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest::
+
--
Default:
----
[
"core"
]
----
Unsets the implicit `#[cfg(test)]` for the specified crates.
--
[[rust-analyzer.checkOnSave]]rust-analyzer.checkOnSave (default: `true`)::
+
@@ -321,46 +328,46 @@ Enables completions of private items and fields that are defined in the current
Default:
----
{
"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"
}
}
"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"
}
}
----
Custom completion snippets.