aboutsummaryrefslogtreecommitdiff
path: root/crates/project_model/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/project_model/src')
-rw-r--r--crates/project_model/src/cargo_workspace.rs36
-rw-r--r--crates/project_model/src/sysroot.rs4
-rw-r--r--crates/project_model/src/workspace.rs89
3 files changed, 112 insertions, 17 deletions
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index ad705c752..a8fee4f08 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -121,7 +121,7 @@ pub struct PackageDependency {
121 pub kind: DepKind, 121 pub kind: DepKind,
122} 122}
123 123
124#[derive(Debug, Clone, Eq, PartialEq)] 124#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
125pub enum DepKind { 125pub enum DepKind {
126 /// Available to the library, binary, and dev targets in the package (but not the build script). 126 /// Available to the library, binary, and dev targets in the package (but not the build script).
127 Normal, 127 Normal,
@@ -132,17 +132,23 @@ pub enum DepKind {
132} 132}
133 133
134impl DepKind { 134impl DepKind {
135 fn new(list: &[cargo_metadata::DepKindInfo]) -> Self { 135 fn iter(list: &[cargo_metadata::DepKindInfo]) -> impl Iterator<Item = Self> + '_ {
136 let mut dep_kinds = Vec::new();
137 if list.is_empty() {
138 dep_kinds.push(Self::Normal);
139 }
136 for info in list { 140 for info in list {
137 match info.kind { 141 let kind = match info.kind {
138 cargo_metadata::DependencyKind::Normal => return Self::Normal, 142 cargo_metadata::DependencyKind::Normal => Self::Normal,
139 cargo_metadata::DependencyKind::Development => return Self::Dev, 143 cargo_metadata::DependencyKind::Development => Self::Dev,
140 cargo_metadata::DependencyKind::Build => return Self::Build, 144 cargo_metadata::DependencyKind::Build => Self::Build,
141 cargo_metadata::DependencyKind::Unknown => continue, 145 cargo_metadata::DependencyKind::Unknown => continue,
142 } 146 };
147 dep_kinds.push(kind);
143 } 148 }
144 149 dep_kinds.sort_unstable();
145 Self::Normal 150 dep_kinds.dedup();
151 dep_kinds.into_iter()
146 } 152 }
147} 153}
148 154
@@ -317,7 +323,11 @@ impl CargoWorkspace {
317 } 323 }
318 }; 324 };
319 node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg)); 325 node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg));
320 for dep_node in node.deps { 326 for (dep_node, kind) in node
327 .deps
328 .iter()
329 .flat_map(|dep| DepKind::iter(&dep.dep_kinds).map(move |kind| (dep, kind)))
330 {
321 let pkg = match pkg_by_id.get(&dep_node.pkg) { 331 let pkg = match pkg_by_id.get(&dep_node.pkg) {
322 Some(&pkg) => pkg, 332 Some(&pkg) => pkg,
323 None => { 333 None => {
@@ -328,11 +338,7 @@ impl CargoWorkspace {
328 continue; 338 continue;
329 } 339 }
330 }; 340 };
331 let dep = PackageDependency { 341 let dep = PackageDependency { name: dep_node.name.clone(), pkg, kind };
332 name: dep_node.name,
333 pkg,
334 kind: DepKind::new(&dep_node.dep_kinds),
335 };
336 packages[source].dependencies.push(dep); 342 packages[source].dependencies.push(dep);
337 } 343 }
338 packages[source].active_features.extend(node.features); 344 packages[source].active_features.extend(node.features);
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs
index 3b0ff506d..4e39d6dd3 100644
--- a/crates/project_model/src/sysroot.rs
+++ b/crates/project_model/src/sysroot.rs
@@ -50,7 +50,9 @@ impl Sysroot {
50 50
51 pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> { 51 pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
52 log::debug!("Discovering sysroot for {}", cargo_toml.display()); 52 log::debug!("Discovering sysroot for {}", cargo_toml.display());
53 let current_dir = cargo_toml.parent().unwrap(); 53 let current_dir = cargo_toml.parent().ok_or_else(|| {
54 format_err!("Failed to find the parent directory for {}", cargo_toml.display())
55 })?;
54 let sysroot_dir = discover_sysroot_dir(current_dir)?; 56 let sysroot_dir = discover_sysroot_dir(current_dir)?;
55 let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?; 57 let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?;
56 let res = Sysroot::load(&sysroot_src_dir)?; 58 let res = Sysroot::load(&sysroot_src_dir)?;
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 607e62ea5..84990075f 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -4,7 +4,7 @@
4 4
5use std::{collections::VecDeque, fmt, fs, path::Path, process::Command}; 5use std::{collections::VecDeque, fmt, fs, path::Path, process::Command};
6 6
7use anyhow::{Context, Result}; 7use anyhow::{format_err, Context, Result};
8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; 8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
9use cargo_workspace::DepKind; 9use cargo_workspace::DepKind;
10use cfg::CfgOptions; 10use cfg::CfgOptions;
@@ -49,6 +49,18 @@ pub enum ProjectWorkspace {
49 }, 49 },
50 /// Project workspace was manually specified using a `rust-project.json` file. 50 /// Project workspace was manually specified using a `rust-project.json` file.
51 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> }, 51 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
52
53 // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
54 // That's not the end user experience we should strive for.
55 // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
56 // That needs some changes on the salsa-level though.
57 // In particular, we should split the unified CrateGraph (which currently has maximal durability) into proper crate graph, and a set of ad hoc roots (with minimal durability).
58 // Then, we need to hide the graph behind the queries such that most queries look only at the proper crate graph, and fall back to ad hoc roots only if there's no results.
59 // After this, we should be able to tweak the logic in reload.rs to add newly opened files, which don't belong to any existing crates, to the set of the detached files.
60 // //
61 /// Project with a set of disjoint files, not belonging to any particular workspace.
62 /// Backed by basic sysroot crates for basic completion and highlighting.
63 DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
52} 64}
53 65
54impl fmt::Debug for ProjectWorkspace { 66impl fmt::Debug for ProjectWorkspace {
@@ -75,6 +87,12 @@ impl fmt::Debug for ProjectWorkspace {
75 debug_struct.field("n_rustc_cfg", &rustc_cfg.len()); 87 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
76 debug_struct.finish() 88 debug_struct.finish()
77 } 89 }
90 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
91 .debug_struct("DetachedFiles")
92 .field("n_files", &files.len())
93 .field("n_sysroot_crates", &sysroot.crates().len())
94 .field("n_rustc_cfg", &rustc_cfg.len())
95 .finish(),
78 } 96 }
79 } 97 }
80} 98}
@@ -165,6 +183,14 @@ impl ProjectWorkspace {
165 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) 183 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
166 } 184 }
167 185
186 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
187 let sysroot = Sysroot::discover(
188 &detached_files.first().ok_or_else(|| format_err!("No detached files to load"))?,
189 )?;
190 let rustc_cfg = rustc_cfg::get(None, None);
191 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
192 }
193
168 /// Returns the roots for the current `ProjectWorkspace` 194 /// Returns the roots for the current `ProjectWorkspace`
169 /// The return type contains the path and whether or not 195 /// The return type contains the path and whether or not
170 /// the root is a member of the current workspace 196 /// the root is a member of the current workspace
@@ -224,6 +250,19 @@ impl ProjectWorkspace {
224 }) 250 })
225 })) 251 }))
226 .collect(), 252 .collect(),
253 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
254 .into_iter()
255 .map(|detached_file| PackageRoot {
256 is_member: true,
257 include: vec![detached_file.clone()],
258 exclude: Vec::new(),
259 })
260 .chain(sysroot.crates().map(|krate| PackageRoot {
261 is_member: false,
262 include: vec![sysroot[krate].root_dir().to_path_buf()],
263 exclude: Vec::new(),
264 }))
265 .collect(),
227 } 266 }
228 } 267 }
229 268
@@ -234,6 +273,9 @@ impl ProjectWorkspace {
234 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len()); 273 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len());
235 cargo.packages().len() + sysroot.crates().len() + rustc_package_len 274 cargo.packages().len() + sysroot.crates().len() + rustc_package_len
236 } 275 }
276 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
277 sysroot.crates().len() + files.len()
278 }
237 } 279 }
238 } 280 }
239 281
@@ -267,6 +309,9 @@ impl ProjectWorkspace {
267 rustc, 309 rustc,
268 rustc.as_ref().zip(build_data).and_then(|(it, map)| map.get(it.workspace_root())), 310 rustc.as_ref().zip(build_data).and_then(|(it, map)| map.get(it.workspace_root())),
269 ), 311 ),
312 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
313 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
314 }
270 }; 315 };
271 if crate_graph.patch_cfg_if() { 316 if crate_graph.patch_cfg_if() {
272 log::debug!("Patched std to depend on cfg-if") 317 log::debug!("Patched std to depend on cfg-if")
@@ -474,6 +519,48 @@ fn cargo_to_crate_graph(
474 crate_graph 519 crate_graph
475} 520}
476 521
522fn detached_files_to_crate_graph(
523 rustc_cfg: Vec<CfgFlag>,
524 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
525 detached_files: &[AbsPathBuf],
526 sysroot: &Sysroot,
527) -> CrateGraph {
528 let _p = profile::span("detached_files_to_crate_graph");
529 let mut crate_graph = CrateGraph::default();
530 let (public_deps, _libproc_macro) =
531 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
532
533 let mut cfg_options = CfgOptions::default();
534 cfg_options.extend(rustc_cfg);
535
536 for detached_file in detached_files {
537 let file_id = match load(&detached_file) {
538 Some(file_id) => file_id,
539 None => {
540 log::error!("Failed to load detached file {:?}", detached_file);
541 continue;
542 }
543 };
544 let display_name = detached_file
545 .file_stem()
546 .and_then(|os_str| os_str.to_str())
547 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
548 let detached_file_crate = crate_graph.add_crate_root(
549 file_id,
550 Edition::Edition2018,
551 display_name,
552 cfg_options.clone(),
553 Env::default(),
554 Vec::new(),
555 );
556
557 for (name, krate) in public_deps.iter() {
558 add_dep(&mut crate_graph, detached_file_crate, name.clone(), *krate);
559 }
560 }
561 crate_graph
562}
563
477fn handle_rustc_crates( 564fn handle_rustc_crates(
478 rustc_workspace: &CargoWorkspace, 565 rustc_workspace: &CargoWorkspace,
479 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 566 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,