aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/project_model/src/build_data.rs288
-rw-r--r--crates/project_model/src/cargo_workspace.rs47
-rw-r--r--crates/project_model/src/lib.rs1
-rw-r--r--crates/project_model/src/workspace.rs53
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs36
-rw-r--r--crates/rust-analyzer/src/config.rs4
-rw-r--r--crates/rust-analyzer/src/global_state.rs12
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs1
-rw-r--r--crates/rust-analyzer/src/main_loop.rs38
-rw-r--r--crates/rust-analyzer/src/op_queue.rs28
-rw-r--r--crates/rust-analyzer/src/reload.rs88
11 files changed, 397 insertions, 199 deletions
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
index 3ff347e2c..a5c564e0a 100644
--- a/crates/project_model/src/build_data.rs
+++ b/crates/project_model/src/build_data.rs
@@ -5,10 +5,11 @@ use std::{
5 io::BufReader, 5 io::BufReader,
6 path::{Path, PathBuf}, 6 path::{Path, PathBuf},
7 process::{Command, Stdio}, 7 process::{Command, Stdio},
8 sync::Arc,
8}; 9};
9 10
10use anyhow::Result; 11use anyhow::Result;
11use cargo_metadata::{BuildScript, Message, Package, PackageId}; 12use cargo_metadata::{BuildScript, Message};
12use itertools::Itertools; 13use itertools::Itertools;
13use paths::{AbsPath, AbsPathBuf}; 14use paths::{AbsPath, AbsPathBuf};
14use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
@@ -16,150 +17,195 @@ use stdx::JodChild;
16 17
17use crate::{cfg_flag::CfgFlag, CargoConfig}; 18use crate::{cfg_flag::CfgFlag, CargoConfig};
18 19
19#[derive(Debug, Clone, Default)]
20pub(crate) struct BuildDataMap {
21 data: FxHashMap<PackageId, BuildData>,
22}
23#[derive(Debug, Clone, Default, PartialEq, Eq)] 20#[derive(Debug, Clone, Default, PartialEq, Eq)]
24pub struct BuildData { 21pub(crate) struct BuildData {
25 /// List of config flags defined by this package's build script 22 /// List of config flags defined by this package's build script
26 pub cfgs: Vec<CfgFlag>, 23 pub(crate) cfgs: Vec<CfgFlag>,
27 /// List of cargo-related environment variables with their value 24 /// List of cargo-related environment variables with their value
28 /// 25 ///
29 /// If the package has a build script which defines environment variables, 26 /// If the package has a build script which defines environment variables,
30 /// they can also be found here. 27 /// they can also be found here.
31 pub envs: Vec<(String, String)>, 28 pub(crate) envs: Vec<(String, String)>,
32 /// Directory where a build script might place its output 29 /// Directory where a build script might place its output
33 pub out_dir: Option<AbsPathBuf>, 30 pub(crate) out_dir: Option<AbsPathBuf>,
34 /// Path to the proc-macro library file if this package exposes proc-macros 31 /// Path to the proc-macro library file if this package exposes proc-macros
35 pub proc_macro_dylib_path: Option<AbsPathBuf>, 32 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
36} 33}
37 34
38impl BuildDataMap { 35#[derive(Clone, Debug)]
39 pub(crate) fn new( 36pub(crate) struct BuildDataConfig {
40 cargo_toml: &AbsPath, 37 cargo_toml: AbsPathBuf,
41 cargo_features: &CargoConfig, 38 cargo_features: CargoConfig,
42 packages: &Vec<Package>, 39 packages: Arc<Vec<cargo_metadata::Package>>,
43 progress: &dyn Fn(String), 40}
44 ) -> Result<BuildDataMap> {
45 let mut cmd = Command::new(toolchain::cargo());
46 cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
47 .arg(cargo_toml.as_ref());
48
49 // --all-targets includes tests, benches and examples in addition to the
50 // default lib and bins. This is an independent concept from the --targets
51 // flag below.
52 cmd.arg("--all-targets");
53
54 if let Some(target) = &cargo_features.target {
55 cmd.args(&["--target", target]);
56 }
57 41
58 if cargo_features.all_features { 42impl PartialEq for BuildDataConfig {
59 cmd.arg("--all-features"); 43 fn eq(&self, other: &Self) -> bool {
60 } else { 44 Arc::ptr_eq(&self.packages, &other.packages)
61 if cargo_features.no_default_features { 45 }
62 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` 46}
63 // https://github.com/oli-obk/cargo_metadata/issues/79
64 cmd.arg("--no-default-features");
65 }
66 if !cargo_features.features.is_empty() {
67 cmd.arg("--features");
68 cmd.arg(cargo_features.features.join(" "));
69 }
70 }
71 47
72 cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); 48impl Eq for BuildDataConfig {}
73
74 let mut child = cmd.spawn().map(JodChild)?;
75 let child_stdout = child.stdout.take().unwrap();
76 let stdout = BufReader::new(child_stdout);
77
78 let mut res = BuildDataMap::default();
79 for message in cargo_metadata::Message::parse_stream(stdout) {
80 if let Ok(message) = message {
81 match message {
82 Message::BuildScriptExecuted(BuildScript {
83 package_id,
84 out_dir,
85 cfgs,
86 env,
87 ..
88 }) => {
89 let cfgs = {
90 let mut acc = Vec::new();
91 for cfg in cfgs {
92 match cfg.parse::<CfgFlag>() {
93 Ok(it) => acc.push(it),
94 Err(err) => {
95 anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
96 }
97 };
98 }
99 acc
100 };
101 let res = res.data.entry(package_id.clone()).or_default();
102 // cargo_metadata crate returns default (empty) path for
103 // older cargos, which is not absolute, so work around that.
104 if out_dir != PathBuf::default() {
105 let out_dir = AbsPathBuf::assert(out_dir);
106 res.out_dir = Some(out_dir);
107 res.cfgs = cfgs;
108 }
109 49
110 res.envs = env; 50#[derive(Debug, Default)]
111 } 51pub struct BuildDataCollector {
112 Message::CompilerArtifact(message) => { 52 configs: FxHashMap<AbsPathBuf, BuildDataConfig>,
113 progress(format!("metadata {}", message.target.name)); 53}
114 54
115 if message.target.kind.contains(&"proc-macro".to_string()) { 55#[derive(Debug, Default, PartialEq, Eq)]
116 let package_id = message.package_id; 56pub struct BuildDataResult {
117 // Skip rmeta file 57 data: FxHashMap<AbsPathBuf, BuildDataMap>,
118 if let Some(filename) = 58}
119 message.filenames.iter().find(|name| is_dylib(name)) 59
120 { 60pub(crate) type BuildDataMap = FxHashMap<String, BuildData>;
121 let filename = AbsPathBuf::assert(filename.clone()); 61
122 let res = res.data.entry(package_id.clone()).or_default(); 62impl BuildDataCollector {
123 res.proc_macro_dylib_path = Some(filename); 63 pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) {
124 } 64 self.configs.insert(workspace_root.to_path_buf().clone(), config);
125 } 65 }
126 } 66
127 Message::CompilerMessage(message) => { 67 pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> {
128 progress(message.target.name.clone()); 68 let mut res = BuildDataResult::default();
129 } 69 for (path, config) in self.configs.iter() {
130 Message::Unknown => (), 70 res.data.insert(
131 Message::BuildFinished(_) => {} 71 path.clone(),
132 Message::TextLine(_) => {} 72 collect_from_workspace(
133 } 73 &config.cargo_toml,
134 } 74 &config.cargo_features,
75 &config.packages,
76 progress,
77 )?,
78 );
135 } 79 }
136 res.inject_cargo_env(packages);
137 Ok(res) 80 Ok(res)
138 } 81 }
82}
83
84impl BuildDataResult {
85 pub(crate) fn get(&self, root: &AbsPath) -> Option<&BuildDataMap> {
86 self.data.get(&root.to_path_buf())
87 }
88}
139 89
140 pub(crate) fn with_cargo_env(packages: &Vec<Package>) -> Self { 90impl BuildDataConfig {
141 let mut res = Self::default(); 91 pub(crate) fn new(
142 res.inject_cargo_env(packages); 92 cargo_toml: AbsPathBuf,
143 res 93 cargo_features: CargoConfig,
94 packages: Arc<Vec<cargo_metadata::Package>>,
95 ) -> Self {
96 Self { cargo_toml, cargo_features, packages }
144 } 97 }
98}
145 99
146 pub(crate) fn get(&self, id: &PackageId) -> Option<&BuildData> { 100fn collect_from_workspace(
147 self.data.get(id) 101 cargo_toml: &AbsPath,
102 cargo_features: &CargoConfig,
103 packages: &Vec<cargo_metadata::Package>,
104 progress: &dyn Fn(String),
105) -> Result<BuildDataMap> {
106 let mut cmd = Command::new(toolchain::cargo());
107 cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
108 .arg(cargo_toml.as_ref());
109
110 // --all-targets includes tests, benches and examples in addition to the
111 // default lib and bins. This is an independent concept from the --targets
112 // flag below.
113 cmd.arg("--all-targets");
114
115 if let Some(target) = &cargo_features.target {
116 cmd.args(&["--target", target]);
148 } 117 }
149 118
150 fn inject_cargo_env(&mut self, packages: &Vec<Package>) { 119 if cargo_features.all_features {
151 for meta_pkg in packages { 120 cmd.arg("--all-features");
152 let resource = self.data.entry(meta_pkg.id.clone()).or_default(); 121 } else {
153 inject_cargo_env(meta_pkg, &mut resource.envs); 122 if cargo_features.no_default_features {
123 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
124 // https://github.com/oli-obk/cargo_metadata/issues/79
125 cmd.arg("--no-default-features");
126 }
127 if !cargo_features.features.is_empty() {
128 cmd.arg("--features");
129 cmd.arg(cargo_features.features.join(" "));
130 }
131 }
132
133 cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
154 134
155 if let Some(out_dir) = &resource.out_dir { 135 let mut child = cmd.spawn().map(JodChild)?;
156 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() 136 let child_stdout = child.stdout.take().unwrap();
157 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { 137 let stdout = BufReader::new(child_stdout);
158 resource.envs.push(("OUT_DIR".to_string(), out_dir)); 138
139 let mut res = BuildDataMap::default();
140 for message in cargo_metadata::Message::parse_stream(stdout) {
141 if let Ok(message) = message {
142 match message {
143 Message::BuildScriptExecuted(BuildScript {
144 package_id,
145 out_dir,
146 cfgs,
147 env,
148 ..
149 }) => {
150 let cfgs = {
151 let mut acc = Vec::new();
152 for cfg in cfgs {
153 match cfg.parse::<CfgFlag>() {
154 Ok(it) => acc.push(it),
155 Err(err) => {
156 anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
157 }
158 };
159 }
160 acc
161 };
162 let res = res.entry(package_id.repr.clone()).or_default();
163 // cargo_metadata crate returns default (empty) path for
164 // older cargos, which is not absolute, so work around that.
165 if out_dir != PathBuf::default() {
166 let out_dir = AbsPathBuf::assert(out_dir);
167 res.out_dir = Some(out_dir);
168 res.cfgs = cfgs;
169 }
170
171 res.envs = env;
172 }
173 Message::CompilerArtifact(message) => {
174 progress(format!("metadata {}", message.target.name));
175
176 if message.target.kind.contains(&"proc-macro".to_string()) {
177 let package_id = message.package_id;
178 // Skip rmeta file
179 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
180 {
181 let filename = AbsPathBuf::assert(filename.clone());
182 let res = res.entry(package_id.repr.clone()).or_default();
183 res.proc_macro_dylib_path = Some(filename);
184 }
185 }
186 }
187 Message::CompilerMessage(message) => {
188 progress(message.target.name.clone());
159 } 189 }
190 Message::Unknown => (),
191 Message::BuildFinished(_) => {}
192 Message::TextLine(_) => {}
160 } 193 }
161 } 194 }
162 } 195 }
196
197 for package in packages {
198 let build_data = res.entry(package.id.repr.clone()).or_default();
199 inject_cargo_env(package, build_data);
200 if let Some(out_dir) = &build_data.out_dir {
201 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
202 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
203 build_data.envs.push(("OUT_DIR".to_string(), out_dir));
204 }
205 }
206 }
207
208 Ok(res)
163} 209}
164 210
165// FIXME: File a better way to know if it is a dylib 211// FIXME: File a better way to know if it is a dylib
@@ -173,7 +219,9 @@ fn is_dylib(path: &Path) -> bool {
173/// Recreates the compile-time environment variables that Cargo sets. 219/// Recreates the compile-time environment variables that Cargo sets.
174/// 220///
175/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> 221/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
176fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) { 222fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut BuildData) {
223 let env = &mut build_data.envs;
224
177 // FIXME: Missing variables: 225 // FIXME: Missing variables:
178 // CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name> 226 // CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
179 227
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index c8a5333c4..f47898b9b 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{convert::TryInto, ops, process::Command}; 3use std::{convert::TryInto, ops, process::Command, sync::Arc};
4 4
5use anyhow::{Context, Result}; 5use anyhow::{Context, Result};
6use base_db::Edition; 6use base_db::Edition;
@@ -9,7 +9,7 @@ use la_arena::{Arena, Idx};
9use paths::{AbsPath, AbsPathBuf}; 9use paths::{AbsPath, AbsPathBuf};
10use rustc_hash::FxHashMap; 10use rustc_hash::FxHashMap;
11 11
12use crate::build_data::{BuildData, BuildDataMap}; 12use crate::build_data::BuildDataConfig;
13use crate::utf8_stdout; 13use crate::utf8_stdout;
14 14
15/// `CargoWorkspace` represents the logical structure of, well, a Cargo 15/// `CargoWorkspace` represents the logical structure of, well, a Cargo
@@ -27,6 +27,7 @@ pub struct CargoWorkspace {
27 packages: Arena<PackageData>, 27 packages: Arena<PackageData>,
28 targets: Arena<TargetData>, 28 targets: Arena<TargetData>,
29 workspace_root: AbsPathBuf, 29 workspace_root: AbsPathBuf,
30 build_data_config: BuildDataConfig,
30} 31}
31 32
32impl ops::Index<Package> for CargoWorkspace { 33impl ops::Index<Package> for CargoWorkspace {
@@ -55,9 +56,6 @@ pub struct CargoConfig {
55 /// This will be ignored if `cargo_all_features` is true. 56 /// This will be ignored if `cargo_all_features` is true.
56 pub features: Vec<String>, 57 pub features: Vec<String>,
57 58
58 /// Runs cargo check on launch to figure out the correct values of OUT_DIR
59 pub load_out_dirs_from_check: bool,
60
61 /// rustc target 59 /// rustc target
62 pub target: Option<String>, 60 pub target: Option<String>,
63 61
@@ -94,8 +92,8 @@ pub struct PackageData {
94 pub features: FxHashMap<String, Vec<String>>, 92 pub features: FxHashMap<String, Vec<String>>,
95 /// List of features enabled on this package 93 /// List of features enabled on this package
96 pub active_features: Vec<String>, 94 pub active_features: Vec<String>,
97 /// Build script related data for this package 95 // String representation of package id
98 pub build_data: BuildData, 96 pub id: String,
99} 97}
100 98
101#[derive(Debug, Clone, Eq, PartialEq)] 99#[derive(Debug, Clone, Eq, PartialEq)]
@@ -228,12 +226,6 @@ impl CargoWorkspace {
228 ) 226 )
229 })?; 227 })?;
230 228
231 let resources = if config.load_out_dirs_from_check {
232 BuildDataMap::new(cargo_toml, config, &meta.packages, progress)?
233 } else {
234 BuildDataMap::with_cargo_env(&meta.packages)
235 };
236
237 let mut pkg_by_id = FxHashMap::default(); 229 let mut pkg_by_id = FxHashMap::default();
238 let mut packages = Arena::default(); 230 let mut packages = Arena::default();
239 let mut targets = Arena::default(); 231 let mut targets = Arena::default();
@@ -241,10 +233,7 @@ impl CargoWorkspace {
241 let ws_members = &meta.workspace_members; 233 let ws_members = &meta.workspace_members;
242 234
243 meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); 235 meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
244 for meta_pkg in meta.packages { 236 for meta_pkg in &meta.packages {
245 let id = meta_pkg.id.clone();
246 let build_data = resources.get(&id).cloned().unwrap_or_default();
247
248 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = 237 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
249 meta_pkg; 238 meta_pkg;
250 let is_member = ws_members.contains(&id); 239 let is_member = ws_members.contains(&id);
@@ -252,24 +241,24 @@ impl CargoWorkspace {
252 .parse::<Edition>() 241 .parse::<Edition>()
253 .with_context(|| format!("Failed to parse edition {}", edition))?; 242 .with_context(|| format!("Failed to parse edition {}", edition))?;
254 let pkg = packages.alloc(PackageData { 243 let pkg = packages.alloc(PackageData {
255 name, 244 id: id.repr.clone(),
245 name: name.clone(),
256 version: version.to_string(), 246 version: version.to_string(),
257 manifest: AbsPathBuf::assert(manifest_path), 247 manifest: AbsPathBuf::assert(manifest_path.clone()),
258 targets: Vec::new(), 248 targets: Vec::new(),
259 is_member, 249 is_member,
260 edition, 250 edition,
261 dependencies: Vec::new(), 251 dependencies: Vec::new(),
262 features: meta_pkg.features.into_iter().collect(), 252 features: meta_pkg.features.clone().into_iter().collect(),
263 active_features: Vec::new(), 253 active_features: Vec::new(),
264 build_data,
265 }); 254 });
266 let pkg_data = &mut packages[pkg]; 255 let pkg_data = &mut packages[pkg];
267 pkg_by_id.insert(id, pkg); 256 pkg_by_id.insert(id, pkg);
268 for meta_tgt in meta_pkg.targets { 257 for meta_tgt in &meta_pkg.targets {
269 let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"]; 258 let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"];
270 let tgt = targets.alloc(TargetData { 259 let tgt = targets.alloc(TargetData {
271 package: pkg, 260 package: pkg,
272 name: meta_tgt.name, 261 name: meta_tgt.name.clone(),
273 root: AbsPathBuf::assert(meta_tgt.src_path.clone()), 262 root: AbsPathBuf::assert(meta_tgt.src_path.clone()),
274 kind: TargetKind::new(meta_tgt.kind.as_slice()), 263 kind: TargetKind::new(meta_tgt.kind.as_slice()),
275 is_proc_macro, 264 is_proc_macro,
@@ -308,7 +297,13 @@ impl CargoWorkspace {
308 } 297 }
309 298
310 let workspace_root = AbsPathBuf::assert(meta.workspace_root); 299 let workspace_root = AbsPathBuf::assert(meta.workspace_root);
311 Ok(CargoWorkspace { packages, targets, workspace_root: workspace_root }) 300 let build_data_config = BuildDataConfig::new(
301 cargo_toml.to_path_buf(),
302 config.clone(),
303 Arc::new(meta.packages.clone()),
304 );
305
306 Ok(CargoWorkspace { packages, targets, workspace_root, build_data_config })
312 } 307 }
313 308
314 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a { 309 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
@@ -334,6 +329,10 @@ impl CargoWorkspace {
334 } 329 }
335 } 330 }
336 331
332 pub(crate) fn build_data_config(&self) -> &BuildDataConfig {
333 &self.build_data_config
334 }
335
337 fn is_unique(&self, name: &str) -> bool { 336 fn is_unique(&self, name: &str) -> bool {
338 self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 337 self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
339 } 338 }
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index 525c336e6..d712095a6 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -19,6 +19,7 @@ use paths::{AbsPath, AbsPathBuf};
19use rustc_hash::FxHashSet; 19use rustc_hash::FxHashSet;
20 20
21pub use crate::{ 21pub use crate::{
22 build_data::{BuildDataCollector, BuildDataResult},
22 cargo_workspace::{ 23 cargo_workspace::{
23 CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, Target, TargetData, 24 CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, Target, TargetData,
24 TargetKind, 25 TargetKind,
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 559f4e7bf..c30861976 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -16,8 +16,13 @@ use proc_macro_api::ProcMacroClient;
16use rustc_hash::{FxHashMap, FxHashSet}; 16use rustc_hash::{FxHashMap, FxHashSet};
17 17
18use crate::{ 18use crate::{
19 cargo_workspace, cfg_flag::CfgFlag, rustc_cfg, sysroot::SysrootCrate, utf8_stdout, CargoConfig, 19 build_data::{BuildData, BuildDataMap, BuildDataResult},
20 CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind, 20 cargo_workspace,
21 cfg_flag::CfgFlag,
22 rustc_cfg,
23 sysroot::SysrootCrate,
24 utf8_stdout, BuildDataCollector, CargoConfig, CargoWorkspace, ProjectJson, ProjectManifest,
25 Sysroot, TargetKind,
21}; 26};
22 27
23/// `PackageRoot` describes a package root folder. 28/// `PackageRoot` describes a package root folder.
@@ -153,7 +158,7 @@ impl ProjectWorkspace {
153 /// Returns the roots for the current `ProjectWorkspace` 158 /// Returns the roots for the current `ProjectWorkspace`
154 /// The return type contains the path and whether or not 159 /// The return type contains the path and whether or not
155 /// the root is a member of the current workspace 160 /// the root is a member of the current workspace
156 pub fn to_roots(&self) -> Vec<PackageRoot> { 161 pub fn to_roots(&self, build_data: Option<&BuildDataResult>) -> Vec<PackageRoot> {
157 match self { 162 match self {
158 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project 163 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
159 .crates() 164 .crates()
@@ -179,7 +184,12 @@ impl ProjectWorkspace {
179 let pkg_root = cargo[pkg].root().to_path_buf(); 184 let pkg_root = cargo[pkg].root().to_path_buf();
180 185
181 let mut include = vec![pkg_root.clone()]; 186 let mut include = vec![pkg_root.clone()];
182 include.extend(cargo[pkg].build_data.out_dir.clone()); 187 include.extend(
188 build_data
189 .and_then(|it| it.get(cargo.workspace_root()))
190 .and_then(|map| map.get(&cargo[pkg].id))
191 .and_then(|it| it.out_dir.clone()),
192 );
183 193
184 let mut exclude = vec![pkg_root.join(".git")]; 194 let mut exclude = vec![pkg_root.join(".git")];
185 if is_member { 195 if is_member {
@@ -219,6 +229,7 @@ impl ProjectWorkspace {
219 229
220 pub fn to_crate_graph( 230 pub fn to_crate_graph(
221 &self, 231 &self,
232 build_data: Option<&BuildDataResult>,
222 proc_macro_client: Option<&ProcMacroClient>, 233 proc_macro_client: Option<&ProcMacroClient>,
223 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 234 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
224 ) -> CrateGraph { 235 ) -> CrateGraph {
@@ -241,8 +252,10 @@ impl ProjectWorkspace {
241 &proc_macro_loader, 252 &proc_macro_loader,
242 load, 253 load,
243 cargo, 254 cargo,
255 build_data.and_then(|it| it.get(cargo.workspace_root())),
244 sysroot, 256 sysroot,
245 rustc, 257 rustc,
258 rustc.as_ref().zip(build_data).and_then(|(it, map)| map.get(it.workspace_root())),
246 ), 259 ),
247 }; 260 };
248 if crate_graph.patch_cfg_if() { 261 if crate_graph.patch_cfg_if() {
@@ -252,6 +265,18 @@ impl ProjectWorkspace {
252 } 265 }
253 crate_graph 266 crate_graph
254 } 267 }
268
269 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) {
270 match self {
271 ProjectWorkspace::Cargo { cargo, rustc, .. } => {
272 collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone());
273 if let Some(rustc) = rustc {
274 collector.add_config(rustc.workspace_root(), rustc.build_data_config().clone());
275 }
276 }
277 _ => {}
278 }
279 }
255} 280}
256 281
257fn project_json_to_crate_graph( 282fn project_json_to_crate_graph(
@@ -324,8 +349,10 @@ fn cargo_to_crate_graph(
324 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 349 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
325 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 350 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
326 cargo: &CargoWorkspace, 351 cargo: &CargoWorkspace,
352 build_data_map: Option<&BuildDataMap>,
327 sysroot: &Sysroot, 353 sysroot: &Sysroot,
328 rustc: &Option<CargoWorkspace>, 354 rustc: &Option<CargoWorkspace>,
355 rustc_build_data_map: Option<&BuildDataMap>,
329) -> CrateGraph { 356) -> CrateGraph {
330 let _p = profile::span("cargo_to_crate_graph"); 357 let _p = profile::span("cargo_to_crate_graph");
331 let mut crate_graph = CrateGraph::default(); 358 let mut crate_graph = CrateGraph::default();
@@ -351,6 +378,7 @@ fn cargo_to_crate_graph(
351 let crate_id = add_target_crate_root( 378 let crate_id = add_target_crate_root(
352 &mut crate_graph, 379 &mut crate_graph,
353 &cargo[pkg], 380 &cargo[pkg],
381 build_data_map.and_then(|it| it.get(&cargo[pkg].id)),
354 &cfg_options, 382 &cfg_options,
355 proc_macro_loader, 383 proc_macro_loader,
356 file_id, 384 file_id,
@@ -427,6 +455,7 @@ fn cargo_to_crate_graph(
427 let crate_id = add_target_crate_root( 455 let crate_id = add_target_crate_root(
428 &mut crate_graph, 456 &mut crate_graph,
429 &rustc_workspace[pkg], 457 &rustc_workspace[pkg],
458 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)),
430 &cfg_options, 459 &cfg_options,
431 proc_macro_loader, 460 proc_macro_loader,
432 file_id, 461 file_id,
@@ -475,6 +504,7 @@ fn cargo_to_crate_graph(
475fn add_target_crate_root( 504fn add_target_crate_root(
476 crate_graph: &mut CrateGraph, 505 crate_graph: &mut CrateGraph,
477 pkg: &cargo_workspace::PackageData, 506 pkg: &cargo_workspace::PackageData,
507 build_data: Option<&BuildData>,
478 cfg_options: &CfgOptions, 508 cfg_options: &CfgOptions,
479 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 509 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
480 file_id: FileId, 510 file_id: FileId,
@@ -485,19 +515,22 @@ fn add_target_crate_root(
485 for feature in pkg.active_features.iter() { 515 for feature in pkg.active_features.iter() {
486 opts.insert_key_value("feature".into(), feature.into()); 516 opts.insert_key_value("feature".into(), feature.into());
487 } 517 }
488 opts.extend(pkg.build_data.cfgs.iter().cloned()); 518 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
519 opts.extend(cfgs.iter().cloned());
520 }
489 opts 521 opts
490 }; 522 };
491 523
492 let mut env = Env::default(); 524 let mut env = Env::default();
493 for (k, v) in &pkg.build_data.envs { 525 if let Some(envs) = build_data.map(|it| &it.envs) {
494 env.set(k, v.clone()); 526 for (k, v) in envs {
527 env.set(k, v.clone());
528 }
495 } 529 }
496 530
497 let proc_macro = pkg 531 let proc_macro = build_data
498 .build_data
499 .proc_macro_dylib_path
500 .as_ref() 532 .as_ref()
533 .and_then(|it| it.proc_macro_dylib_path.as_ref())
501 .map(|it| proc_macro_loader(&it)) 534 .map(|it| proc_macro_loader(&it))
502 .unwrap_or_default(); 535 .unwrap_or_default();
503 536
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index dbab4f5f4..e12e87180 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -6,7 +6,9 @@ use anyhow::Result;
6use crossbeam_channel::{unbounded, Receiver}; 6use crossbeam_channel::{unbounded, Receiver};
7use ide::{AnalysisHost, Change}; 7use ide::{AnalysisHost, Change};
8use ide_db::base_db::CrateGraph; 8use ide_db::base_db::CrateGraph;
9use project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace}; 9use project_model::{
10 BuildDataCollector, CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace,
11};
10use vfs::{loader::Handle, AbsPath, AbsPathBuf}; 12use vfs::{loader::Handle, AbsPath, AbsPathBuf};
11 13
12use crate::reload::{ProjectFolders, SourceRootConfig}; 14use crate::reload::{ProjectFolders, SourceRootConfig};
@@ -18,11 +20,7 @@ pub fn load_cargo(
18) -> Result<(AnalysisHost, vfs::Vfs)> { 20) -> Result<(AnalysisHost, vfs::Vfs)> {
19 let root = AbsPathBuf::assert(std::env::current_dir()?.join(root)); 21 let root = AbsPathBuf::assert(std::env::current_dir()?.join(root));
20 let root = ProjectManifest::discover_single(&root)?; 22 let root = ProjectManifest::discover_single(&root)?;
21 let ws = ProjectWorkspace::load( 23 let ws = ProjectWorkspace::load(root, &CargoConfig::default(), &|_| {})?;
22 root,
23 &CargoConfig { load_out_dirs_from_check, ..Default::default() },
24 &|_| {},
25 )?;
26 24
27 let (sender, receiver) = unbounded(); 25 let (sender, receiver) = unbounded();
28 let mut vfs = vfs::Vfs::default(); 26 let mut vfs = vfs::Vfs::default();
@@ -39,14 +37,26 @@ pub fn load_cargo(
39 None 37 None
40 }; 38 };
41 39
42 let crate_graph = ws.to_crate_graph(proc_macro_client.as_ref(), &mut |path: &AbsPath| { 40 let build_data = if load_out_dirs_from_check {
43 let contents = loader.load_sync(path); 41 let mut collector = BuildDataCollector::default();
44 let path = vfs::VfsPath::from(path.to_path_buf()); 42 ws.collect_build_data_configs(&mut collector);
45 vfs.set_file_contents(path.clone(), contents); 43 Some(collector.collect(&|_| {})?)
46 vfs.file_id(&path) 44 } else {
47 }); 45 None
46 };
47
48 let crate_graph = ws.to_crate_graph(
49 build_data.as_ref(),
50 proc_macro_client.as_ref(),
51 &mut |path: &AbsPath| {
52 let contents = loader.load_sync(path);
53 let path = vfs::VfsPath::from(path.to_path_buf());
54 vfs.set_file_contents(path.clone(), contents);
55 vfs.file_id(&path)
56 },
57 );
48 58
49 let project_folders = ProjectFolders::new(&[ws], &[]); 59 let project_folders = ProjectFolders::new(&[ws], &[], build_data.as_ref());
50 loader.set_config(vfs::loader::Config { load: project_folders.load, watch: vec![] }); 60 loader.set_config(vfs::loader::Config { load: project_folders.load, watch: vec![] });
51 61
52 log::debug!("crate graph: {:?}", crate_graph); 62 log::debug!("crate graph: {:?}", crate_graph);
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 37487b6ac..cc0b22bff 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -469,6 +469,9 @@ impl Config {
469 pub fn cargo_autoreload(&self) -> bool { 469 pub fn cargo_autoreload(&self) -> bool {
470 self.data.cargo_autoreload 470 self.data.cargo_autoreload
471 } 471 }
472 pub fn load_out_dirs_from_check(&self) -> bool {
473 self.data.cargo_loadOutDirsFromCheck
474 }
472 pub fn cargo(&self) -> CargoConfig { 475 pub fn cargo(&self) -> CargoConfig {
473 let rustc_source = self.data.rustcSource.as_ref().map(|it| self.root_path.join(&it)); 476 let rustc_source = self.data.rustcSource.as_ref().map(|it| self.root_path.join(&it));
474 477
@@ -476,7 +479,6 @@ impl Config {
476 no_default_features: self.data.cargo_noDefaultFeatures, 479 no_default_features: self.data.cargo_noDefaultFeatures,
477 all_features: self.data.cargo_allFeatures, 480 all_features: self.data.cargo_allFeatures,
478 features: self.data.cargo_features.clone(), 481 features: self.data.cargo_features.clone(),
479 load_out_dirs_from_check: self.data.cargo_loadOutDirsFromCheck,
480 target: self.data.cargo_target.clone(), 482 target: self.data.cargo_target.clone(),
481 rustc_source, 483 rustc_source,
482 no_sysroot: self.data.cargo_noSysroot, 484 no_sysroot: self.data.cargo_noSysroot,
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 442fbd14c..6374a9f3c 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -11,7 +11,9 @@ use ide::{Analysis, AnalysisHost, Change, FileId};
11use ide_db::base_db::{CrateId, VfsPath}; 11use ide_db::base_db::{CrateId, VfsPath};
12use lsp_types::{SemanticTokens, Url}; 12use lsp_types::{SemanticTokens, Url};
13use parking_lot::{Mutex, RwLock}; 13use parking_lot::{Mutex, RwLock};
14use project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; 14use project_model::{
15 BuildDataCollector, BuildDataResult, CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target,
16};
15use rustc_hash::FxHashMap; 17use rustc_hash::FxHashMap;
16use vfs::AnchoredPathBuf; 18use vfs::AnchoredPathBuf;
17 19
@@ -33,7 +35,7 @@ use crate::{
33#[derive(Eq, PartialEq, Copy, Clone)] 35#[derive(Eq, PartialEq, Copy, Clone)]
34pub(crate) enum Status { 36pub(crate) enum Status {
35 Loading, 37 Loading,
36 Ready, 38 Ready { partial: bool },
37 Invalid, 39 Invalid,
38 NeedsReload, 40 NeedsReload,
39} 41}
@@ -79,7 +81,9 @@ pub(crate) struct GlobalState {
79 pub(crate) source_root_config: SourceRootConfig, 81 pub(crate) source_root_config: SourceRootConfig,
80 pub(crate) proc_macro_client: Option<ProcMacroClient>, 82 pub(crate) proc_macro_client: Option<ProcMacroClient>,
81 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, 83 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
82 pub(crate) fetch_workspaces_queue: OpQueue, 84 pub(crate) fetch_workspaces_queue: OpQueue<()>,
85 pub(crate) workspace_build_data: Option<BuildDataResult>,
86 pub(crate) fetch_build_data_queue: OpQueue<BuildDataCollector>,
83 latest_requests: Arc<RwLock<LatestRequests>>, 87 latest_requests: Arc<RwLock<LatestRequests>>,
84} 88}
85 89
@@ -133,6 +137,8 @@ impl GlobalState {
133 proc_macro_client: None, 137 proc_macro_client: None,
134 workspaces: Arc::new(Vec::new()), 138 workspaces: Arc::new(Vec::new()),
135 fetch_workspaces_queue: OpQueue::default(), 139 fetch_workspaces_queue: OpQueue::default(),
140 workspace_build_data: None,
141 fetch_build_data_queue: OpQueue::default(),
136 latest_requests: Default::default(), 142 latest_requests: Default::default(),
137 } 143 }
138 } 144 }
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index a85978737..670ca9a45 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -234,6 +234,7 @@ pub enum StatusNotification {}
234#[derive(Serialize, Deserialize)] 234#[derive(Serialize, Deserialize)]
235pub enum Status { 235pub enum Status {
236 Loading, 236 Loading,
237 ReadyPartial,
237 Ready, 238 Ready,
238 NeedsReload, 239 NeedsReload,
239 Invalid, 240 Invalid,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 6d2475a59..f4fd1ac13 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -21,7 +21,7 @@ use crate::{
21 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, 21 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status},
22 handlers, lsp_ext, 22 handlers, lsp_ext,
23 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, 23 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
24 reload::ProjectWorkspaceProgress, 24 reload::{BuildDataProgress, ProjectWorkspaceProgress},
25 Result, 25 Result,
26}; 26};
27 27
@@ -63,6 +63,7 @@ pub(crate) enum Task {
63 Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>), 63 Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
64 PrimeCaches(PrimeCachesProgress), 64 PrimeCaches(PrimeCachesProgress),
65 FetchWorkspace(ProjectWorkspaceProgress), 65 FetchWorkspace(ProjectWorkspaceProgress),
66 FetchBuildData(BuildDataProgress),
66} 67}
67 68
68impl fmt::Debug for Event { 69impl fmt::Debug for Event {
@@ -226,12 +227,33 @@ impl GlobalState {
226 } 227 }
227 ProjectWorkspaceProgress::End(workspaces) => { 228 ProjectWorkspaceProgress::End(workspaces) => {
228 self.fetch_workspaces_completed(); 229 self.fetch_workspaces_completed();
229 self.switch_workspaces(workspaces); 230 self.switch_workspaces(workspaces, None);
230 (Progress::End, None) 231 (Progress::End, None)
231 } 232 }
232 }; 233 };
233 self.report_progress("fetching", state, msg, None); 234 self.report_progress("fetching", state, msg, None);
234 } 235 }
236 Task::FetchBuildData(progress) => {
237 let (state, msg) = match progress {
238 BuildDataProgress::Begin => (Some(Progress::Begin), None),
239 BuildDataProgress::Report(msg) => {
240 (Some(Progress::Report), Some(msg))
241 }
242 BuildDataProgress::End(collector) => {
243 self.fetch_build_data_completed();
244 let workspaces = (*self.workspaces)
245 .clone()
246 .into_iter()
247 .map(|it| Ok(it))
248 .collect();
249 self.switch_workspaces(workspaces, Some(collector));
250 (Some(Progress::End), None)
251 }
252 };
253 if let Some(state) = state {
254 self.report_progress("loading", state, msg, None);
255 }
256 }
235 } 257 }
236 // Coalesce multiple task events into one loop turn 258 // Coalesce multiple task events into one loop turn
237 task = match self.task_pool.receiver.try_recv() { 259 task = match self.task_pool.receiver.try_recv() {
@@ -287,7 +309,11 @@ impl GlobalState {
287 Progress::Report 309 Progress::Report
288 } else { 310 } else {
289 assert_eq!(n_done, n_total); 311 assert_eq!(n_done, n_total);
290 self.transition(Status::Ready); 312 let status = Status::Ready {
313 partial: self.config.load_out_dirs_from_check()
314 && self.workspace_build_data.is_none(),
315 };
316 self.transition(status);
291 Progress::End 317 Progress::End
292 }; 318 };
293 self.report_progress( 319 self.report_progress(
@@ -372,13 +398,14 @@ impl GlobalState {
372 } 398 }
373 399
374 let state_changed = self.process_changes(); 400 let state_changed = self.process_changes();
375 if prev_status == Status::Loading && self.status == Status::Ready { 401 let is_ready = matches!(self.status, Status::Ready { .. } );
402 if prev_status == Status::Loading && is_ready {
376 for flycheck in &self.flycheck { 403 for flycheck in &self.flycheck {
377 flycheck.update(); 404 flycheck.update();
378 } 405 }
379 } 406 }
380 407
381 if self.status == Status::Ready && (state_changed || prev_status == Status::Loading) { 408 if is_ready && (state_changed || prev_status == Status::Loading) {
382 self.update_file_notifications_on_threadpool(); 409 self.update_file_notifications_on_threadpool();
383 410
384 // Refresh semantic tokens if the client supports it. 411 // Refresh semantic tokens if the client supports it.
@@ -408,6 +435,7 @@ impl GlobalState {
408 } 435 }
409 436
410 self.fetch_workspaces_if_needed(); 437 self.fetch_workspaces_if_needed();
438 self.fetch_build_data_if_needed();
411 439
412 let loop_duration = loop_start.elapsed(); 440 let loop_duration = loop_start.elapsed();
413 if loop_duration > Duration::from_millis(100) { 441 if loop_duration > Duration::from_millis(100) {
diff --git a/crates/rust-analyzer/src/op_queue.rs b/crates/rust-analyzer/src/op_queue.rs
index 51d66f4b3..761b9ad39 100644
--- a/crates/rust-analyzer/src/op_queue.rs
+++ b/crates/rust-analyzer/src/op_queue.rs
@@ -1,22 +1,26 @@
1//! Bookkeeping to make sure only one long-running operation is executed. 1//! Bookkeeping to make sure only one long-running operation is executed.
2 2
3#[derive(Default)] 3pub(crate) struct OpQueue<D> {
4pub(crate) struct OpQueue { 4 op_scheduled: Option<D>,
5 op_scheduled: bool,
6 op_in_progress: bool, 5 op_in_progress: bool,
7} 6}
8 7
9impl OpQueue { 8impl<D> Default for OpQueue<D> {
10 pub(crate) fn request_op(&mut self) { 9 fn default() -> Self {
11 self.op_scheduled = true; 10 Self { op_scheduled: None, op_in_progress: false }
12 } 11 }
13 pub(crate) fn should_start_op(&mut self) -> bool { 12}
14 if !self.op_in_progress && self.op_scheduled { 13
15 self.op_in_progress = true; 14impl<D> OpQueue<D> {
16 self.op_scheduled = false; 15 pub(crate) fn request_op(&mut self, data: D) {
17 return true; 16 self.op_scheduled = Some(data);
17 }
18 pub(crate) fn should_start_op(&mut self) -> Option<D> {
19 if self.op_in_progress {
20 return None;
18 } 21 }
19 false 22 self.op_in_progress = self.op_scheduled.is_some();
23 self.op_scheduled.take()
20 } 24 }
21 pub(crate) fn op_completed(&mut self) { 25 pub(crate) fn op_completed(&mut self) {
22 assert!(self.op_in_progress); 26 assert!(self.op_in_progress);
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index ef73099cf..289bbc443 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -4,7 +4,7 @@ use std::{mem, sync::Arc};
4use flycheck::{FlycheckConfig, FlycheckHandle}; 4use flycheck::{FlycheckConfig, FlycheckHandle};
5use ide::Change; 5use ide::Change;
6use ide_db::base_db::{CrateGraph, SourceRoot, VfsPath}; 6use ide_db::base_db::{CrateGraph, SourceRoot, VfsPath};
7use project_model::{ProcMacroClient, ProjectWorkspace}; 7use project_model::{BuildDataCollector, BuildDataResult, ProcMacroClient, ProjectWorkspace};
8use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; 8use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
9 9
10use crate::{ 10use crate::{
@@ -22,6 +22,13 @@ pub(crate) enum ProjectWorkspaceProgress {
22 End(Vec<anyhow::Result<ProjectWorkspace>>), 22 End(Vec<anyhow::Result<ProjectWorkspace>>),
23} 23}
24 24
25#[derive(Debug)]
26pub(crate) enum BuildDataProgress {
27 Begin,
28 Report(String),
29 End(anyhow::Result<BuildDataResult>),
30}
31
25impl GlobalState { 32impl GlobalState {
26 pub(crate) fn update_configuration(&mut self, config: Config) { 33 pub(crate) fn update_configuration(&mut self, config: Config) {
27 let _p = profile::span("GlobalState::update_configuration"); 34 let _p = profile::span("GlobalState::update_configuration");
@@ -41,7 +48,7 @@ impl GlobalState {
41 } 48 }
42 match self.status { 49 match self.status {
43 Status::Loading | Status::NeedsReload => return, 50 Status::Loading | Status::NeedsReload => return,
44 Status::Ready | Status::Invalid => (), 51 Status::Ready { .. } | Status::Invalid => (),
45 } 52 }
46 if self.config.cargo_autoreload() { 53 if self.config.cargo_autoreload() {
47 self.fetch_workspaces_request(); 54 self.fetch_workspaces_request();
@@ -89,7 +96,8 @@ impl GlobalState {
89 if self.config.status_notification() { 96 if self.config.status_notification() {
90 let lsp_status = match new_status { 97 let lsp_status = match new_status {
91 Status::Loading => lsp_ext::Status::Loading, 98 Status::Loading => lsp_ext::Status::Loading,
92 Status::Ready => lsp_ext::Status::Ready, 99 Status::Ready { partial: true } => lsp_ext::Status::ReadyPartial,
100 Status::Ready { partial: false } => lsp_ext::Status::Ready,
93 Status::Invalid => lsp_ext::Status::Invalid, 101 Status::Invalid => lsp_ext::Status::Invalid,
94 Status::NeedsReload => lsp_ext::Status::NeedsReload, 102 Status::NeedsReload => lsp_ext::Status::NeedsReload,
95 }; 103 };
@@ -99,11 +107,37 @@ impl GlobalState {
99 } 107 }
100 } 108 }
101 109
110 pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) {
111 self.fetch_build_data_queue.request_op(build_data_collector);
112 }
113
114 pub(crate) fn fetch_build_data_if_needed(&mut self) {
115 let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() {
116 Some(it) => it,
117 None => return,
118 };
119 self.task_pool.handle.spawn_with_sender(move |sender| {
120 sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
121
122 let progress = {
123 let sender = sender.clone();
124 move |msg| {
125 sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
126 }
127 };
128 let res = build_data_collector.collect(&progress);
129 sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap();
130 });
131 }
132 pub(crate) fn fetch_build_data_completed(&mut self) {
133 self.fetch_build_data_queue.op_completed()
134 }
135
102 pub(crate) fn fetch_workspaces_request(&mut self) { 136 pub(crate) fn fetch_workspaces_request(&mut self) {
103 self.fetch_workspaces_queue.request_op() 137 self.fetch_workspaces_queue.request_op(())
104 } 138 }
105 pub(crate) fn fetch_workspaces_if_needed(&mut self) { 139 pub(crate) fn fetch_workspaces_if_needed(&mut self) {
106 if !self.fetch_workspaces_queue.should_start_op() { 140 if self.fetch_workspaces_queue.should_start_op().is_none() {
107 return; 141 return;
108 } 142 }
109 log::info!("will fetch workspaces"); 143 log::info!("will fetch workspaces");
@@ -154,7 +188,11 @@ impl GlobalState {
154 self.fetch_workspaces_queue.op_completed() 188 self.fetch_workspaces_queue.op_completed()
155 } 189 }
156 190
157 pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) { 191 pub(crate) fn switch_workspaces(
192 &mut self,
193 workspaces: Vec<anyhow::Result<ProjectWorkspace>>,
194 workspace_build_data: Option<anyhow::Result<BuildDataResult>>,
195 ) {
158 let _p = profile::span("GlobalState::switch_workspaces"); 196 let _p = profile::span("GlobalState::switch_workspaces");
159 log::info!("will switch workspaces: {:?}", workspaces); 197 log::info!("will switch workspaces: {:?}", workspaces);
160 198
@@ -176,7 +214,20 @@ impl GlobalState {
176 }) 214 })
177 .collect::<Vec<_>>(); 215 .collect::<Vec<_>>();
178 216
179 if &*self.workspaces == &workspaces { 217 let workspace_build_data = match workspace_build_data {
218 Some(Ok(it)) => Some(it),
219 Some(Err(err)) => {
220 log::error!("failed to fetch build data: {:#}", err);
221 self.show_message(
222 lsp_types::MessageType::Error,
223 format!("rust-analyzer failed to fetch build data: {:#}", err),
224 );
225 return;
226 }
227 None => None,
228 };
229
230 if &*self.workspaces == &workspaces && self.workspace_build_data == workspace_build_data {
180 return; 231 return;
181 } 232 }
182 233
@@ -189,7 +240,7 @@ impl GlobalState {
189 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { 240 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
190 watchers: workspaces 241 watchers: workspaces
191 .iter() 242 .iter()
192 .flat_map(ProjectWorkspace::to_roots) 243 .flat_map(|it| it.to_roots(workspace_build_data.as_ref()))
193 .filter(|it| it.is_member) 244 .filter(|it| it.is_member)
194 .flat_map(|root| { 245 .flat_map(|root| {
195 root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display())) 246 root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display()))
@@ -215,7 +266,8 @@ impl GlobalState {
215 let mut change = Change::new(); 266 let mut change = Change::new();
216 267
217 let files_config = self.config.files(); 268 let files_config = self.config.files();
218 let project_folders = ProjectFolders::new(&workspaces, &files_config.exclude); 269 let project_folders =
270 ProjectFolders::new(&workspaces, &files_config.exclude, workspace_build_data.as_ref());
219 271
220 self.proc_macro_client = match self.config.proc_macro_srv() { 272 self.proc_macro_client = match self.config.proc_macro_srv() {
221 None => None, 273 None => None,
@@ -257,15 +309,28 @@ impl GlobalState {
257 res 309 res
258 }; 310 };
259 for ws in workspaces.iter() { 311 for ws in workspaces.iter() {
260 crate_graph.extend(ws.to_crate_graph(self.proc_macro_client.as_ref(), &mut load)); 312 crate_graph.extend(ws.to_crate_graph(
313 self.workspace_build_data.as_ref(),
314 self.proc_macro_client.as_ref(),
315 &mut load,
316 ));
261 } 317 }
262 318
263 crate_graph 319 crate_graph
264 }; 320 };
265 change.set_crate_graph(crate_graph); 321 change.set_crate_graph(crate_graph);
266 322
323 if self.config.load_out_dirs_from_check() && workspace_build_data.is_none() {
324 let mut collector = BuildDataCollector::default();
325 for ws in &workspaces {
326 ws.collect_build_data_configs(&mut collector);
327 }
328 self.fetch_build_data_request(collector)
329 }
330
267 self.source_root_config = project_folders.source_root_config; 331 self.source_root_config = project_folders.source_root_config;
268 self.workspaces = Arc::new(workspaces); 332 self.workspaces = Arc::new(workspaces);
333 self.workspace_build_data = workspace_build_data;
269 334
270 self.analysis_host.apply_change(change); 335 self.analysis_host.apply_change(change);
271 self.process_changes(); 336 self.process_changes();
@@ -323,12 +388,13 @@ impl ProjectFolders {
323 pub(crate) fn new( 388 pub(crate) fn new(
324 workspaces: &[ProjectWorkspace], 389 workspaces: &[ProjectWorkspace],
325 global_excludes: &[AbsPathBuf], 390 global_excludes: &[AbsPathBuf],
391 build_data: Option<&BuildDataResult>,
326 ) -> ProjectFolders { 392 ) -> ProjectFolders {
327 let mut res = ProjectFolders::default(); 393 let mut res = ProjectFolders::default();
328 let mut fsc = FileSetConfig::builder(); 394 let mut fsc = FileSetConfig::builder();
329 let mut local_filesets = vec![]; 395 let mut local_filesets = vec![];
330 396
331 for root in workspaces.iter().flat_map(|it| it.to_roots()) { 397 for root in workspaces.iter().flat_map(|it| it.to_roots(build_data)) {
332 let file_set_roots: Vec<VfsPath> = 398 let file_set_roots: Vec<VfsPath> =
333 root.include.iter().cloned().map(VfsPath::from).collect(); 399 root.include.iter().cloned().map(VfsPath::from).collect();
334 400