From 39a2bc5e3cd86876eef6f3a96bef188f88e85114 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 21 Jul 2020 12:52:51 +0200 Subject: Expose package roots more directly --- crates/ra_project_model/src/lib.rs | 62 ++++++++++++++++++++------------------ crates/rust-analyzer/src/reload.rs | 40 +++++++++++------------- crates/vfs/src/loader.rs | 2 +- 3 files changed, 50 insertions(+), 54 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index b9c5424bf..cf46048e5 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -37,28 +37,10 @@ pub enum ProjectWorkspace { /// the current workspace. #[derive(Debug, Clone)] pub struct PackageRoot { - /// Path to the root folder - path: AbsPathBuf, /// Is a member of the current workspace - is_member: bool, - out_dir: Option, -} -impl PackageRoot { - pub fn new_member(path: AbsPathBuf) -> PackageRoot { - Self { path, is_member: true, out_dir: None } - } - pub fn new_non_member(path: AbsPathBuf) -> PackageRoot { - Self { path, is_member: false, out_dir: None } - } - pub fn path(&self) -> &AbsPath { - &self.path - } - pub fn out_dir(&self) -> Option<&AbsPath> { - self.out_dir.as_deref() - } - pub fn is_member(&self) -> bool { - self.is_member - } + pub is_member: bool, + pub include: Vec, + pub exclude: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] @@ -195,18 +177,38 @@ impl ProjectWorkspace { /// the root is a member of the current workspace pub fn to_roots(&self) -> Vec { match self { - ProjectWorkspace::Json { project } => { - project.roots.iter().map(|r| PackageRoot::new_member(r.path.clone())).collect() - } + ProjectWorkspace::Json { project } => project + .roots + .iter() + .map(|r| { + let path = r.path.clone(); + let include = vec![path]; + PackageRoot { is_member: true, include, exclude: Vec::new() } + }) + .collect(), ProjectWorkspace::Cargo { cargo, sysroot } => cargo .packages() - .map(|pkg| PackageRoot { - path: cargo[pkg].root().to_path_buf(), - is_member: cargo[pkg].is_member, - out_dir: cargo[pkg].out_dir.clone(), + .map(|pkg| { + let is_member = cargo[pkg].is_member; + let pkg_root = cargo[pkg].root().to_path_buf(); + + let mut include = vec![pkg_root.clone()]; + include.extend(cargo[pkg].out_dir.clone()); + + let mut exclude = vec![pkg_root.join(".git")]; + if is_member { + exclude.push(pkg_root.join("target")); + } else { + exclude.push(pkg_root.join("tests")); + exclude.push(pkg_root.join("examples")); + exclude.push(pkg_root.join("benches")); + } + PackageRoot { is_member, include, exclude } }) - .chain(sysroot.crates().map(|krate| { - PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) + .chain(sysroot.crates().map(|krate| PackageRoot { + is_member: false, + include: vec![sysroot[krate].root_dir().to_path_buf()], + exclude: Vec::new(), })) .collect(), } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index d7ae00b07..1907f2f13 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -5,7 +5,7 @@ use flycheck::FlycheckHandle; use ra_db::{CrateGraph, SourceRoot, VfsPath}; use ra_ide::AnalysisChange; use ra_prof::profile; -use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace}; +use ra_project_model::{ProcMacroClient, ProjectWorkspace}; use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; use crate::{ @@ -149,8 +149,10 @@ impl GlobalState { watchers: workspaces .iter() .flat_map(ProjectWorkspace::to_roots) - .filter(PackageRoot::is_member) - .map(|root| format!("{}/**/*.rs", root.path().display())) + .filter(|it| it.is_member) + .flat_map(|root| { + root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display())) + }) .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None }) .collect(), }; @@ -261,31 +263,23 @@ impl ProjectFolders { let mut local_filesets = vec![]; for root in workspaces.iter().flat_map(|it| it.to_roots()) { - let path = root.path().to_owned(); - - let mut file_set_roots: Vec = vec![]; + let file_set_roots: Vec = + root.include.iter().cloned().map(VfsPath::from).collect(); - let entry = if root.is_member() { - vfs::loader::Entry::local_cargo_package(path.to_path_buf()) - } else { - vfs::loader::Entry::cargo_package_dependency(path.to_path_buf()) + let entry = { + let mut dirs = vfs::loader::Directories::default(); + dirs.extensions.push("rs".into()); + dirs.include.extend(root.include); + dirs.exclude.extend(root.exclude); + vfs::loader::Entry::Directories(dirs) }; - res.load.push(entry); - if root.is_member() { - res.watch.push(res.load.len() - 1); - } - if let Some(out_dir) = root.out_dir() { - let out_dir = out_dir.to_path_buf(); - res.load.push(vfs::loader::Entry::rs_files_recursively(out_dir.clone())); - if root.is_member() { - res.watch.push(res.load.len() - 1); - } - file_set_roots.push(out_dir.into()); + if root.is_member { + res.watch.push(res.load.len()); } - file_set_roots.push(path.to_path_buf().into()); + res.load.push(entry); - if root.is_member() { + if root.is_member { local_filesets.push(fsc.len()); } fsc.add_file_set(file_set_roots) diff --git a/crates/vfs/src/loader.rs b/crates/vfs/src/loader.rs index 04e257f53..40cf96020 100644 --- a/crates/vfs/src/loader.rs +++ b/crates/vfs/src/loader.rs @@ -17,7 +17,7 @@ pub enum Entry { /// * it is not under `exclude` path /// /// If many include/exclude paths match, the longest one wins. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct Directories { pub extensions: Vec, pub include: Vec, -- cgit v1.2.3 From fe87aec7b61c7cf4c62162f257655507c4fd9422 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 21 Jul 2020 14:57:20 +0200 Subject: Replace roots with include/exclude directories --- crates/ra_project_model/src/lib.rs | 29 ++++++++---------------- crates/ra_project_model/src/project_json.rs | 34 +++++++++++++++++++---------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index cf46048e5..05f2e7b7a 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -7,7 +7,6 @@ mod sysroot; use std::{ fs::{self, read_dir, ReadDir}, io, - path::Path, process::{Command, Output}, }; @@ -35,7 +34,7 @@ pub enum ProjectWorkspace { /// `PackageRoot` describes a package root folder. /// Which may be an external dependency, or a member of /// the current workspace. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct PackageRoot { /// Is a member of the current workspace pub is_member: bool, @@ -178,14 +177,16 @@ impl ProjectWorkspace { pub fn to_roots(&self) -> Vec { match self { ProjectWorkspace::Json { project } => project - .roots + .crates .iter() - .map(|r| { - let path = r.path.clone(); - let include = vec![path]; - PackageRoot { is_member: true, include, exclude: Vec::new() } + .map(|krate| PackageRoot { + is_member: krate.is_workspace_member, + include: krate.include.clone(), + exclude: krate.exclude.clone(), }) - .collect(), + .collect::>() + .into_iter() + .collect::>(), ProjectWorkspace::Cargo { cargo, sysroot } => cargo .packages() .map(|pkg| { @@ -505,18 +506,6 @@ impl ProjectWorkspace { } crate_graph } - - pub fn workspace_root_for(&self, path: &Path) -> Option<&AbsPath> { - match self { - ProjectWorkspace::Cargo { cargo, .. } => { - Some(cargo.workspace_root()).filter(|root| path.starts_with(root)) - } - ProjectWorkspace::Json { project: ProjectJson { roots, .. }, .. } => roots - .iter() - .find(|root| path.starts_with(&root.path)) - .map(|root| root.path.as_path()), - } - } } fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions { diff --git a/crates/ra_project_model/src/project_json.rs b/crates/ra_project_model/src/project_json.rs index 778cc84ef..e0052ac6d 100644 --- a/crates/ra_project_model/src/project_json.rs +++ b/crates/ra_project_model/src/project_json.rs @@ -12,17 +12,9 @@ use stdx::split_delim; /// Roots and crates that compose this Rust project. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ProjectJson { - pub(crate) roots: Vec, pub(crate) crates: Vec, } -/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in -/// all roots. Roots might be nested. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Root { - pub(crate) path: AbsPathBuf, -} - /// A crate points to the root module of a crate and lists the dependencies of the crate. This is /// useful in creating the crate graph. #[derive(Clone, Debug, Eq, PartialEq)] @@ -35,12 +27,13 @@ pub struct Crate { pub(crate) out_dir: Option, pub(crate) proc_macro_dylib_path: Option, pub(crate) is_workspace_member: bool, + pub(crate) include: Vec, + pub(crate) exclude: Vec, } impl ProjectJson { pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { ProjectJson { - roots: data.roots.into_iter().map(|path| Root { path: base.join(path) }).collect(), crates: data .crates .into_iter() @@ -50,8 +43,19 @@ impl ProjectJson { && !crate_data.root_module.starts_with("..") || crate_data.root_module.starts_with(base) }); + let root_module = base.join(crate_data.root_module); + let (include, exclude) = match crate_data.source { + Some(src) => { + let absolutize = |dirs: Vec| { + dirs.into_iter().map(|it| base.join(it)).collect::>() + }; + (absolutize(src.include_dirs), absolutize(src.exclude_dirs)) + } + None => (vec![root_module.parent().unwrap().to_path_buf()], Vec::new()), + }; + Crate { - root_module: base.join(crate_data.root_module), + root_module, edition: crate_data.edition.into(), deps: crate_data .deps @@ -79,6 +83,8 @@ impl ProjectJson { .proc_macro_dylib_path .map(|it| base.join(it)), is_workspace_member, + include, + exclude, } }) .collect::>(), @@ -88,7 +94,6 @@ impl ProjectJson { #[derive(Deserialize)] pub struct ProjectJsonData { - roots: Vec, crates: Vec, } @@ -103,6 +108,7 @@ struct CrateData { out_dir: Option, proc_macro_dylib_path: Option, is_workspace_member: Option, + source: Option, } #[derive(Deserialize)] @@ -132,6 +138,12 @@ struct DepData { name: CrateName, } +#[derive(Deserialize)] +struct CrateSource { + include_dirs: Vec, + exclude_dirs: Vec, +} + fn deserialize_crate_name<'de, D>(de: D) -> Result where D: de::Deserializer<'de>, -- cgit v1.2.3 From b48336bf940ce1b55e72d244ff9f28573f2e5548 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 21 Jul 2020 15:12:12 +0200 Subject: Replace OUT_DIR in project.json with general env OUT_DIR doesn't make sense here, as this is a cargo-specific concept --- crates/ra_project_model/src/lib.rs | 7 ++----- crates/ra_project_model/src/project_json.rs | 9 +++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 05f2e7b7a..2bb156610 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -259,11 +259,8 @@ impl ProjectWorkspace { let file_id = load(&file_path)?; let mut env = Env::default(); - if let Some(out_dir) = &krate.out_dir { - // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() - if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { - env.set("OUT_DIR", out_dir); - } + for (k, v) in &krate.env { + env.set(k, v.clone()); } let proc_macro = krate .proc_macro_dylib_path diff --git a/crates/ra_project_model/src/project_json.rs b/crates/ra_project_model/src/project_json.rs index e0052ac6d..e9a333191 100644 --- a/crates/ra_project_model/src/project_json.rs +++ b/crates/ra_project_model/src/project_json.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use paths::{AbsPath, AbsPathBuf}; use ra_cfg::CfgOptions; use ra_db::{CrateId, CrateName, Dependency, Edition}; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use serde::{de, Deserialize}; use stdx::split_delim; @@ -24,7 +24,7 @@ pub struct Crate { pub(crate) deps: Vec, pub(crate) cfg: CfgOptions, pub(crate) target: Option, - pub(crate) out_dir: Option, + pub(crate) env: FxHashMap, pub(crate) proc_macro_dylib_path: Option, pub(crate) is_workspace_member: bool, pub(crate) include: Vec, @@ -78,7 +78,7 @@ impl ProjectJson { cfg }, target: crate_data.target, - out_dir: crate_data.out_dir.map(|it| base.join(it)), + env: crate_data.env, proc_macro_dylib_path: crate_data .proc_macro_dylib_path .map(|it| base.join(it)), @@ -105,7 +105,8 @@ struct CrateData { #[serde(default)] cfg: FxHashSet, target: Option, - out_dir: Option, + #[serde(default)] + env: FxHashMap, proc_macro_dylib_path: Option, is_workspace_member: Option, source: Option, -- cgit v1.2.3 From ca2a4ccf0578e1bc3ed06f0a7d34708478a8acae Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 21 Jul 2020 15:43:56 +0200 Subject: Document new rust-project.json format --- docs/user/manual.adoc | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 978b463d5..8e95f51e3 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -273,9 +273,6 @@ However, if you use some other build system, you'll have to describe the structu [source,TypeScript] ---- interface JsonProject { - /// The set of paths containing the crates for this project. - /// Any `Crate` must be nested inside some `root`. - roots: string[]; /// The set of crates comprising the current project. /// Must include all transitive dependencies as well as sysroot crate (libstd, libcore and such). crates: Crate[]; @@ -288,11 +285,37 @@ interface Crate { edition: "2015" | "2018"; /// Dependencies deps: Dep[]; + /// Should this crate be treated as a member of current "workspace". + /// + /// By default, inferred from the `root_module` (members are the crates which reside + /// inside the directory opened in the editor). + /// + /// Set this too `false` for things like standard library and 3rd party crates to + /// enable performance optimizations (rust-analyzer assumes that non-member crates + /// don't change). + is_workspace_member?: boolean; + /// Optionally specify the (super)set of `.rs` files comprising this crate. + /// + /// By default, rust-analyzer assumes that only files under `root_module.parent` can belong to a crate. + /// `include_dirs` are included recursively, unless a subdirectory is in `exclude_dirs`. + /// + /// Different crates can share the same `source`. + + /// If two crates share an `.rs` file in common, they *must* have the same `source`. + /// rust-analyzer assumes that files from one source can't refer to files in another source. + source?: { + include_dirs: string[], + exclude_dirs: string[], + }, /// The set of cfgs activated for a given crate, like `["unix", "feature=foo", "feature=bar"]`. cfg: string[]; + /// Target tripple for this Crate. + /// + /// It is use when running `rustc --print cfg` to get target-specific cfgs. + target?: string; + /// Environment variables, used for `env!` macro + env: : { [key: string]: string; }, - /// value of the OUT_DIR env variable. - out_dir?: string; /// For proc-macro crates, path to compiles proc-macro (.so file). proc_macro_dylib_path?: string; } -- cgit v1.2.3 From eb613c74da3b82529ed269817b388a3a37dff1fc Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 21 Jul 2020 17:09:56 +0200 Subject: Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Laurențiu Nicola --- docs/user/manual.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 8e95f51e3..2a58cd85f 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -290,7 +290,7 @@ interface Crate { /// By default, inferred from the `root_module` (members are the crates which reside /// inside the directory opened in the editor). /// - /// Set this too `false` for things like standard library and 3rd party crates to + /// Set this to `false` for things like standard library and 3rd party crates to /// enable performance optimizations (rust-analyzer assumes that non-member crates /// don't change). is_workspace_member?: boolean; @@ -309,11 +309,11 @@ interface Crate { }, /// The set of cfgs activated for a given crate, like `["unix", "feature=foo", "feature=bar"]`. cfg: string[]; - /// Target tripple for this Crate. + /// Target triple for this Crate. /// - /// It is use when running `rustc --print cfg` to get target-specific cfgs. + /// Used when running `rustc --print cfg` to get target-specific cfgs. target?: string; - /// Environment variables, used for `env!` macro + /// Environment variables, used for the `env!` macro env: : { [key: string]: string; }, /// For proc-macro crates, path to compiles proc-macro (.so file). -- cgit v1.2.3 From b68ef1231daf6eb1abeb06a30dc89af1254b833d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 21 Jul 2020 17:17:21 +0200 Subject: More Rustic API for Env --- crates/ra_db/src/fixture.rs | 2 +- crates/ra_db/src/input.rs | 17 ++++------------- crates/ra_ide/src/mock_analysis.rs | 4 ++-- crates/ra_project_model/src/lib.rs | 5 +---- 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index 209713987..2aafb9965 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs @@ -222,7 +222,7 @@ impl From for FileMeta { .edition .as_ref() .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), - env: Env::from(f.env.iter()), + env: f.env.into_iter().collect(), } } } diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index aaa492759..6f2e5cfc7 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs @@ -6,7 +6,7 @@ //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how //! actual IO is done and lowered to input. -use std::{fmt, ops, str::FromStr, sync::Arc}; +use std::{fmt, iter::FromIterator, ops, str::FromStr, sync::Arc}; use ra_cfg::CfgOptions; use ra_syntax::SmolStr; @@ -298,18 +298,9 @@ impl fmt::Display for Edition { } } -impl<'a, T> From for Env -where - T: Iterator, -{ - fn from(iter: T) -> Self { - let mut result = Self::default(); - - for (k, v) in iter { - result.entries.insert(k.to_owned(), v.to_owned()); - } - - result +impl FromIterator<(String, String)> for Env { + fn from_iter>(iter: T) -> Self { + Env { entries: FromIterator::from_iter(iter) } } } diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index b28054688..c7e0f4b58 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use ra_cfg::CfgOptions; -use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath}; +use ra_db::{CrateName, FileSet, SourceRoot, VfsPath}; use test_utils::{ extract_annotations, extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, }; @@ -110,7 +110,7 @@ impl MockAnalysis { data.edition.and_then(|it| it.parse().ok()).unwrap_or(Edition::Edition2018); let file_id = FileId(i as u32 + 1); - let env = Env::from(data.env.iter()); + let env = data.env.into_iter().collect(); if path == "/lib.rs" || path == "/main.rs" { root_crate = Some(crate_graph.add_crate_root( file_id, diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 2bb156610..6da4d7928 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -258,10 +258,7 @@ impl ProjectWorkspace { let file_path = &krate.root_module; let file_id = load(&file_path)?; - let mut env = Env::default(); - for (k, v) in &krate.env { - env.set(k, v.clone()); - } + let env = krate.env.clone().into_iter().collect(); let proc_macro = krate .proc_macro_dylib_path .clone() -- cgit v1.2.3