diff options
Diffstat (limited to 'crates/project_model/src')
-rw-r--r-- | crates/project_model/src/cargo_workspace.rs | 36 | ||||
-rw-r--r-- | crates/project_model/src/sysroot.rs | 4 | ||||
-rw-r--r-- | crates/project_model/src/workspace.rs | 89 |
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)] |
125 | pub enum DepKind { | 125 | pub 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 | ||
134 | impl DepKind { | 134 | impl 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 | ||
5 | use std::{collections::VecDeque, fmt, fs, path::Path, process::Command}; | 5 | use std::{collections::VecDeque, fmt, fs, path::Path, process::Command}; |
6 | 6 | ||
7 | use anyhow::{Context, Result}; | 7 | use anyhow::{format_err, Context, Result}; |
8 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; | 8 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; |
9 | use cargo_workspace::DepKind; | 9 | use cargo_workspace::DepKind; |
10 | use cfg::CfgOptions; | 10 | use 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 | ||
54 | impl fmt::Debug for ProjectWorkspace { | 66 | impl 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 | ||
522 | fn 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 | |||
477 | fn handle_rustc_crates( | 564 | fn 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>, |