diff options
author | Edwin Cheng <[email protected]> | 2021-01-28 15:33:02 +0000 |
---|---|---|
committer | Edwin Cheng <[email protected]> | 2021-01-28 17:04:14 +0000 |
commit | 9358eecc042d8b551f58d2d5ddb9c88d258880c1 (patch) | |
tree | 7188b0e27d9d00640b5c76319ee59b2d5cab1b05 /crates | |
parent | f421ee672253499b8ca8d1badf98db42525a5216 (diff) |
Async Loading outdir and proc-macro
Diffstat (limited to 'crates')
-rw-r--r-- | crates/project_model/src/build_data.rs | 288 | ||||
-rw-r--r-- | crates/project_model/src/cargo_workspace.rs | 47 | ||||
-rw-r--r-- | crates/project_model/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/project_model/src/workspace.rs | 53 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/load_cargo.rs | 36 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 4 | ||||
-rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 12 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_ext.rs | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 38 | ||||
-rw-r--r-- | crates/rust-analyzer/src/op_queue.rs | 28 | ||||
-rw-r--r-- | crates/rust-analyzer/src/reload.rs | 88 |
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 | ||
10 | use anyhow::Result; | 11 | use anyhow::Result; |
11 | use cargo_metadata::{BuildScript, Message, Package, PackageId}; | 12 | use cargo_metadata::{BuildScript, Message}; |
12 | use itertools::Itertools; | 13 | use itertools::Itertools; |
13 | use paths::{AbsPath, AbsPathBuf}; | 14 | use paths::{AbsPath, AbsPathBuf}; |
14 | use rustc_hash::FxHashMap; | 15 | use rustc_hash::FxHashMap; |
@@ -16,150 +17,195 @@ use stdx::JodChild; | |||
16 | 17 | ||
17 | use crate::{cfg_flag::CfgFlag, CargoConfig}; | 18 | use crate::{cfg_flag::CfgFlag, CargoConfig}; |
18 | 19 | ||
19 | #[derive(Debug, Clone, Default)] | ||
20 | pub(crate) struct BuildDataMap { | ||
21 | data: FxHashMap<PackageId, BuildData>, | ||
22 | } | ||
23 | #[derive(Debug, Clone, Default, PartialEq, Eq)] | 20 | #[derive(Debug, Clone, Default, PartialEq, Eq)] |
24 | pub struct BuildData { | 21 | pub(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 | ||
38 | impl BuildDataMap { | 35 | #[derive(Clone, Debug)] |
39 | pub(crate) fn new( | 36 | pub(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 { | 42 | impl 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()); | 48 | impl 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 | } | 51 | pub 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; | 56 | pub 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 | { | 60 | pub(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(); | 62 | impl 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 | |||
84 | impl 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 { | 90 | impl 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> { | 100 | fn 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> |
176 | fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) { | 222 | fn 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 | ||
3 | use std::{convert::TryInto, ops, process::Command}; | 3 | use std::{convert::TryInto, ops, process::Command, sync::Arc}; |
4 | 4 | ||
5 | use anyhow::{Context, Result}; | 5 | use anyhow::{Context, Result}; |
6 | use base_db::Edition; | 6 | use base_db::Edition; |
@@ -9,7 +9,7 @@ use la_arena::{Arena, Idx}; | |||
9 | use paths::{AbsPath, AbsPathBuf}; | 9 | use paths::{AbsPath, AbsPathBuf}; |
10 | use rustc_hash::FxHashMap; | 10 | use rustc_hash::FxHashMap; |
11 | 11 | ||
12 | use crate::build_data::{BuildData, BuildDataMap}; | 12 | use crate::build_data::BuildDataConfig; |
13 | use crate::utf8_stdout; | 13 | use 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 | ||
32 | impl ops::Index<Package> for CargoWorkspace { | 33 | impl 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}; | |||
19 | use rustc_hash::FxHashSet; | 19 | use rustc_hash::FxHashSet; |
20 | 20 | ||
21 | pub use crate::{ | 21 | pub 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; | |||
16 | use rustc_hash::{FxHashMap, FxHashSet}; | 16 | use rustc_hash::{FxHashMap, FxHashSet}; |
17 | 17 | ||
18 | use crate::{ | 18 | use 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 | ||
257 | fn project_json_to_crate_graph( | 282 | fn 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( | |||
475 | fn add_target_crate_root( | 504 | fn 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; | |||
6 | use crossbeam_channel::{unbounded, Receiver}; | 6 | use crossbeam_channel::{unbounded, Receiver}; |
7 | use ide::{AnalysisHost, Change}; | 7 | use ide::{AnalysisHost, Change}; |
8 | use ide_db::base_db::CrateGraph; | 8 | use ide_db::base_db::CrateGraph; |
9 | use project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace}; | 9 | use project_model::{ |
10 | BuildDataCollector, CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace, | ||
11 | }; | ||
10 | use vfs::{loader::Handle, AbsPath, AbsPathBuf}; | 12 | use vfs::{loader::Handle, AbsPath, AbsPathBuf}; |
11 | 13 | ||
12 | use crate::reload::{ProjectFolders, SourceRootConfig}; | 14 | use 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}; | |||
11 | use ide_db::base_db::{CrateId, VfsPath}; | 11 | use ide_db::base_db::{CrateId, VfsPath}; |
12 | use lsp_types::{SemanticTokens, Url}; | 12 | use lsp_types::{SemanticTokens, Url}; |
13 | use parking_lot::{Mutex, RwLock}; | 13 | use parking_lot::{Mutex, RwLock}; |
14 | use project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; | 14 | use project_model::{ |
15 | BuildDataCollector, BuildDataResult, CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target, | ||
16 | }; | ||
15 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::FxHashMap; |
16 | use vfs::AnchoredPathBuf; | 18 | use vfs::AnchoredPathBuf; |
17 | 19 | ||
@@ -33,7 +35,7 @@ use crate::{ | |||
33 | #[derive(Eq, PartialEq, Copy, Clone)] | 35 | #[derive(Eq, PartialEq, Copy, Clone)] |
34 | pub(crate) enum Status { | 36 | pub(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)] |
235 | pub enum Status { | 235 | pub 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 | ||
68 | impl fmt::Debug for Event { | 69 | impl 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)] | 3 | pub(crate) struct OpQueue<D> { |
4 | pub(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 | ||
9 | impl OpQueue { | 8 | impl<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; | 14 | impl<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}; | |||
4 | use flycheck::{FlycheckConfig, FlycheckHandle}; | 4 | use flycheck::{FlycheckConfig, FlycheckHandle}; |
5 | use ide::Change; | 5 | use ide::Change; |
6 | use ide_db::base_db::{CrateGraph, SourceRoot, VfsPath}; | 6 | use ide_db::base_db::{CrateGraph, SourceRoot, VfsPath}; |
7 | use project_model::{ProcMacroClient, ProjectWorkspace}; | 7 | use project_model::{BuildDataCollector, BuildDataResult, ProcMacroClient, ProjectWorkspace}; |
8 | use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; | 8 | use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; |
9 | 9 | ||
10 | use crate::{ | 10 | use 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)] | ||
26 | pub(crate) enum BuildDataProgress { | ||
27 | Begin, | ||
28 | Report(String), | ||
29 | End(anyhow::Result<BuildDataResult>), | ||
30 | } | ||
31 | |||
25 | impl GlobalState { | 32 | impl 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 | ||