aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_db/src/fixture.rs2
-rw-r--r--crates/ra_db/src/input.rs17
-rw-r--r--crates/ra_ide/src/mock_analysis.rs4
-rw-r--r--crates/ra_project_model/src/lib.rs87
-rw-r--r--crates/ra_project_model/src/project_json.rs43
-rw-r--r--crates/rust-analyzer/src/reload.rs40
-rw-r--r--crates/vfs/src/loader.rs2
-rw-r--r--docs/user/manual.adoc33
8 files changed, 117 insertions, 111 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<Fixture> for FileMeta {
222 .edition 222 .edition
223 .as_ref() 223 .as_ref()
224 .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), 224 .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()),
225 env: Env::from(f.env.iter()), 225 env: f.env.into_iter().collect(),
226 } 226 }
227 } 227 }
228} 228}
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 @@
6//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how 6//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
7//! actual IO is done and lowered to input. 7//! actual IO is done and lowered to input.
8 8
9use std::{fmt, ops, str::FromStr, sync::Arc}; 9use std::{fmt, iter::FromIterator, ops, str::FromStr, sync::Arc};
10 10
11use ra_cfg::CfgOptions; 11use ra_cfg::CfgOptions;
12use ra_syntax::SmolStr; 12use ra_syntax::SmolStr;
@@ -298,18 +298,9 @@ impl fmt::Display for Edition {
298 } 298 }
299} 299}
300 300
301impl<'a, T> From<T> for Env 301impl FromIterator<(String, String)> for Env {
302where 302 fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
303 T: Iterator<Item = (&'a String, &'a String)>, 303 Env { entries: FromIterator::from_iter(iter) }
304{
305 fn from(iter: T) -> Self {
306 let mut result = Self::default();
307
308 for (k, v) in iter {
309 result.entries.insert(k.to_owned(), v.to_owned());
310 }
311
312 result
313 } 304 }
314} 305}
315 306
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 @@
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use ra_cfg::CfgOptions; 4use ra_cfg::CfgOptions;
5use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath}; 5use ra_db::{CrateName, FileSet, SourceRoot, VfsPath};
6use test_utils::{ 6use test_utils::{
7 extract_annotations, extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, 7 extract_annotations, extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER,
8}; 8};
@@ -110,7 +110,7 @@ impl MockAnalysis {
110 data.edition.and_then(|it| it.parse().ok()).unwrap_or(Edition::Edition2018); 110 data.edition.and_then(|it| it.parse().ok()).unwrap_or(Edition::Edition2018);
111 111
112 let file_id = FileId(i as u32 + 1); 112 let file_id = FileId(i as u32 + 1);
113 let env = Env::from(data.env.iter()); 113 let env = data.env.into_iter().collect();
114 if path == "/lib.rs" || path == "/main.rs" { 114 if path == "/lib.rs" || path == "/main.rs" {
115 root_crate = Some(crate_graph.add_crate_root( 115 root_crate = Some(crate_graph.add_crate_root(
116 file_id, 116 file_id,
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index b9c5424bf..6da4d7928 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -7,7 +7,6 @@ mod sysroot;
7use std::{ 7use std::{
8 fs::{self, read_dir, ReadDir}, 8 fs::{self, read_dir, ReadDir},
9 io, 9 io,
10 path::Path,
11 process::{Command, Output}, 10 process::{Command, Output},
12}; 11};
13 12
@@ -35,30 +34,12 @@ pub enum ProjectWorkspace {
35/// `PackageRoot` describes a package root folder. 34/// `PackageRoot` describes a package root folder.
36/// Which may be an external dependency, or a member of 35/// Which may be an external dependency, or a member of
37/// the current workspace. 36/// the current workspace.
38#[derive(Debug, Clone)] 37#[derive(Debug, Clone, Eq, PartialEq, Hash)]
39pub struct PackageRoot { 38pub struct PackageRoot {
40 /// Path to the root folder
41 path: AbsPathBuf,
42 /// Is a member of the current workspace 39 /// Is a member of the current workspace
43 is_member: bool, 40 pub is_member: bool,
44 out_dir: Option<AbsPathBuf>, 41 pub include: Vec<AbsPathBuf>,
45} 42 pub exclude: Vec<AbsPathBuf>,
46impl PackageRoot {
47 pub fn new_member(path: AbsPathBuf) -> PackageRoot {
48 Self { path, is_member: true, out_dir: None }
49 }
50 pub fn new_non_member(path: AbsPathBuf) -> PackageRoot {
51 Self { path, is_member: false, out_dir: None }
52 }
53 pub fn path(&self) -> &AbsPath {
54 &self.path
55 }
56 pub fn out_dir(&self) -> Option<&AbsPath> {
57 self.out_dir.as_deref()
58 }
59 pub fn is_member(&self) -> bool {
60 self.is_member
61 }
62} 43}
63 44
64#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] 45#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
@@ -195,18 +176,40 @@ impl ProjectWorkspace {
195 /// the root is a member of the current workspace 176 /// the root is a member of the current workspace
196 pub fn to_roots(&self) -> Vec<PackageRoot> { 177 pub fn to_roots(&self) -> Vec<PackageRoot> {
197 match self { 178 match self {
198 ProjectWorkspace::Json { project } => { 179 ProjectWorkspace::Json { project } => project
199 project.roots.iter().map(|r| PackageRoot::new_member(r.path.clone())).collect() 180 .crates
200 } 181 .iter()
182 .map(|krate| PackageRoot {
183 is_member: krate.is_workspace_member,
184 include: krate.include.clone(),
185 exclude: krate.exclude.clone(),
186 })
187 .collect::<FxHashSet<_>>()
188 .into_iter()
189 .collect::<Vec<_>>(),
201 ProjectWorkspace::Cargo { cargo, sysroot } => cargo 190 ProjectWorkspace::Cargo { cargo, sysroot } => cargo
202 .packages() 191 .packages()
203 .map(|pkg| PackageRoot { 192 .map(|pkg| {
204 path: cargo[pkg].root().to_path_buf(), 193 let is_member = cargo[pkg].is_member;
205 is_member: cargo[pkg].is_member, 194 let pkg_root = cargo[pkg].root().to_path_buf();
206 out_dir: cargo[pkg].out_dir.clone(), 195
196 let mut include = vec![pkg_root.clone()];
197 include.extend(cargo[pkg].out_dir.clone());
198
199 let mut exclude = vec![pkg_root.join(".git")];
200 if is_member {
201 exclude.push(pkg_root.join("target"));
202 } else {
203 exclude.push(pkg_root.join("tests"));
204 exclude.push(pkg_root.join("examples"));
205 exclude.push(pkg_root.join("benches"));
206 }
207 PackageRoot { is_member, include, exclude }
207 }) 208 })
208 .chain(sysroot.crates().map(|krate| { 209 .chain(sysroot.crates().map(|krate| PackageRoot {
209 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) 210 is_member: false,
211 include: vec![sysroot[krate].root_dir().to_path_buf()],
212 exclude: Vec::new(),
210 })) 213 }))
211 .collect(), 214 .collect(),
212 } 215 }
@@ -255,13 +258,7 @@ impl ProjectWorkspace {
255 let file_path = &krate.root_module; 258 let file_path = &krate.root_module;
256 let file_id = load(&file_path)?; 259 let file_id = load(&file_path)?;
257 260
258 let mut env = Env::default(); 261 let env = krate.env.clone().into_iter().collect();
259 if let Some(out_dir) = &krate.out_dir {
260 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
261 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
262 env.set("OUT_DIR", out_dir);
263 }
264 }
265 let proc_macro = krate 262 let proc_macro = krate
266 .proc_macro_dylib_path 263 .proc_macro_dylib_path
267 .clone() 264 .clone()
@@ -503,18 +500,6 @@ impl ProjectWorkspace {
503 } 500 }
504 crate_graph 501 crate_graph
505 } 502 }
506
507 pub fn workspace_root_for(&self, path: &Path) -> Option<&AbsPath> {
508 match self {
509 ProjectWorkspace::Cargo { cargo, .. } => {
510 Some(cargo.workspace_root()).filter(|root| path.starts_with(root))
511 }
512 ProjectWorkspace::Json { project: ProjectJson { roots, .. }, .. } => roots
513 .iter()
514 .find(|root| path.starts_with(&root.path))
515 .map(|root| root.path.as_path()),
516 }
517 }
518} 503}
519 504
520fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions { 505fn 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..e9a333191 100644
--- a/crates/ra_project_model/src/project_json.rs
+++ b/crates/ra_project_model/src/project_json.rs
@@ -5,24 +5,16 @@ use std::path::PathBuf;
5use paths::{AbsPath, AbsPathBuf}; 5use paths::{AbsPath, AbsPathBuf};
6use ra_cfg::CfgOptions; 6use ra_cfg::CfgOptions;
7use ra_db::{CrateId, CrateName, Dependency, Edition}; 7use ra_db::{CrateId, CrateName, Dependency, Edition};
8use rustc_hash::FxHashSet; 8use rustc_hash::{FxHashMap, FxHashSet};
9use serde::{de, Deserialize}; 9use serde::{de, Deserialize};
10use stdx::split_delim; 10use stdx::split_delim;
11 11
12/// Roots and crates that compose this Rust project. 12/// Roots and crates that compose this Rust project.
13#[derive(Clone, Debug, Eq, PartialEq)] 13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct ProjectJson { 14pub struct ProjectJson {
15 pub(crate) roots: Vec<Root>,
16 pub(crate) crates: Vec<Crate>, 15 pub(crate) crates: Vec<Crate>,
17} 16}
18 17
19/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in
20/// all roots. Roots might be nested.
21#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct Root {
23 pub(crate) path: AbsPathBuf,
24}
25
26/// A crate points to the root module of a crate and lists the dependencies of the crate. This is 18/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
27/// useful in creating the crate graph. 19/// useful in creating the crate graph.
28#[derive(Clone, Debug, Eq, PartialEq)] 20#[derive(Clone, Debug, Eq, PartialEq)]
@@ -32,15 +24,16 @@ pub struct Crate {
32 pub(crate) deps: Vec<Dependency>, 24 pub(crate) deps: Vec<Dependency>,
33 pub(crate) cfg: CfgOptions, 25 pub(crate) cfg: CfgOptions,
34 pub(crate) target: Option<String>, 26 pub(crate) target: Option<String>,
35 pub(crate) out_dir: Option<AbsPathBuf>, 27 pub(crate) env: FxHashMap<String, String>,
36 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, 28 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
37 pub(crate) is_workspace_member: bool, 29 pub(crate) is_workspace_member: bool,
30 pub(crate) include: Vec<AbsPathBuf>,
31 pub(crate) exclude: Vec<AbsPathBuf>,
38} 32}
39 33
40impl ProjectJson { 34impl ProjectJson {
41 pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { 35 pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson {
42 ProjectJson { 36 ProjectJson {
43 roots: data.roots.into_iter().map(|path| Root { path: base.join(path) }).collect(),
44 crates: data 37 crates: data
45 .crates 38 .crates
46 .into_iter() 39 .into_iter()
@@ -50,8 +43,19 @@ impl ProjectJson {
50 && !crate_data.root_module.starts_with("..") 43 && !crate_data.root_module.starts_with("..")
51 || crate_data.root_module.starts_with(base) 44 || crate_data.root_module.starts_with(base)
52 }); 45 });
46 let root_module = base.join(crate_data.root_module);
47 let (include, exclude) = match crate_data.source {
48 Some(src) => {
49 let absolutize = |dirs: Vec<PathBuf>| {
50 dirs.into_iter().map(|it| base.join(it)).collect::<Vec<_>>()
51 };
52 (absolutize(src.include_dirs), absolutize(src.exclude_dirs))
53 }
54 None => (vec![root_module.parent().unwrap().to_path_buf()], Vec::new()),
55 };
56
53 Crate { 57 Crate {
54 root_module: base.join(crate_data.root_module), 58 root_module,
55 edition: crate_data.edition.into(), 59 edition: crate_data.edition.into(),
56 deps: crate_data 60 deps: crate_data
57 .deps 61 .deps
@@ -74,11 +78,13 @@ impl ProjectJson {
74 cfg 78 cfg
75 }, 79 },
76 target: crate_data.target, 80 target: crate_data.target,
77 out_dir: crate_data.out_dir.map(|it| base.join(it)), 81 env: crate_data.env,
78 proc_macro_dylib_path: crate_data 82 proc_macro_dylib_path: crate_data
79 .proc_macro_dylib_path 83 .proc_macro_dylib_path
80 .map(|it| base.join(it)), 84 .map(|it| base.join(it)),
81 is_workspace_member, 85 is_workspace_member,
86 include,
87 exclude,
82 } 88 }
83 }) 89 })
84 .collect::<Vec<_>>(), 90 .collect::<Vec<_>>(),
@@ -88,7 +94,6 @@ impl ProjectJson {
88 94
89#[derive(Deserialize)] 95#[derive(Deserialize)]
90pub struct ProjectJsonData { 96pub struct ProjectJsonData {
91 roots: Vec<PathBuf>,
92 crates: Vec<CrateData>, 97 crates: Vec<CrateData>,
93} 98}
94 99
@@ -100,9 +105,11 @@ struct CrateData {
100 #[serde(default)] 105 #[serde(default)]
101 cfg: FxHashSet<String>, 106 cfg: FxHashSet<String>,
102 target: Option<String>, 107 target: Option<String>,
103 out_dir: Option<PathBuf>, 108 #[serde(default)]
109 env: FxHashMap<String, String>,
104 proc_macro_dylib_path: Option<PathBuf>, 110 proc_macro_dylib_path: Option<PathBuf>,
105 is_workspace_member: Option<bool>, 111 is_workspace_member: Option<bool>,
112 source: Option<CrateSource>,
106} 113}
107 114
108#[derive(Deserialize)] 115#[derive(Deserialize)]
@@ -132,6 +139,12 @@ struct DepData {
132 name: CrateName, 139 name: CrateName,
133} 140}
134 141
142#[derive(Deserialize)]
143struct CrateSource {
144 include_dirs: Vec<PathBuf>,
145 exclude_dirs: Vec<PathBuf>,
146}
147
135fn deserialize_crate_name<'de, D>(de: D) -> Result<CrateName, D::Error> 148fn deserialize_crate_name<'de, D>(de: D) -> Result<CrateName, D::Error>
136where 149where
137 D: de::Deserializer<'de>, 150 D: de::Deserializer<'de>,
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;
5use ra_db::{CrateGraph, SourceRoot, VfsPath}; 5use ra_db::{CrateGraph, SourceRoot, VfsPath};
6use ra_ide::AnalysisChange; 6use ra_ide::AnalysisChange;
7use ra_prof::profile; 7use ra_prof::profile;
8use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace}; 8use ra_project_model::{ProcMacroClient, ProjectWorkspace};
9use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; 9use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
10 10
11use crate::{ 11use crate::{
@@ -149,8 +149,10 @@ impl GlobalState {
149 watchers: workspaces 149 watchers: workspaces
150 .iter() 150 .iter()
151 .flat_map(ProjectWorkspace::to_roots) 151 .flat_map(ProjectWorkspace::to_roots)
152 .filter(PackageRoot::is_member) 152 .filter(|it| it.is_member)
153 .map(|root| format!("{}/**/*.rs", root.path().display())) 153 .flat_map(|root| {
154 root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display()))
155 })
154 .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None }) 156 .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
155 .collect(), 157 .collect(),
156 }; 158 };
@@ -261,31 +263,23 @@ impl ProjectFolders {
261 let mut local_filesets = vec![]; 263 let mut local_filesets = vec![];
262 264
263 for root in workspaces.iter().flat_map(|it| it.to_roots()) { 265 for root in workspaces.iter().flat_map(|it| it.to_roots()) {
264 let path = root.path().to_owned(); 266 let file_set_roots: Vec<VfsPath> =
265 267 root.include.iter().cloned().map(VfsPath::from).collect();
266 let mut file_set_roots: Vec<VfsPath> = vec![];
267 268
268 let entry = if root.is_member() { 269 let entry = {
269 vfs::loader::Entry::local_cargo_package(path.to_path_buf()) 270 let mut dirs = vfs::loader::Directories::default();
270 } else { 271 dirs.extensions.push("rs".into());
271 vfs::loader::Entry::cargo_package_dependency(path.to_path_buf()) 272 dirs.include.extend(root.include);
273 dirs.exclude.extend(root.exclude);
274 vfs::loader::Entry::Directories(dirs)
272 }; 275 };
273 res.load.push(entry);
274 if root.is_member() {
275 res.watch.push(res.load.len() - 1);
276 }
277 276
278 if let Some(out_dir) = root.out_dir() { 277 if root.is_member {
279 let out_dir = out_dir.to_path_buf(); 278 res.watch.push(res.load.len());
280 res.load.push(vfs::loader::Entry::rs_files_recursively(out_dir.clone()));
281 if root.is_member() {
282 res.watch.push(res.load.len() - 1);
283 }
284 file_set_roots.push(out_dir.into());
285 } 279 }
286 file_set_roots.push(path.to_path_buf().into()); 280 res.load.push(entry);
287 281
288 if root.is_member() { 282 if root.is_member {
289 local_filesets.push(fsc.len()); 283 local_filesets.push(fsc.len());
290 } 284 }
291 fsc.add_file_set(file_set_roots) 285 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 {
17/// * it is not under `exclude` path 17/// * it is not under `exclude` path
18/// 18///
19/// If many include/exclude paths match, the longest one wins. 19/// If many include/exclude paths match, the longest one wins.
20#[derive(Debug, Clone)] 20#[derive(Debug, Clone, Default)]
21pub struct Directories { 21pub struct Directories {
22 pub extensions: Vec<String>, 22 pub extensions: Vec<String>,
23 pub include: Vec<AbsPathBuf>, 23 pub include: Vec<AbsPathBuf>,
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 57251b851..4b31145de 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
273[source,TypeScript] 273[source,TypeScript]
274---- 274----
275interface JsonProject { 275interface JsonProject {
276 /// The set of paths containing the crates for this project.
277 /// Any `Crate` must be nested inside some `root`.
278 roots: string[];
279 /// The set of crates comprising the current project. 276 /// The set of crates comprising the current project.
280 /// Must include all transitive dependencies as well as sysroot crate (libstd, libcore and such). 277 /// Must include all transitive dependencies as well as sysroot crate (libstd, libcore and such).
281 crates: Crate[]; 278 crates: Crate[];
@@ -288,11 +285,37 @@ interface Crate {
288 edition: "2015" | "2018"; 285 edition: "2015" | "2018";
289 /// Dependencies 286 /// Dependencies
290 deps: Dep[]; 287 deps: Dep[];
288 /// Should this crate be treated as a member of current "workspace".
289 ///
290 /// By default, inferred from the `root_module` (members are the crates which reside
291 /// inside the directory opened in the editor).
292 ///
293 /// Set this to `false` for things like standard library and 3rd party crates to
294 /// enable performance optimizations (rust-analyzer assumes that non-member crates
295 /// don't change).
296 is_workspace_member?: boolean;
297 /// Optionally specify the (super)set of `.rs` files comprising this crate.
298 ///
299 /// By default, rust-analyzer assumes that only files under `root_module.parent` can belong to a crate.
300 /// `include_dirs` are included recursively, unless a subdirectory is in `exclude_dirs`.
301 ///
302 /// Different crates can share the same `source`.
303
304 /// If two crates share an `.rs` file in common, they *must* have the same `source`.
305 /// rust-analyzer assumes that files from one source can't refer to files in another source.
306 source?: {
307 include_dirs: string[],
308 exclude_dirs: string[],
309 },
291 /// The set of cfgs activated for a given crate, like `["unix", "feature=foo", "feature=bar"]`. 310 /// The set of cfgs activated for a given crate, like `["unix", "feature=foo", "feature=bar"]`.
292 cfg: string[]; 311 cfg: string[];
312 /// Target triple for this Crate.
313 ///
314 /// Used when running `rustc --print cfg` to get target-specific cfgs.
315 target?: string;
316 /// Environment variables, used for the `env!` macro
317 env: : { [key: string]: string; },
293 318
294 /// value of the OUT_DIR env variable.
295 out_dir?: string;
296 /// For proc-macro crates, path to compiles proc-macro (.so file). 319 /// For proc-macro crates, path to compiles proc-macro (.so file).
297 proc_macro_dylib_path?: string; 320 proc_macro_dylib_path?: string;
298} 321}