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
-rw-r--r--docs/dev/lsp-extensions.md4
-rw-r--r--editors/code/src/ctx.ts6
-rw-r--r--editors/code/src/lsp_ext.ts2
14 files changed, 406 insertions, 202 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..031a0cef4 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 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
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 78d86f060..d7f287894 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
1<!--- 1<!---
2lsp_ext.rs hash: 91f2c62457e0a20f 2lsp_ext.rs hash: 7609fd6d7b4ab231
3 3
4If you need to change the above hash to make the test pass, please check if you 4If you need to change the above hash to make the test pass, please check if you
5need to adjust this doc as well and ping this issue: 5need to adjust this doc as well and ping this issue:
@@ -423,7 +423,7 @@ Reloads project information (that is, re-executes `cargo metadata`).
423 423
424```typescript 424```typescript
425interface StatusParams { 425interface StatusParams {
426 status: "loading" | "ready" | "invalid" | "needsReload", 426 status: "loading" | "readyPartial" | "ready" | "invalid" | "needsReload",
427} 427}
428``` 428```
429 429
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index e7585184b..c07583cfa 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -74,6 +74,12 @@ export class Ctx {
74 this.statusBar.command = undefined; 74 this.statusBar.command = undefined;
75 this.statusBar.color = undefined; 75 this.statusBar.color = undefined;
76 break; 76 break;
77 case "readyPartial":
78 this.statusBar.text = "rust-analyzer";
79 this.statusBar.tooltip = "Ready (Partial)";
80 this.statusBar.command = undefined;
81 this.statusBar.color = undefined;
82 break;
77 case "ready": 83 case "ready":
78 this.statusBar.text = "rust-analyzer"; 84 this.statusBar.text = "rust-analyzer";
79 this.statusBar.tooltip = "Ready"; 85 this.statusBar.tooltip = "Ready";
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index d21a3db86..2de1e427d 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -10,7 +10,7 @@ export interface AnalyzerStatusParams {
10export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>("rust-analyzer/analyzerStatus"); 10export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>("rust-analyzer/analyzerStatus");
11export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage"); 11export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage");
12 12
13export type Status = "loading" | "ready" | "invalid" | "needsReload"; 13export type Status = "loading" | "ready" | "readyPartial" | "invalid" | "needsReload";
14export interface StatusParams { 14export interface StatusParams {
15 status: Status; 15 status: Status;
16} 16}