diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 7d9faa43c2..a48d05b98a 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs @@ -48,6 +48,9 @@ impl CrateGraph { assert!(prev.is_none()); crate_id } + //FIXME: check that we don't have cycles here. + // Just a simple depth first search from `to` should work, + // the graph is small. pub fn add_dep(&mut self, from: CrateId, to: CrateId) { self.arena.get_mut(&from).unwrap().add_dep(to) } diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs index 22495f49cb..5da71b9f54 100644 --- a/crates/ra_lsp_server/src/project_model.rs +++ b/crates/ra_lsp_server/src/project_model.rs @@ -1,6 +1,5 @@ use std::path::{Path, PathBuf}; -use serde_derive::Serialize; use cargo_metadata::{metadata_run, CargoOpt}; use ra_syntax::SmolStr; use rustc_hash::{FxHashMap, FxHashSet}; @@ -11,15 +10,22 @@ use crate::{ thread_watcher::{ThreadWatcher, Worker}, }; +/// `CargoWorksapce` represents the logical structure of, well, a Cargo +/// workspace. It pretty closely mirrors `cargo metadata` output. +/// +/// Note that internally, rust analyzer uses a differnet structure: +/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates, +/// while this knows about `Pacakges` & `Targets`: purely cargo-related +/// concepts. #[derive(Debug, Clone)] pub struct CargoWorkspace { packages: Vec, targets: Vec, } -#[derive(Clone, Copy, Debug, Serialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Package(usize); -#[derive(Clone, Copy, Debug, Serialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Target(usize); #[derive(Debug, Clone)] @@ -62,6 +68,9 @@ impl Package { pub fn is_member(self, ws: &CargoWorkspace) -> bool { ws.pkg(self).is_member } + pub fn dependencies<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator + 'a { + ws.pkg(self).dependencies.iter().cloned() + } } impl Target { diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index c3f89ad5f0..f2d602dc78 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs @@ -13,7 +13,7 @@ use failure::{bail, format_err}; use crate::{ path_map::{PathMap, Root}, - project_model::CargoWorkspace, + project_model::{CargoWorkspace, TargetKind}, vfs::{FileEvent, FileEventKind}, Result, }; @@ -142,17 +142,34 @@ impl ServerWorldState { } pub fn set_workspaces(&mut self, ws: Vec) { let mut crate_graph = CrateGraph::default(); - ws.iter() - .flat_map(|ws| { - ws.packages() - .flat_map(move |pkg| pkg.targets(ws)) - .map(move |tgt| tgt.root(ws)) - }) - .for_each(|root| { - if let Some(file_id) = self.path_map.get_id(root) { - crate_graph.add_crate_root(file_id); + let mut pkg_to_lib_crate = FxHashMap::default(); + let mut pkg_crates = FxHashMap::default(); + for ws in ws.iter() { + for pkg in ws.packages() { + for tgt in pkg.targets(ws) { + let root = tgt.root(ws); + if let Some(file_id) = self.path_map.get_id(root) { + let crate_id = crate_graph.add_crate_root(file_id); + if tgt.kind(ws) == TargetKind::Lib { + pkg_to_lib_crate.insert(pkg, crate_id); + } + pkg_crates + .entry(pkg) + .or_insert_with(Vec::new) + .push(crate_id); + } } - }); + } + for pkg in ws.packages() { + for dep in pkg.dependencies(ws) { + if let Some(&to) = pkg_to_lib_crate.get(&dep) { + for &from in pkg_crates.get(&pkg).into_iter().flatten() { + crate_graph.add_dep(from, to); + } + } + } + } + } self.workspaces = Arc::new(ws); let mut change = AnalysisChange::new(); change.set_crate_graph(crate_graph);