From d7524556375f2b37e61fe85f17c0e4940e9e4d40 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 2 Sep 2018 11:37:16 +0300 Subject: [PATCH] introduce project model --- crates/server/Cargo.toml | 1 + crates/server/src/lib.rs | 9 +- crates/server/src/project_model.rs | 138 +++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 crates/server/src/project_model.rs diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index 2a9374e9840..c3e7a623858 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml @@ -19,6 +19,7 @@ languageserver-types = "0.49.0" walkdir = "2.2.0" im = { version = "11.0.1", features = ["arc"] } text_unit = { version = "0.1.2", features = ["serde"] } +cargo_metadata = "0.6.0" libsyntax2 = { path = "../libsyntax2" } libeditor = { path = "../libeditor" } diff --git a/crates/server/src/lib.rs b/crates/server/src/lib.rs index bfa4bc41e89..096b94a6d5b 100644 --- a/crates/server/src/lib.rs +++ b/crates/server/src/lib.rs @@ -13,12 +13,14 @@ extern crate log; extern crate drop_bomb; extern crate url_serde; extern crate walkdir; +extern crate im; +extern crate relative_path; +extern crate cargo_metadata; + +extern crate gen_lsp_server; extern crate libeditor; extern crate libanalysis; extern crate libsyntax2; -extern crate gen_lsp_server; -extern crate im; -extern crate relative_path; mod caps; pub mod req; @@ -27,6 +29,7 @@ mod main_loop; mod vfs; mod path_map; mod server_world; +mod project_model; pub type Result = ::std::result::Result; pub use caps::server_capabilities; diff --git a/crates/server/src/project_model.rs b/crates/server/src/project_model.rs new file mode 100644 index 00000000000..a33b34dd0fc --- /dev/null +++ b/crates/server/src/project_model.rs @@ -0,0 +1,138 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; +use libsyntax2::SmolStr; +use cargo_metadata::{metadata_run, CargoOpt}; +use Result; + +#[derive(Debug)] +pub struct CargoWorkspace { + ws_members: Vec, + packages: Vec, + targets: Vec, +} + +#[derive(Clone, Copy, Debug)] +pub struct Package(usize); +#[derive(Clone, Copy, Debug)] +pub struct Target(usize); + +#[derive(Debug)] +struct PackageData { + name: SmolStr, + manifest: PathBuf, + targets: Vec +} + +#[derive(Debug)] +struct TargetData { + pkg: Package, + name: SmolStr, + root: PathBuf, + kind: TargetKind, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum TargetKind { + Bin, Lib, Example, Test, Bench, Other, +} + +impl Package { + pub fn name(self, ws: &CargoWorkspace) -> &str { + ws.pkg(self).name.as_str() + } + pub fn manifest(self, ws: &CargoWorkspace) -> &Path { + ws.pkg(self).manifest.as_path() + } + pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator + 'a { + ws.pkg(self).targets.iter().cloned() + } +} + +impl Target { + pub fn pkg(self, ws: &CargoWorkspace) -> Package { + ws.tgt(self).pkg + } + pub fn name(self, ws: &CargoWorkspace) -> &str { + ws.tgt(self).name.as_str() + } + pub fn root(self, ws: &CargoWorkspace) -> &Path { + ws.tgt(self).root.as_path() + } + pub fn kind(self, ws: &CargoWorkspace) -> TargetKind { + ws.tgt(self).kind + } +} + +impl CargoWorkspace { + pub fn from_path(path: &Path) -> Result { + let meta = metadata_run( + Some(path), + true, + Some(CargoOpt::AllFeatures) + ).map_err(|e| format_err!("cargo metadata failed: {}", e))?; + let mut pkg_by_id = HashMap::new(); + let mut packages = Vec::new(); + let mut targets = Vec::new(); + for meta_pkg in meta.packages { + let pkg = Package(packages.len()); + pkg_by_id.insert(meta_pkg.id.clone(), pkg); + let mut pkg_data = PackageData { + name: meta_pkg.name.into(), + manifest: PathBuf::from(meta_pkg.manifest_path), + targets: Vec::new(), + }; + for meta_tgt in meta_pkg.targets { + let tgt = Target(targets.len()); + targets.push(TargetData { + pkg, + name: meta_tgt.name.into(), + root: PathBuf::from(meta_tgt.src_path), + kind: TargetKind::new(meta_tgt.kind.as_slice()), + }); + pkg_data.targets.push(tgt); + } + packages.push(pkg_data) + } + let ws_members = meta.workspace_members + .iter() + .map(|it| pkg_by_id[&it.raw]) + .collect(); + + Ok(CargoWorkspace { packages, targets, ws_members }) + } + pub fn packages<'a>(&'a self) -> impl Iterator + 'a { + (0..self.packages.len()).map(Package) + } + pub fn ws_members<'a>(&'a self) -> impl Iterator + 'a { + self.ws_members.iter().cloned() + } + pub fn target_by_roo(&self, root: &Path) -> Option { + self.packages() + .filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)) + .next() + } + fn pkg(&self, pkg: Package) -> &PackageData { + &self.packages[pkg.0] + } + fn tgt(&self, tgt: Target) -> &TargetData { + &self.targets[tgt.0] + } +} + +impl TargetKind { + fn new(kinds: &[String]) -> TargetKind { + for kind in kinds { + return match kind.as_str() { + "bin" => TargetKind::Bin, + "test" => TargetKind::Test, + "bench" => TargetKind::Bench, + "example" => TargetKind::Example, + _ if kind.contains("lib") => TargetKind::Lib, + _ => continue, + } + } + TargetKind::Other + } +}