X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=crates%2Frust-analyzer%2Fsrc%2Fconfig.rs;h=cac48e9117099074a33638e3137cdc66b32e3d2e;hb=96fc01a30b88d95619b26fd96c58627dd54cb339;hp=27b92a5a9bf47478e06488758518761e0a7712f5;hpb=60c501fa19dc446ca8122db17bca9e0dcd5233a2;p=rust.git diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 27b92a5a9bf..cac48e91170 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -7,23 +7,26 @@ //! configure the server itself, feature flags are passed into analysis, and //! tweak things like automatic insertion of `()` in completions. -use std::{convert::TryFrom, ffi::OsString, iter, path::PathBuf}; +use std::{ffi::OsString, iter, path::PathBuf}; use flycheck::FlycheckConfig; use hir::PrefixKind; -use ide::{ - AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, - InsertUseConfig, +use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig}; +use ide_db::helpers::{ + insert_use::{InsertUseConfig, MergeBehavior}, + SnippetCap, }; -use ide_db::helpers::{insert_use::MergeBehavior, SnippetCap}; use itertools::Itertools; use lsp_types::{ClientCapabilities, MarkupKind}; -use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; +use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; use rustc_hash::FxHashSet; use serde::{de::DeserializeOwned, Deserialize}; use vfs::AbsPathBuf; -use crate::{caps::completion_item_edit_resolve, diagnostics::DiagnosticsMapConfig}; +use crate::{ + caps::completion_item_edit_resolve, diagnostics::DiagnosticsMapConfig, + line_index::OffsetEncoding, lsp_ext::supports_utf8, +}; config_data! { struct ConfigData { @@ -32,19 +35,20 @@ struct ConfigData { assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"", /// The path structure for newly inserted paths to use. assist_importPrefix: ImportPrefixDef = "\"plain\"", - + /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. + assist_importGroup: bool = "true", /// Show function name and docs in parameter hints. callInfo_full: bool = "true", /// Automatically refresh project info via `cargo metadata` on /// `Cargo.toml` changes. cargo_autoreload: bool = "true", - /// Activate all available features. + /// Activate all available features (`--all-features`). cargo_allFeatures: bool = "false", /// List of features to activate. cargo_features: Vec = "[]", - /// Run `cargo check` on startup to get the correct value for package - /// OUT_DIRs. + /// Run build scripts (`build.rs`) for more precise code analysis. + cargo_runBuildScripts | cargo_loadOutDirsFromCheck: bool = "false", /// Do not activate the `default` feature. cargo_noDefaultFeatures: bool = "false", @@ -55,10 +59,10 @@ struct ConfigData { /// Run specified `cargo check` command for diagnostics on save. checkOnSave_enable: bool = "true", - /// Check with all features (will be passed as `--all-features`). + /// Check with all features (`--all-features`). /// Defaults to `#rust-analyzer.cargo.allFeatures#`. checkOnSave_allFeatures: Option = "null", - /// Check all targets and tests (will be passed as `--all-targets`). + /// Check all targets and tests (`--all-targets`). checkOnSave_allTargets: bool = "true", /// Cargo command to use for `cargo check`. checkOnSave_command: String = "\"check\"", @@ -105,6 +109,8 @@ struct ConfigData { /// Controls file watching implementation. files_watcher: String = "\"client\"", + /// These directories will be ignored by rust-analyzer. + files_excludeDirs: Vec = "[]", /// Whether to show `Debug` action. Only applies when /// `#rust-analyzer.hoverActions.enable#` is set. @@ -147,20 +153,22 @@ struct ConfigData { /// Whether to show `Method References` lens. Only applies when /// `#rust-analyzer.lens.enable#` is set. lens_methodReferences: bool = "false", + /// Whether to show `References` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_references: bool = "false", /// Disable project auto-discovery in favor of explicitly specified set /// of projects.\n\nElements must be paths pointing to `Cargo.toml`, /// `rust-project.json`, or JSON objects in `rust-project.json` format. linkedProjects: Vec = "[]", - /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. + /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. lruCapacity: Option = "null", /// Whether to show `can't find Cargo.toml` error message. notifications_cargoTomlNotFound: bool = "true", - /// Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be - /// enabled. + /// Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScripts#`. procMacro_enable: bool = "false", /// Internal config, path to proc-macro server executable (typically, /// this is rust-analyzer itself, but we override this in tests). @@ -172,8 +180,9 @@ struct ConfigData { /// tests or binaries.\nFor example, it may be `--release`. runnables_cargoExtraArgs: Vec = "[]", - /// Path to the rust compiler sources, for usage in rustc_private projects. - rustcSource : Option = "null", + /// Path to the rust compiler sources, for usage in rustc_private projects, or "discover" + /// to try to automatically find it. + rustcSource : Option = "null", /// Additional arguments to `rustfmt`. rustfmt_extraArgs: Vec = "[]", @@ -221,6 +230,7 @@ pub struct LensConfig { pub debug: bool, pub implementations: bool, pub method_refs: bool, + pub refs: bool, // for Struct, Enum, Union and Trait } impl LensConfig { @@ -237,14 +247,14 @@ pub fn runnable(&self) -> bool { } pub fn references(&self) -> bool { - self.method_refs + self.method_refs || self.refs } } #[derive(Debug, Clone)] pub struct FilesConfig { pub watcher: FilesWatcher, - pub exclude: Vec, + pub exclude: Vec, } #[derive(Debug, Clone)] @@ -408,6 +418,13 @@ pub fn signature_help_label_offsets(&self) -> bool { false ) } + pub fn offset_encoding(&self) -> OffsetEncoding { + if supports_utf8(&self.caps) { + OffsetEncoding::Utf8 + } else { + OffsetEncoding::Utf16 + } + } fn experimental(&self, index: &'static str) -> bool { try_or!(self.caps.experimental.as_ref()?.get(index)?.as_bool()?, false) @@ -454,7 +471,7 @@ pub fn files(&self) -> FilesConfig { "notify" => FilesWatcher::Notify, "client" | _ => FilesWatcher::Client, }, - exclude: Vec::new(), + exclude: self.data.files_excludeDirs.iter().map(|it| self.root_path.join(it)).collect(), } } pub fn notifications(&self) -> NotificationsConfig { @@ -463,18 +480,22 @@ pub fn notifications(&self) -> NotificationsConfig { pub fn cargo_autoreload(&self) -> bool { self.data.cargo_autoreload } + pub fn run_build_scripts(&self) -> bool { + self.data.cargo_runBuildScripts || self.data.procMacro_enable + } pub fn cargo(&self) -> CargoConfig { - let rustc_source = self.data.rustcSource.clone().and_then(|it| { - AbsPathBuf::try_from(it) - .map_err(|_| log::error!("rustc source directory must be an absolute path")) - .ok() + let rustc_source = self.data.rustcSource.as_ref().map(|rustc_src| { + if rustc_src == "discover" { + RustcSource::Discover + } else { + RustcSource::Path(self.root_path.join(rustc_src)) + } }); CargoConfig { no_default_features: self.data.cargo_noDefaultFeatures, all_features: self.data.cargo_allFeatures, features: self.data.cargo_features.clone(), - load_out_dirs_from_check: self.data.cargo_loadOutDirsFromCheck, target: self.data.cargo_target.clone(), rustc_source, no_sysroot: self.data.cargo_noSysroot, @@ -508,7 +529,7 @@ pub fn flycheck(&self) -> Option { .data .checkOnSave_target .clone() - .or(self.data.cargo_target.clone()), + .or_else(|| self.data.cargo_target.clone()), all_targets: self.data.checkOnSave_allTargets, no_default_features: self .data @@ -522,7 +543,7 @@ pub fn flycheck(&self) -> Option { .data .checkOnSave_features .clone() - .unwrap_or(self.data.cargo_features.clone()), + .unwrap_or_else(|| self.data.cargo_features.clone()), extra_args: self.data.checkOnSave_extraArgs.clone(), }, }; @@ -542,21 +563,29 @@ pub fn inlay_hints(&self) -> InlayHintsConfig { max_length: self.data.inlayHints_maxLength, } } - fn merge_behavior(&self) -> Option { - match self.data.assist_importMergeBehavior { - MergeBehaviorDef::None => None, - MergeBehaviorDef::Full => Some(MergeBehavior::Full), - MergeBehaviorDef::Last => Some(MergeBehavior::Last), + fn insert_use_config(&self) -> InsertUseConfig { + InsertUseConfig { + merge: match self.data.assist_importMergeBehavior { + MergeBehaviorDef::None => None, + MergeBehaviorDef::Full => Some(MergeBehavior::Full), + MergeBehaviorDef::Last => Some(MergeBehavior::Last), + }, + prefix_kind: match self.data.assist_importPrefix { + ImportPrefixDef::Plain => PrefixKind::Plain, + ImportPrefixDef::ByCrate => PrefixKind::ByCrate, + ImportPrefixDef::BySelf => PrefixKind::BySelf, + }, + group: self.data.assist_importGroup, } } pub fn completion(&self) -> CompletionConfig { CompletionConfig { enable_postfix_completions: self.data.completion_postfix_enable, - enable_autoimport_completions: self.data.completion_autoimport_enable + enable_imports_on_the_fly: self.data.completion_autoimport_enable && completion_item_edit_resolve(&self.caps), add_call_parenthesis: self.data.completion_addCallParenthesis, add_call_argument_snippets: self.data.completion_addCallArgumentSnippets, - merge: self.merge_behavior(), + insert_use: self.insert_use_config(), snippet_cap: SnippetCap::new(try_or!( self.caps .text_document @@ -574,14 +603,7 @@ pub fn assist(&self) -> AssistConfig { AssistConfig { snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")), allowed: None, - insert_use: InsertUseConfig { - merge: self.merge_behavior(), - prefix_kind: match self.data.assist_importPrefix { - ImportPrefixDef::Plain => PrefixKind::Plain, - ImportPrefixDef::ByCrate => PrefixKind::ByCrate, - ImportPrefixDef::BySelf => PrefixKind::BySelf, - }, - }, + insert_use: self.insert_use_config(), } } pub fn call_info_full(&self) -> bool { @@ -593,6 +615,7 @@ pub fn lens(&self) -> LensConfig { debug: self.data.lens_enable && self.data.lens_debug, implementations: self.data.lens_enable && self.data.lens_implementations, method_refs: self.data.lens_enable && self.data.lens_methodReferences, + refs: self.data.lens_enable && self.data.lens_references, } } pub fn hover(&self) -> HoverConfig { @@ -719,7 +742,7 @@ fn get_field( fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json::Value { for ((f1, ..), (f2, ..)) in fields.iter().zip(&fields[1..]) { fn key(f: &str) -> &str { - f.splitn(2, "_").next().unwrap() + f.splitn(2, '_').next().unwrap() } assert!(key(f1) <= key(f2), "wrong field order: {:?} {:?}", f1, f2); } @@ -762,6 +785,10 @@ macro_rules! set { "type": "array", "items": { "type": "string" }, }, + "Vec" => set! { + "type": "array", + "items": { "type": "string" }, + }, "FxHashSet" => set! { "type": "array", "items": { "type": "string" }, @@ -839,15 +866,32 @@ mod tests { fn schema_in_sync_with_package_json() { let s = Config::json_schema(); let schema = format!("{:#}", s); - let schema = schema.trim_start_matches('{').trim_end_matches('}'); - - let package_json = project_dir().join("editors/code/package.json"); - let package_json = fs::read_to_string(&package_json).unwrap(); - - let p = remove_ws(&package_json); + let mut schema = schema + .trim_start_matches('{') + .trim_end_matches('}') + .replace(" ", " ") + .replace("\n", "\n ") + .trim_start_matches('\n') + .trim_end() + .to_string(); + schema.push_str(",\n"); + + let package_json_path = project_dir().join("editors/code/package.json"); + let mut package_json = fs::read_to_string(&package_json_path).unwrap(); + + let start_marker = " \"$generated-start\": false,\n"; + let end_marker = " \"$generated-end\": false\n"; + + let start = package_json.find(start_marker).unwrap() + start_marker.len(); + let end = package_json.find(end_marker).unwrap(); + let p = remove_ws(&package_json[start..end]); let s = remove_ws(&schema); - assert!(p.contains(&s), "update config in package.json. New config:\n{:#}", schema); + if !p.contains(&s) { + package_json.replace_range(start..end, &schema); + fs::write(&package_json_path, &mut package_json).unwrap(); + panic!("new config, updating package.json") + } } #[test]