//! 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 {
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<String> = "[]",
- /// 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",
/// 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<bool> = "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\"",
/// Controls file watching implementation.
files_watcher: String = "\"client\"",
+ /// These directories will be ignored by rust-analyzer.
+ files_excludeDirs: Vec<PathBuf> = "[]",
/// Whether to show `Debug` action. Only applies when
/// `#rust-analyzer.hoverActions.enable#` is set.
/// Whether to show `Method References` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_methodReferences: bool = "false",
+ /// Whether to show `References` lens. Only applies when
+ /// `#rust-analyzer.lens.enable#` is set.
+ 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<ManifestOrProjectJson> = "[]",
- /// 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<usize> = "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).
/// tests or binaries.\nFor example, it may be `--release`.
runnables_cargoExtraArgs: Vec<String> = "[]",
- /// Path to the rust compiler sources, for usage in rustc_private projects.
- rustcSource : Option<PathBuf> = "null",
+ /// Path to the rust compiler sources, for usage in rustc_private projects, or "discover"
+ /// to try to automatically find it.
+ rustcSource : Option<String> = "null",
/// Additional arguments to `rustfmt`.
rustfmt_extraArgs: Vec<String> = "[]",
pub debug: bool,
pub implementations: bool,
pub method_refs: bool,
+ pub refs: bool, // for Struct, Enum, Union and Trait
}
impl LensConfig {
}
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<String>,
+ pub exclude: Vec<AbsPathBuf>,
}
#[derive(Debug, Clone)]
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)
"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 {
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,
.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
.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(),
},
};
max_length: self.data.inlayHints_maxLength,
}
}
- fn merge_behavior(&self) -> Option<MergeBehavior> {
- 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
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 {
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 {
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);
}
"type": "array",
"items": { "type": "string" },
},
+ "Vec<PathBuf>" => set! {
+ "type": "array",
+ "items": { "type": "string" },
+ },
"FxHashSet<String>" => set! {
"type": "array",
"items": { "type": "string" },
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]