aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--crates/ra_cargo_watch/src/lib.rs175
-rw-r--r--crates/ra_db/src/input.rs21
-rw-r--r--crates/ra_project_model/Cargo.toml1
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs67
-rw-r--r--crates/ra_project_model/src/json_project.rs1
-rw-r--r--crates/ra_project_model/src/lib.rs57
-rw-r--r--crates/rust-analyzer/src/bin/args.rs14
-rw-r--r--crates/rust-analyzer/src/bin/main.rs32
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs9
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs3
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs70
-rw-r--r--crates/rust-analyzer/src/config.rs4
-rw-r--r--crates/rust-analyzer/src/main_loop.rs1
-rw-r--r--crates/rust-analyzer/src/world.rs20
-rw-r--r--editors/code/package.json10
-rw-r--r--editors/code/src/client.ts1
-rw-r--r--editors/code/src/config.ts4
18 files changed, 315 insertions, 176 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f6df77206..f76857724 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1092,6 +1092,7 @@ dependencies = [
1092 "cargo_metadata", 1092 "cargo_metadata",
1093 "log", 1093 "log",
1094 "ra_arena", 1094 "ra_arena",
1095 "ra_cargo_watch",
1095 "ra_cfg", 1096 "ra_cfg",
1096 "ra_db", 1097 "ra_db",
1097 "rustc-hash", 1098 "rustc-hash",
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs
index 1a6926db3..bffe5eb00 100644
--- a/crates/ra_cargo_watch/src/lib.rs
+++ b/crates/ra_cargo_watch/src/lib.rs
@@ -9,8 +9,8 @@ use lsp_types::{
9}; 9};
10use std::{ 10use std::{
11 io::{BufRead, BufReader}, 11 io::{BufRead, BufReader},
12 path::PathBuf, 12 path::{Path, PathBuf},
13 process::{Command, Stdio}, 13 process::{Child, Command, Stdio},
14 thread::JoinHandle, 14 thread::JoinHandle,
15 time::Instant, 15 time::Instant,
16}; 16};
@@ -246,18 +246,71 @@ enum CheckEvent {
246 End, 246 End,
247} 247}
248 248
249pub fn run_cargo(
250 args: &[String],
251 current_dir: Option<&Path>,
252 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
253) -> Child {
254 let mut command = Command::new("cargo");
255 if let Some(current_dir) = current_dir {
256 command.current_dir(current_dir);
257 }
258
259 let mut child = command
260 .args(args)
261 .stdout(Stdio::piped())
262 .stderr(Stdio::null())
263 .stdin(Stdio::null())
264 .spawn()
265 .expect("couldn't launch cargo");
266
267 // We manually read a line at a time, instead of using serde's
268 // stream deserializers, because the deserializer cannot recover
269 // from an error, resulting in it getting stuck, because we try to
270 // be resillient against failures.
271 //
272 // Because cargo only outputs one JSON object per line, we can
273 // simply skip a line if it doesn't parse, which just ignores any
274 // erroneus output.
275 let stdout = BufReader::new(child.stdout.take().unwrap());
276 for line in stdout.lines() {
277 let line = match line {
278 Ok(line) => line,
279 Err(err) => {
280 log::error!("Couldn't read line from cargo: {}", err);
281 continue;
282 }
283 };
284
285 let message = serde_json::from_str::<cargo_metadata::Message>(&line);
286 let message = match message {
287 Ok(message) => message,
288 Err(err) => {
289 log::error!("Invalid json from cargo check, ignoring ({}): {:?} ", err, line);
290 continue;
291 }
292 };
293
294 if !on_message(message) {
295 break;
296 }
297 }
298
299 child
300}
301
249impl WatchThread { 302impl WatchThread {
250 fn dummy() -> WatchThread { 303 fn dummy() -> WatchThread {
251 WatchThread { handle: None, message_recv: never() } 304 WatchThread { handle: None, message_recv: never() }
252 } 305 }
253 306
254 fn new(options: &CheckOptions, workspace_root: &PathBuf) -> WatchThread { 307 fn new(options: &CheckOptions, workspace_root: &Path) -> WatchThread {
255 let mut args: Vec<String> = vec![ 308 let mut args: Vec<String> = vec![
256 options.command.clone(), 309 options.command.clone(),
257 "--workspace".to_string(), 310 "--workspace".to_string(),
258 "--message-format=json".to_string(), 311 "--message-format=json".to_string(),
259 "--manifest-path".to_string(), 312 "--manifest-path".to_string(),
260 format!("{}/Cargo.toml", workspace_root.to_string_lossy()), 313 format!("{}/Cargo.toml", workspace_root.display()),
261 ]; 314 ];
262 if options.all_targets { 315 if options.all_targets {
263 args.push("--all-targets".to_string()); 316 args.push("--all-targets".to_string());
@@ -265,83 +318,47 @@ impl WatchThread {
265 args.extend(options.args.iter().cloned()); 318 args.extend(options.args.iter().cloned());
266 319
267 let (message_send, message_recv) = unbounded(); 320 let (message_send, message_recv) = unbounded();
268 let enabled = options.enable; 321 let workspace_root = workspace_root.to_owned();
269 let handle = std::thread::spawn(move || { 322 let handle = if options.enable {
270 if !enabled { 323 Some(std::thread::spawn(move || {
271 return; 324 // If we trigger an error here, we will do so in the loop instead,
272 } 325 // which will break out of the loop, and continue the shutdown
273 326 let _ = message_send.send(CheckEvent::Begin);
274 let mut command = Command::new("cargo") 327
275 .args(&args) 328 let mut child = run_cargo(&args, Some(&workspace_root), &mut |message| {
276 .stdout(Stdio::piped()) 329 // Skip certain kinds of messages to only spend time on what's useful
277 .stderr(Stdio::null()) 330 match &message {
278 .stdin(Stdio::null()) 331 Message::CompilerArtifact(artifact) if artifact.fresh => return true,
279 .spawn() 332 Message::BuildScriptExecuted(_) => return true,
280 .expect("couldn't launch cargo"); 333 Message::Unknown => return true,
281 334 _ => {}
282 // If we trigger an error here, we will do so in the loop instead,
283 // which will break out of the loop, and continue the shutdown
284 let _ = message_send.send(CheckEvent::Begin);
285
286 // We manually read a line at a time, instead of using serde's
287 // stream deserializers, because the deserializer cannot recover
288 // from an error, resulting in it getting stuck, because we try to
289 // be resillient against failures.
290 //
291 // Because cargo only outputs one JSON object per line, we can
292 // simply skip a line if it doesn't parse, which just ignores any
293 // erroneus output.
294 let stdout = BufReader::new(command.stdout.take().unwrap());
295 for line in stdout.lines() {
296 let line = match line {
297 Ok(line) => line,
298 Err(err) => {
299 log::error!("Couldn't read line from cargo: {}", err);
300 continue;
301 }
302 };
303
304 let message = serde_json::from_str::<cargo_metadata::Message>(&line);
305 let message = match message {
306 Ok(message) => message,
307 Err(err) => {
308 log::error!(
309 "Invalid json from cargo check, ignoring ({}): {:?} ",
310 err,
311 line
312 );
313 continue;
314 } 335 }
315 };
316
317 // Skip certain kinds of messages to only spend time on what's useful
318 match &message {
319 Message::CompilerArtifact(artifact) if artifact.fresh => continue,
320 Message::BuildScriptExecuted(_) => continue,
321 Message::Unknown => continue,
322 _ => {}
323 }
324 336
325 match message_send.send(CheckEvent::Msg(message)) { 337 match message_send.send(CheckEvent::Msg(message)) {
326 Ok(()) => {} 338 Ok(()) => {}
327 Err(_err) => { 339 Err(_err) => {
328 // The send channel was closed, so we want to shutdown 340 // The send channel was closed, so we want to shutdown
329 break; 341 return false;
330 } 342 }
331 } 343 };
332 } 344
333 345 true
334 // We can ignore any error here, as we are already in the progress 346 });
335 // of shutting down. 347
336 let _ = message_send.send(CheckEvent::End); 348 // We can ignore any error here, as we are already in the progress
337 349 // of shutting down.
338 // It is okay to ignore the result, as it only errors if the process is already dead 350 let _ = message_send.send(CheckEvent::End);
339 let _ = command.kill(); 351
340 352 // It is okay to ignore the result, as it only errors if the process is already dead
341 // Again, we don't care about the exit status so just ignore the result 353 let _ = child.kill();
342 let _ = command.wait(); 354
343 }); 355 // Again, we don't care about the exit status so just ignore the result
344 WatchThread { handle: Some(handle), message_recv } 356 let _ = child.wait();
357 }))
358 } else {
359 None
360 };
361 WatchThread { handle, message_recv }
345 } 362 }
346} 363}
347 364
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index bde843001..e371f849d 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -6,7 +6,11 @@
6//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how 6//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
7//! actual IO is done and lowered to input. 7//! actual IO is done and lowered to input.
8 8
9use std::{fmt, ops, str::FromStr}; 9use std::{
10 fmt, ops,
11 path::{Path, PathBuf},
12 str::FromStr,
13};
10 14
11use ra_cfg::CfgOptions; 15use ra_cfg::CfgOptions;
12use ra_syntax::SmolStr; 16use ra_syntax::SmolStr;
@@ -144,7 +148,7 @@ pub struct Env {
144// crate. We store a map to allow remap it to ExternSourceId 148// crate. We store a map to allow remap it to ExternSourceId
145#[derive(Default, Debug, Clone, PartialEq, Eq)] 149#[derive(Default, Debug, Clone, PartialEq, Eq)]
146pub struct ExternSource { 150pub struct ExternSource {
147 extern_paths: FxHashMap<String, ExternSourceId>, 151 extern_paths: FxHashMap<PathBuf, ExternSourceId>,
148} 152}
149 153
150#[derive(Debug, Clone, PartialEq, Eq)] 154#[derive(Debug, Clone, PartialEq, Eq)]
@@ -294,13 +298,10 @@ impl Env {
294} 298}
295 299
296impl ExternSource { 300impl ExternSource {
297 pub fn extern_path(&self, path: &str) -> Option<(ExternSourceId, RelativePathBuf)> { 301 pub fn extern_path(&self, path: impl AsRef<Path>) -> Option<(ExternSourceId, RelativePathBuf)> {
302 let path = path.as_ref();
298 self.extern_paths.iter().find_map(|(root_path, id)| { 303 self.extern_paths.iter().find_map(|(root_path, id)| {
299 if path.starts_with(root_path) { 304 if let Ok(rel_path) = path.strip_prefix(root_path) {
300 let mut rel_path = &path[root_path.len()..];
301 if rel_path.starts_with("/") {
302 rel_path = &rel_path[1..];
303 }
304 let rel_path = RelativePathBuf::from_path(rel_path).ok()?; 305 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
305 Some((id.clone(), rel_path)) 306 Some((id.clone(), rel_path))
306 } else { 307 } else {
@@ -309,8 +310,8 @@ impl ExternSource {
309 }) 310 })
310 } 311 }
311 312
312 pub fn set_extern_path(&mut self, root_path: &str, root: ExternSourceId) { 313 pub fn set_extern_path(&mut self, root_path: &Path, root: ExternSourceId) {
313 self.extern_paths.insert(root_path.to_owned(), root); 314 self.extern_paths.insert(root_path.to_path_buf(), root);
314 } 315 }
315} 316}
316 317
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml
index 6252241bf..22300548a 100644
--- a/crates/ra_project_model/Cargo.toml
+++ b/crates/ra_project_model/Cargo.toml
@@ -16,6 +16,7 @@ cargo_metadata = "0.9.1"
16ra_arena = { path = "../ra_arena" } 16ra_arena = { path = "../ra_arena" }
17ra_db = { path = "../ra_db" } 17ra_db = { path = "../ra_db" }
18ra_cfg = { path = "../ra_cfg" } 18ra_cfg = { path = "../ra_cfg" }
19ra_cargo_watch = { path = "../ra_cargo_watch" }
19 20
20serde = { version = "1.0.104", features = ["derive"] } 21serde = { version = "1.0.104", features = ["derive"] }
21serde_json = "1.0.48" 22serde_json = "1.0.48"
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 4fea459d5..10ecfa951 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -3,8 +3,9 @@
3use std::path::{Path, PathBuf}; 3use std::path::{Path, PathBuf};
4 4
5use anyhow::{Context, Result}; 5use anyhow::{Context, Result};
6use cargo_metadata::{CargoOpt, MetadataCommand}; 6use cargo_metadata::{CargoOpt, Message, MetadataCommand, PackageId};
7use ra_arena::{impl_arena_id, Arena, RawId}; 7use ra_arena::{impl_arena_id, Arena, RawId};
8use ra_cargo_watch::run_cargo;
8use ra_db::Edition; 9use ra_db::Edition;
9use rustc_hash::FxHashMap; 10use rustc_hash::FxHashMap;
10use serde::Deserialize; 11use serde::Deserialize;
@@ -35,11 +36,19 @@ pub struct CargoFeatures {
35 /// List of features to activate. 36 /// List of features to activate.
36 /// This will be ignored if `cargo_all_features` is true. 37 /// This will be ignored if `cargo_all_features` is true.
37 pub features: Vec<String>, 38 pub features: Vec<String>,
39
40 /// Runs cargo check on launch to figure out the correct values of OUT_DIR
41 pub load_out_dirs_from_check: bool,
38} 42}
39 43
40impl Default for CargoFeatures { 44impl Default for CargoFeatures {
41 fn default() -> Self { 45 fn default() -> Self {
42 CargoFeatures { no_default_features: false, all_features: true, features: Vec::new() } 46 CargoFeatures {
47 no_default_features: false,
48 all_features: true,
49 features: Vec::new(),
50 load_out_dirs_from_check: false,
51 }
43 } 52 }
44} 53}
45 54
@@ -60,6 +69,7 @@ struct PackageData {
60 dependencies: Vec<PackageDependency>, 69 dependencies: Vec<PackageDependency>,
61 edition: Edition, 70 edition: Edition,
62 features: Vec<String>, 71 features: Vec<String>,
72 out_dir: Option<PathBuf>,
63} 73}
64 74
65#[derive(Debug, Clone)] 75#[derive(Debug, Clone)]
@@ -131,6 +141,9 @@ impl Package {
131 ) -> impl Iterator<Item = &'a PackageDependency> + 'a { 141 ) -> impl Iterator<Item = &'a PackageDependency> + 'a {
132 ws.packages[self].dependencies.iter() 142 ws.packages[self].dependencies.iter()
133 } 143 }
144 pub fn out_dir(self, ws: &CargoWorkspace) -> Option<&Path> {
145 ws.packages[self].out_dir.as_ref().map(PathBuf::as_path)
146 }
134} 147}
135 148
136impl Target { 149impl Target {
@@ -173,6 +186,12 @@ impl CargoWorkspace {
173 let meta = meta.exec().with_context(|| { 186 let meta = meta.exec().with_context(|| {
174 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) 187 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display())
175 })?; 188 })?;
189
190 let mut out_dir_by_id = FxHashMap::default();
191 if cargo_features.load_out_dirs_from_check {
192 out_dir_by_id = load_out_dirs(cargo_toml, cargo_features);
193 }
194
176 let mut pkg_by_id = FxHashMap::default(); 195 let mut pkg_by_id = FxHashMap::default();
177 let mut packages = Arena::default(); 196 let mut packages = Arena::default();
178 let mut targets = Arena::default(); 197 let mut targets = Arena::default();
@@ -193,6 +212,7 @@ impl CargoWorkspace {
193 edition, 212 edition,
194 dependencies: Vec::new(), 213 dependencies: Vec::new(),
195 features: Vec::new(), 214 features: Vec::new(),
215 out_dir: out_dir_by_id.get(&id).cloned(),
196 }); 216 });
197 let pkg_data = &mut packages[pkg]; 217 let pkg_data = &mut packages[pkg];
198 pkg_by_id.insert(id, pkg); 218 pkg_by_id.insert(id, pkg);
@@ -252,3 +272,46 @@ impl CargoWorkspace {
252 &self.workspace_root 272 &self.workspace_root
253 } 273 }
254} 274}
275
276pub fn load_out_dirs(
277 cargo_toml: &Path,
278 cargo_features: &CargoFeatures,
279) -> FxHashMap<PackageId, PathBuf> {
280 let mut args: Vec<String> = vec![
281 "check".to_string(),
282 "--message-format=json".to_string(),
283 "--manifest-path".to_string(),
284 format!("{}", cargo_toml.display()),
285 ];
286
287 if cargo_features.all_features {
288 args.push("--all-features".to_string());
289 } else if cargo_features.no_default_features {
290 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
291 // https://github.com/oli-obk/cargo_metadata/issues/79
292 args.push("--no-default-features".to_string());
293 } else if !cargo_features.features.is_empty() {
294 for feature in &cargo_features.features {
295 args.push(feature.clone());
296 }
297 }
298
299 let mut res = FxHashMap::default();
300 let mut child = run_cargo(&args, cargo_toml.parent(), &mut |message| {
301 match message {
302 Message::BuildScriptExecuted(message) => {
303 let package_id = message.package_id;
304 let out_dir = message.out_dir;
305 res.insert(package_id, out_dir);
306 }
307
308 Message::CompilerArtifact(_) => (),
309 Message::CompilerMessage(_) => (),
310 Message::Unknown => (),
311 }
312 true
313 });
314
315 let _ = child.wait();
316 res
317}
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs
index 1bacb1d09..336446e58 100644
--- a/crates/ra_project_model/src/json_project.rs
+++ b/crates/ra_project_model/src/json_project.rs
@@ -22,6 +22,7 @@ pub struct Crate {
22 pub(crate) deps: Vec<Dep>, 22 pub(crate) deps: Vec<Dep>,
23 pub(crate) atom_cfgs: FxHashSet<String>, 23 pub(crate) atom_cfgs: FxHashSet<String>,
24 pub(crate) key_value_cfgs: FxHashMap<String, String>, 24 pub(crate) key_value_cfgs: FxHashMap<String, String>,
25 pub(crate) out_dir: Option<PathBuf>,
25} 26}
26 27
27#[derive(Clone, Copy, Debug, Deserialize)] 28#[derive(Clone, Copy, Debug, Deserialize)]
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 897874813..081b1fec2 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -150,6 +150,29 @@ impl ProjectWorkspace {
150 } 150 }
151 } 151 }
152 152
153 pub fn out_dirs(&self) -> Vec<PathBuf> {
154 match self {
155 ProjectWorkspace::Json { project } => {
156 let mut out_dirs = Vec::with_capacity(project.crates.len());
157 for krate in &project.crates {
158 if let Some(out_dir) = &krate.out_dir {
159 out_dirs.push(out_dir.to_path_buf());
160 }
161 }
162 out_dirs
163 }
164 ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => {
165 let mut out_dirs = Vec::with_capacity(cargo.packages().len());
166 for pkg in cargo.packages() {
167 if let Some(out_dir) = pkg.out_dir(&cargo) {
168 out_dirs.push(out_dir.to_path_buf());
169 }
170 }
171 out_dirs
172 }
173 }
174 }
175
153 pub fn n_packages(&self) -> usize { 176 pub fn n_packages(&self) -> usize {
154 match self { 177 match self {
155 ProjectWorkspace::Json { project } => project.crates.len(), 178 ProjectWorkspace::Json { project } => project.crates.len(),
@@ -162,7 +185,7 @@ impl ProjectWorkspace {
162 pub fn to_crate_graph( 185 pub fn to_crate_graph(
163 &self, 186 &self,
164 default_cfg_options: &CfgOptions, 187 default_cfg_options: &CfgOptions,
165 outdirs: &FxHashMap<String, (ExternSourceId, String)>, 188 extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>,
166 load: &mut dyn FnMut(&Path) -> Option<FileId>, 189 load: &mut dyn FnMut(&Path) -> Option<FileId>,
167 ) -> CrateGraph { 190 ) -> CrateGraph {
168 let mut crate_graph = CrateGraph::default(); 191 let mut crate_graph = CrateGraph::default();
@@ -187,6 +210,16 @@ impl ProjectWorkspace {
187 opts 210 opts
188 }; 211 };
189 212
213 let mut env = Env::default();
214 let mut extern_source = ExternSource::default();
215 if let Some(out_dir) = &krate.out_dir {
216 // FIXME: We probably mangle non UTF-8 paths here, figure out a better solution
217 env.set("OUT_DIR", out_dir.to_string_lossy().to_string());
218 if let Some(&extern_source_id) = extern_source_roots.get(out_dir) {
219 extern_source.set_extern_path(&out_dir, extern_source_id);
220 }
221 }
222
190 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env 223 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env
191 crates.insert( 224 crates.insert(
192 crate_id, 225 crate_id,
@@ -196,8 +229,8 @@ impl ProjectWorkspace {
196 // FIXME json definitions can store the crate name 229 // FIXME json definitions can store the crate name
197 None, 230 None,
198 cfg_options, 231 cfg_options,
199 Env::default(), 232 env,
200 Default::default(), 233 extern_source,
201 ), 234 ),
202 ); 235 );
203 } 236 }
@@ -235,13 +268,8 @@ impl ProjectWorkspace {
235 opts 268 opts
236 }; 269 };
237 270
238 let mut env = Env::default(); 271 let env = Env::default();
239 let mut extern_source = ExternSource::default(); 272 let extern_source = ExternSource::default();
240 if let Some((id, path)) = outdirs.get(krate.name(&sysroot)) {
241 env.set("OUT_DIR", path.clone());
242 extern_source.set_extern_path(&path, *id);
243 }
244
245 let crate_id = crate_graph.add_crate_root( 273 let crate_id = crate_graph.add_crate_root(
246 file_id, 274 file_id,
247 Edition::Edition2018, 275 Edition::Edition2018,
@@ -292,9 +320,12 @@ impl ProjectWorkspace {
292 }; 320 };
293 let mut env = Env::default(); 321 let mut env = Env::default();
294 let mut extern_source = ExternSource::default(); 322 let mut extern_source = ExternSource::default();
295 if let Some((id, path)) = outdirs.get(pkg.name(&cargo)) { 323 if let Some(out_dir) = pkg.out_dir(cargo) {
296 env.set("OUT_DIR", path.clone()); 324 // FIXME: We probably mangle non UTF-8 paths here, figure out a better solution
297 extern_source.set_extern_path(&path, *id); 325 env.set("OUT_DIR", out_dir.to_string_lossy().to_string());
326 if let Some(&extern_source_id) = extern_source_roots.get(out_dir) {
327 extern_source.set_extern_path(&out_dir, extern_source_id);
328 }
298 } 329 }
299 let crate_id = crate_graph.add_crate_root( 330 let crate_id = crate_graph.add_crate_root(
300 file_id, 331 file_id,
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs
index 993d3d0b6..3cf394bb4 100644
--- a/crates/rust-analyzer/src/bin/args.rs
+++ b/crates/rust-analyzer/src/bin/args.rs
@@ -28,10 +28,12 @@ pub(crate) enum Command {
28 only: Option<String>, 28 only: Option<String>,
29 with_deps: bool, 29 with_deps: bool,
30 path: PathBuf, 30 path: PathBuf,
31 load_output_dirs: bool,
31 }, 32 },
32 Bench { 33 Bench {
33 path: PathBuf, 34 path: PathBuf,
34 what: BenchWhat, 35 what: BenchWhat,
36 load_output_dirs: bool,
35 }, 37 },
36 RunServer, 38 RunServer,
37 Version, 39 Version,
@@ -136,8 +138,9 @@ USAGE:
136 rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH] 138 rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH]
137 139
138FLAGS: 140FLAGS:
139 -h, --help Prints help information 141 -h, --help Prints help information
140 --memory-usage 142 --memory-usage
143 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
141 -v, --verbose 144 -v, --verbose
142 -q, --quiet 145 -q, --quiet
143 146
@@ -154,6 +157,7 @@ ARGS:
154 let memory_usage = matches.contains("--memory-usage"); 157 let memory_usage = matches.contains("--memory-usage");
155 let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?; 158 let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
156 let with_deps: bool = matches.contains("--with-deps"); 159 let with_deps: bool = matches.contains("--with-deps");
160 let load_output_dirs = matches.contains("--load-output-dirs");
157 let path = { 161 let path = {
158 let mut trailing = matches.free()?; 162 let mut trailing = matches.free()?;
159 if trailing.len() != 1 { 163 if trailing.len() != 1 {
@@ -162,7 +166,7 @@ ARGS:
162 trailing.pop().unwrap().into() 166 trailing.pop().unwrap().into()
163 }; 167 };
164 168
165 Command::Stats { randomize, memory_usage, only, with_deps, path } 169 Command::Stats { randomize, memory_usage, only, with_deps, path, load_output_dirs }
166 } 170 }
167 "analysis-bench" => { 171 "analysis-bench" => {
168 if matches.contains(["-h", "--help"]) { 172 if matches.contains(["-h", "--help"]) {
@@ -174,7 +178,8 @@ USAGE:
174 rust-analyzer analysis-bench [FLAGS] [OPTIONS] 178 rust-analyzer analysis-bench [FLAGS] [OPTIONS]
175 179
176FLAGS: 180FLAGS:
177 -h, --help Prints help information 181 -h, --help Prints help information
182 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
178 -v, --verbose 183 -v, --verbose
179 184
180OPTIONS: 185OPTIONS:
@@ -201,7 +206,8 @@ ARGS:
201 "exactly one of `--highlight`, `--complete` or `--goto-def` must be set" 206 "exactly one of `--highlight`, `--complete` or `--goto-def` must be set"
202 ), 207 ),
203 }; 208 };
204 Command::Bench { path, what } 209 let load_output_dirs = matches.contains("--load-output-dirs");
210 Command::Bench { path, what, load_output_dirs }
205 } 211 }
206 _ => { 212 _ => {
207 eprintln!( 213 eprintln!(
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index dd15b3458..a744a6695 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -19,19 +19,25 @@ fn main() -> Result<()> {
19 args::Command::Parse { no_dump } => cli::parse(no_dump)?, 19 args::Command::Parse { no_dump } => cli::parse(no_dump)?,
20 args::Command::Symbols => cli::symbols()?, 20 args::Command::Symbols => cli::symbols()?,
21 args::Command::Highlight { rainbow } => cli::highlight(rainbow)?, 21 args::Command::Highlight { rainbow } => cli::highlight(rainbow)?,
22 args::Command::Stats { randomize, memory_usage, only, with_deps, path } => { 22 args::Command::Stats {
23 cli::analysis_stats( 23 randomize,
24 args.verbosity, 24 memory_usage,
25 memory_usage, 25 only,
26 path.as_ref(), 26 with_deps,
27 only.as_ref().map(String::as_ref), 27 path,
28 with_deps, 28 load_output_dirs,
29 randomize, 29 } => cli::analysis_stats(
30 )? 30 args.verbosity,
31 } 31 memory_usage,
32 32 path.as_ref(),
33 args::Command::Bench { path, what } => { 33 only.as_ref().map(String::as_ref),
34 cli::analysis_bench(args.verbosity, path.as_ref(), what)? 34 with_deps,
35 randomize,
36 load_output_dirs,
37 )?,
38
39 args::Command::Bench { path, what, load_output_dirs } => {
40 cli::analysis_bench(args.verbosity, path.as_ref(), what, load_output_dirs)?
35 } 41 }
36 42
37 args::Command::RunServer => run_server()?, 43 args::Command::RunServer => run_server()?,
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 28a23934f..7164b0ade 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -42,12 +42,17 @@ fn rsplit_at_char(s: &str, c: char) -> Result<(&str, &str)> {
42 Ok((&s[..idx], &s[idx + 1..])) 42 Ok((&s[..idx], &s[idx + 1..]))
43} 43}
44 44
45pub fn analysis_bench(verbosity: Verbosity, path: &Path, what: BenchWhat) -> Result<()> { 45pub fn analysis_bench(
46 verbosity: Verbosity,
47 path: &Path,
48 what: BenchWhat,
49 load_output_dirs: bool,
50) -> Result<()> {
46 ra_prof::init(); 51 ra_prof::init();
47 52
48 let start = Instant::now(); 53 let start = Instant::now();
49 eprint!("loading: "); 54 eprint!("loading: ");
50 let (mut host, roots) = load_cargo(path)?; 55 let (mut host, roots) = load_cargo(path, load_output_dirs)?;
51 let db = host.raw_database(); 56 let db = host.raw_database();
52 eprintln!("{:?}\n", start.elapsed()); 57 eprintln!("{:?}\n", start.elapsed());
53 58
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 643c54a9d..27459be8c 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -23,9 +23,10 @@ pub fn analysis_stats(
23 only: Option<&str>, 23 only: Option<&str>,
24 with_deps: bool, 24 with_deps: bool,
25 randomize: bool, 25 randomize: bool,
26 load_output_dirs: bool,
26) -> Result<()> { 27) -> Result<()> {
27 let db_load_time = Instant::now(); 28 let db_load_time = Instant::now();
28 let (mut host, roots) = load_cargo(path)?; 29 let (mut host, roots) = load_cargo(path, load_output_dirs)?;
29 let db = host.raw_database(); 30 let db = host.raw_database();
30 println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed()); 31 println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed());
31 let analysis_time = Instant::now(); 32 let analysis_time = Instant::now();
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 2ce69c9b3..54e2fa1a7 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -1,13 +1,13 @@
1//! Loads a Cargo project into a static instance of analysis, without support 1//! Loads a Cargo project into a static instance of analysis, without support
2//! for incorporating changes. 2//! for incorporating changes.
3 3
4use std::path::Path; 4use std::path::{Path, PathBuf};
5 5
6use anyhow::Result; 6use anyhow::Result;
7use crossbeam_channel::{unbounded, Receiver}; 7use crossbeam_channel::{unbounded, Receiver};
8use ra_db::{CrateGraph, FileId, SourceRootId}; 8use ra_db::{ExternSourceId, FileId, SourceRootId};
9use ra_ide::{AnalysisChange, AnalysisHost}; 9use ra_ide::{AnalysisChange, AnalysisHost};
10use ra_project_model::{get_rustc_cfg_options, PackageRoot, ProjectWorkspace}; 10use ra_project_model::{get_rustc_cfg_options, CargoFeatures, PackageRoot, ProjectWorkspace};
11use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; 11use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
12use rustc_hash::{FxHashMap, FxHashSet}; 12use rustc_hash::{FxHashMap, FxHashSet};
13 13
@@ -22,10 +22,21 @@ fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
22 22
23pub(crate) fn load_cargo( 23pub(crate) fn load_cargo(
24 root: &Path, 24 root: &Path,
25 load_out_dirs_from_check: bool,
25) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { 26) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
26 let root = std::env::current_dir()?.join(root); 27 let root = std::env::current_dir()?.join(root);
27 let ws = ProjectWorkspace::discover(root.as_ref(), &Default::default())?; 28 let ws = ProjectWorkspace::discover(
28 let project_roots = ws.to_roots(); 29 root.as_ref(),
30 &CargoFeatures { load_out_dirs_from_check, ..Default::default() },
31 )?;
32
33 let mut extern_dirs = FxHashSet::default();
34 extern_dirs.extend(ws.out_dirs());
35
36 let mut project_roots = ws.to_roots();
37 project_roots
38 .extend(extern_dirs.iter().map(|path| PackageRoot::new(path.to_path_buf(), false)));
39
29 let (sender, receiver) = unbounded(); 40 let (sender, receiver) = unbounded();
30 let sender = Box::new(move |t| sender.send(t).unwrap()); 41 let sender = Box::new(move |t| sender.send(t).unwrap());
31 let (mut vfs, roots) = Vfs::new( 42 let (mut vfs, roots) = Vfs::new(
@@ -44,24 +55,6 @@ pub(crate) fn load_cargo(
44 Watch(false), 55 Watch(false),
45 ); 56 );
46 57
47 // FIXME: cfg options?
48 let default_cfg_options = {
49 let mut opts = get_rustc_cfg_options();
50 opts.insert_atom("test".into());
51 opts.insert_atom("debug_assertion".into());
52 opts
53 };
54
55 // FIXME: outdirs?
56 let outdirs = FxHashMap::default();
57
58 let crate_graph = ws.to_crate_graph(&default_cfg_options, &outdirs, &mut |path: &Path| {
59 let vfs_file = vfs.load(path);
60 log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
61 vfs_file.map(vfs_file_to_id)
62 });
63 log::debug!("crate graph: {:?}", crate_graph);
64
65 let source_roots = roots 58 let source_roots = roots
66 .iter() 59 .iter()
67 .map(|&vfs_root| { 60 .map(|&vfs_root| {
@@ -74,23 +67,24 @@ pub(crate) fn load_cargo(
74 (source_root_id, project_root) 67 (source_root_id, project_root)
75 }) 68 })
76 .collect::<FxHashMap<_, _>>(); 69 .collect::<FxHashMap<_, _>>();
77 let host = load(&source_roots, crate_graph, &mut vfs, receiver); 70 let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs);
78 Ok((host, source_roots)) 71 Ok((host, source_roots))
79} 72}
80 73
81pub(crate) fn load( 74pub(crate) fn load(
82 source_roots: &FxHashMap<SourceRootId, PackageRoot>, 75 source_roots: &FxHashMap<SourceRootId, PackageRoot>,
83 crate_graph: CrateGraph, 76 ws: ProjectWorkspace,
84 vfs: &mut Vfs, 77 vfs: &mut Vfs,
85 receiver: Receiver<VfsTask>, 78 receiver: Receiver<VfsTask>,
79 extern_dirs: FxHashSet<PathBuf>,
86) -> AnalysisHost { 80) -> AnalysisHost {
87 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); 81 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
88 let mut host = AnalysisHost::new(lru_cap); 82 let mut host = AnalysisHost::new(lru_cap);
89 let mut analysis_change = AnalysisChange::new(); 83 let mut analysis_change = AnalysisChange::new();
90 analysis_change.set_crate_graph(crate_graph);
91 84
92 // wait until Vfs has loaded all roots 85 // wait until Vfs has loaded all roots
93 let mut roots_loaded = FxHashSet::default(); 86 let mut roots_loaded = FxHashSet::default();
87 let mut extern_source_roots = FxHashMap::default();
94 for task in receiver { 88 for task in receiver {
95 vfs.handle_task(task); 89 vfs.handle_task(task);
96 let mut done = false; 90 let mut done = false;
@@ -110,6 +104,11 @@ pub(crate) fn load(
110 source_roots[&source_root_id].path().display().to_string(), 104 source_roots[&source_root_id].path().display().to_string(),
111 ); 105 );
112 106
107 let vfs_root_path = vfs.root2path(root);
108 if extern_dirs.contains(&vfs_root_path) {
109 extern_source_roots.insert(vfs_root_path, ExternSourceId(root.0));
110 }
111
113 let mut file_map = FxHashMap::default(); 112 let mut file_map = FxHashMap::default();
114 for (vfs_file, path, text) in files { 113 for (vfs_file, path, text) in files {
115 let file_id = vfs_file_to_id(vfs_file); 114 let file_id = vfs_file_to_id(vfs_file);
@@ -136,6 +135,23 @@ pub(crate) fn load(
136 } 135 }
137 } 136 }
138 137
138 // FIXME: cfg options?
139 let default_cfg_options = {
140 let mut opts = get_rustc_cfg_options();
141 opts.insert_atom("test".into());
142 opts.insert_atom("debug_assertion".into());
143 opts
144 };
145
146 let crate_graph =
147 ws.to_crate_graph(&default_cfg_options, &extern_source_roots, &mut |path: &Path| {
148 let vfs_file = vfs.load(path);
149 log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
150 vfs_file.map(vfs_file_to_id)
151 });
152 log::debug!("crate graph: {:?}", crate_graph);
153 analysis_change.set_crate_graph(crate_graph);
154
139 host.apply_change(analysis_change); 155 host.apply_change(analysis_change);
140 host 156 host
141} 157}
@@ -149,7 +165,7 @@ mod tests {
149 #[test] 165 #[test]
150 fn test_loading_rust_analyzer() { 166 fn test_loading_rust_analyzer() {
151 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); 167 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
152 let (host, _roots) = load_cargo(path).unwrap(); 168 let (host, _roots) = load_cargo(path, false).unwrap();
153 let n_crates = Crate::all(host.raw_database()).len(); 169 let n_crates = Crate::all(host.raw_database()).len();
154 // RA has quite a few crates, but the exact count doesn't matter 170 // RA has quite a few crates, but the exact count doesn't matter
155 assert!(n_crates > 20); 171 assert!(n_crates > 20);
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 6b9a11a87..103b2b53c 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -48,9 +48,6 @@ pub struct ServerConfig {
48 /// Fine grained feature flags to disable specific features. 48 /// Fine grained feature flags to disable specific features.
49 pub feature_flags: FxHashMap<String, bool>, 49 pub feature_flags: FxHashMap<String, bool>,
50 50
51 /// Fine grained controls for additional `OUT_DIR` env variables
52 pub additional_out_dirs: FxHashMap<String, String>,
53
54 pub rustfmt_args: Vec<String>, 51 pub rustfmt_args: Vec<String>,
55 52
56 /// Cargo feature configurations. 53 /// Cargo feature configurations.
@@ -76,7 +73,6 @@ impl Default for ServerConfig {
76 cargo_watch_all_targets: true, 73 cargo_watch_all_targets: true,
77 with_sysroot: true, 74 with_sysroot: true,
78 feature_flags: FxHashMap::default(), 75 feature_flags: FxHashMap::default(),
79 additional_out_dirs: FxHashMap::default(),
80 cargo_features: Default::default(), 76 cargo_features: Default::default(),
81 rustfmt_args: Vec::new(), 77 rustfmt_args: Vec::new(),
82 vscode_lldb: false, 78 vscode_lldb: false,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 1fefc66aa..a8a5894d2 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -204,7 +204,6 @@ pub fn main_loop(
204 Watch(!config.use_client_watching), 204 Watch(!config.use_client_watching),
205 options, 205 options,
206 feature_flags, 206 feature_flags,
207 config.additional_out_dirs,
208 ) 207 )
209 }; 208 };
210 209
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index 5743471bf..c4244fee2 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -82,7 +82,6 @@ impl WorldState {
82 watch: Watch, 82 watch: Watch,
83 options: Options, 83 options: Options,
84 feature_flags: FeatureFlags, 84 feature_flags: FeatureFlags,
85 additional_out_dirs: FxHashMap<String, String>,
86 ) -> WorldState { 85 ) -> WorldState {
87 let mut change = AnalysisChange::new(); 86 let mut change = AnalysisChange::new();
88 87
@@ -105,11 +104,14 @@ impl WorldState {
105 })); 104 }));
106 } 105 }
107 106
108 let extern_dirs: FxHashSet<_> = 107 let mut extern_dirs = FxHashSet::default();
109 additional_out_dirs.iter().map(|(_, path)| (PathBuf::from(path))).collect(); 108 for ws in workspaces.iter() {
109 extern_dirs.extend(ws.out_dirs());
110 }
111
110 let mut extern_source_roots = FxHashMap::default(); 112 let mut extern_source_roots = FxHashMap::default();
111 113
112 roots.extend(additional_out_dirs.iter().map(|(_, path)| { 114 roots.extend(extern_dirs.iter().map(|path| {
113 let mut filter = RustPackageFilterBuilder::default().set_member(false); 115 let mut filter = RustPackageFilterBuilder::default().set_member(false);
114 for glob in exclude_globs.iter() { 116 for glob in exclude_globs.iter() {
115 filter = filter.exclude(glob.clone()); 117 filter = filter.exclude(glob.clone());
@@ -148,17 +150,9 @@ impl WorldState {
148 vfs_file.map(|f| FileId(f.0)) 150 vfs_file.map(|f| FileId(f.0))
149 }; 151 };
150 152
151 let mut outdirs = FxHashMap::default();
152 for (name, path) in additional_out_dirs {
153 let path = PathBuf::from(&path);
154 if let Some(id) = extern_source_roots.get(&path) {
155 outdirs.insert(name, (id.clone(), path.to_string_lossy().replace("\\", "/")));
156 }
157 }
158
159 workspaces 153 workspaces
160 .iter() 154 .iter()
161 .map(|ws| ws.to_crate_graph(&default_cfg_options, &outdirs, &mut load)) 155 .map(|ws| ws.to_crate_graph(&default_cfg_options, &extern_source_roots, &mut load))
162 .for_each(|graph| { 156 .for_each(|graph| {
163 crate_graph.extend(graph); 157 crate_graph.extend(graph);
164 }); 158 });
diff --git a/editors/code/package.json b/editors/code/package.json
index 48f28b28a..b9e0ffd2b 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -237,11 +237,6 @@
237 "default": true, 237 "default": true,
238 "description": "Whether to ask for permission before downloading any files from the Internet" 238 "description": "Whether to ask for permission before downloading any files from the Internet"
239 }, 239 },
240 "rust-analyzer.additionalOutDirs": {
241 "type": "object",
242 "default": {},
243 "markdownDescription": "Fine grained controls for OUT_DIR `env!(\"OUT_DIR\")` variable. e.g. `{\"foo\":\"/path/to/foo\"}`, "
244 },
245 "rust-analyzer.serverPath": { 240 "rust-analyzer.serverPath": {
246 "type": [ 241 "type": [
247 "null", 242 "null",
@@ -362,6 +357,11 @@
362 }, 357 },
363 "default": [], 358 "default": [],
364 "description": "List of features to activate" 359 "description": "List of features to activate"
360 },
361 "rust-analyzer.cargoFeatures.loadOutDirsFromCheck": {
362 "type": "boolean",
363 "default": false,
364 "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs"
365 } 365 }
366 } 366 }
367 }, 367 },
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index d65454275..08d821dd0 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -42,7 +42,6 @@ export async function createClient(config: Config, serverPath: string): Promise<
42 excludeGlobs: config.excludeGlobs, 42 excludeGlobs: config.excludeGlobs,
43 useClientWatching: config.useClientWatching, 43 useClientWatching: config.useClientWatching,
44 featureFlags: config.featureFlags, 44 featureFlags: config.featureFlags,
45 additionalOutDirs: config.additionalOutDirs,
46 withSysroot: config.withSysroot, 45 withSysroot: config.withSysroot,
47 cargoFeatures: config.cargoFeatures, 46 cargoFeatures: config.cargoFeatures,
48 rustfmtArgs: config.rustfmtArgs, 47 rustfmtArgs: config.rustfmtArgs,
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index bd8096dd6..b45b14bef 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -22,6 +22,7 @@ export interface CargoFeatures {
22 noDefaultFeatures: boolean; 22 noDefaultFeatures: boolean;
23 allFeatures: boolean; 23 allFeatures: boolean;
24 features: string[]; 24 features: string[];
25 loadOutDirsFromCheck: boolean;
25} 26}
26 27
27export const enum UpdatesChannel { 28export const enum UpdatesChannel {
@@ -202,8 +203,8 @@ export class Config {
202 get excludeGlobs() { return this.cfg.get("excludeGlobs") as string[]; } 203 get excludeGlobs() { return this.cfg.get("excludeGlobs") as string[]; }
203 get useClientWatching() { return this.cfg.get("useClientWatching") as boolean; } 204 get useClientWatching() { return this.cfg.get("useClientWatching") as boolean; }
204 get featureFlags() { return this.cfg.get("featureFlags") as Record<string, boolean>; } 205 get featureFlags() { return this.cfg.get("featureFlags") as Record<string, boolean>; }
205 get additionalOutDirs() { return this.cfg.get("additionalOutDirs") as Record<string, string>; }
206 get rustfmtArgs() { return this.cfg.get("rustfmtArgs") as string[]; } 206 get rustfmtArgs() { return this.cfg.get("rustfmtArgs") as string[]; }
207 get loadOutDirsFromCheck() { return this.cfg.get("loadOutDirsFromCheck") as boolean; }
207 208
208 get cargoWatchOptions(): CargoWatchOptions { 209 get cargoWatchOptions(): CargoWatchOptions {
209 return { 210 return {
@@ -219,6 +220,7 @@ export class Config {
219 noDefaultFeatures: this.cfg.get("cargoFeatures.noDefaultFeatures") as boolean, 220 noDefaultFeatures: this.cfg.get("cargoFeatures.noDefaultFeatures") as boolean,
220 allFeatures: this.cfg.get("cargoFeatures.allFeatures") as boolean, 221 allFeatures: this.cfg.get("cargoFeatures.allFeatures") as boolean,
221 features: this.cfg.get("cargoFeatures.features") as string[], 222 features: this.cfg.get("cargoFeatures.features") as string[],
223 loadOutDirsFromCheck: this.cfg.get("cargoFeatures.loadOutDirsFromCheck") as boolean,
222 }; 224 };
223 } 225 }
224 226