diff options
-rw-r--r-- | crates/server/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/server/src/lib.rs | 9 | ||||
-rw-r--r-- | crates/server/src/project_model.rs | 138 |
3 files changed, 145 insertions, 3 deletions
diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index 2a9374e98..c3e7a6238 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml | |||
@@ -19,6 +19,7 @@ languageserver-types = "0.49.0" | |||
19 | walkdir = "2.2.0" | 19 | walkdir = "2.2.0" |
20 | im = { version = "11.0.1", features = ["arc"] } | 20 | im = { version = "11.0.1", features = ["arc"] } |
21 | text_unit = { version = "0.1.2", features = ["serde"] } | 21 | text_unit = { version = "0.1.2", features = ["serde"] } |
22 | cargo_metadata = "0.6.0" | ||
22 | 23 | ||
23 | libsyntax2 = { path = "../libsyntax2" } | 24 | libsyntax2 = { path = "../libsyntax2" } |
24 | libeditor = { path = "../libeditor" } | 25 | libeditor = { path = "../libeditor" } |
diff --git a/crates/server/src/lib.rs b/crates/server/src/lib.rs index bfa4bc41e..096b94a6d 100644 --- a/crates/server/src/lib.rs +++ b/crates/server/src/lib.rs | |||
@@ -13,12 +13,14 @@ extern crate log; | |||
13 | extern crate drop_bomb; | 13 | extern crate drop_bomb; |
14 | extern crate url_serde; | 14 | extern crate url_serde; |
15 | extern crate walkdir; | 15 | extern crate walkdir; |
16 | extern crate im; | ||
17 | extern crate relative_path; | ||
18 | extern crate cargo_metadata; | ||
19 | |||
20 | extern crate gen_lsp_server; | ||
16 | extern crate libeditor; | 21 | extern crate libeditor; |
17 | extern crate libanalysis; | 22 | extern crate libanalysis; |
18 | extern crate libsyntax2; | 23 | extern crate libsyntax2; |
19 | extern crate gen_lsp_server; | ||
20 | extern crate im; | ||
21 | extern crate relative_path; | ||
22 | 24 | ||
23 | mod caps; | 25 | mod caps; |
24 | pub mod req; | 26 | pub mod req; |
@@ -27,6 +29,7 @@ mod main_loop; | |||
27 | mod vfs; | 29 | mod vfs; |
28 | mod path_map; | 30 | mod path_map; |
29 | mod server_world; | 31 | mod server_world; |
32 | mod project_model; | ||
30 | 33 | ||
31 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | 34 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; |
32 | pub use caps::server_capabilities; | 35 | 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 000000000..a33b34dd0 --- /dev/null +++ b/crates/server/src/project_model.rs | |||
@@ -0,0 +1,138 @@ | |||
1 | use std::{ | ||
2 | collections::HashMap, | ||
3 | path::{Path, PathBuf}, | ||
4 | }; | ||
5 | use libsyntax2::SmolStr; | ||
6 | use cargo_metadata::{metadata_run, CargoOpt}; | ||
7 | use Result; | ||
8 | |||
9 | #[derive(Debug)] | ||
10 | pub struct CargoWorkspace { | ||
11 | ws_members: Vec<Package>, | ||
12 | packages: Vec<PackageData>, | ||
13 | targets: Vec<TargetData>, | ||
14 | } | ||
15 | |||
16 | #[derive(Clone, Copy, Debug)] | ||
17 | pub struct Package(usize); | ||
18 | #[derive(Clone, Copy, Debug)] | ||
19 | pub struct Target(usize); | ||
20 | |||
21 | #[derive(Debug)] | ||
22 | struct PackageData { | ||
23 | name: SmolStr, | ||
24 | manifest: PathBuf, | ||
25 | targets: Vec<Target> | ||
26 | } | ||
27 | |||
28 | #[derive(Debug)] | ||
29 | struct TargetData { | ||
30 | pkg: Package, | ||
31 | name: SmolStr, | ||
32 | root: PathBuf, | ||
33 | kind: TargetKind, | ||
34 | } | ||
35 | |||
36 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | ||
37 | pub enum TargetKind { | ||
38 | Bin, Lib, Example, Test, Bench, Other, | ||
39 | } | ||
40 | |||
41 | impl Package { | ||
42 | pub fn name(self, ws: &CargoWorkspace) -> &str { | ||
43 | ws.pkg(self).name.as_str() | ||
44 | } | ||
45 | pub fn manifest(self, ws: &CargoWorkspace) -> &Path { | ||
46 | ws.pkg(self).manifest.as_path() | ||
47 | } | ||
48 | pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item=Target> + 'a { | ||
49 | ws.pkg(self).targets.iter().cloned() | ||
50 | } | ||
51 | } | ||
52 | |||
53 | impl Target { | ||
54 | pub fn pkg(self, ws: &CargoWorkspace) -> Package { | ||
55 | ws.tgt(self).pkg | ||
56 | } | ||
57 | pub fn name(self, ws: &CargoWorkspace) -> &str { | ||
58 | ws.tgt(self).name.as_str() | ||
59 | } | ||
60 | pub fn root(self, ws: &CargoWorkspace) -> &Path { | ||
61 | ws.tgt(self).root.as_path() | ||
62 | } | ||
63 | pub fn kind(self, ws: &CargoWorkspace) -> TargetKind { | ||
64 | ws.tgt(self).kind | ||
65 | } | ||
66 | } | ||
67 | |||
68 | impl CargoWorkspace { | ||
69 | pub fn from_path(path: &Path) -> Result<CargoWorkspace> { | ||
70 | let meta = metadata_run( | ||
71 | Some(path), | ||
72 | true, | ||
73 | Some(CargoOpt::AllFeatures) | ||
74 | ).map_err(|e| format_err!("cargo metadata failed: {}", e))?; | ||
75 | let mut pkg_by_id = HashMap::new(); | ||
76 | let mut packages = Vec::new(); | ||
77 | let mut targets = Vec::new(); | ||
78 | for meta_pkg in meta.packages { | ||
79 | let pkg = Package(packages.len()); | ||
80 | pkg_by_id.insert(meta_pkg.id.clone(), pkg); | ||
81 | let mut pkg_data = PackageData { | ||
82 | name: meta_pkg.name.into(), | ||
83 | manifest: PathBuf::from(meta_pkg.manifest_path), | ||
84 | targets: Vec::new(), | ||
85 | }; | ||
86 | for meta_tgt in meta_pkg.targets { | ||
87 | let tgt = Target(targets.len()); | ||
88 | targets.push(TargetData { | ||
89 | pkg, | ||
90 | name: meta_tgt.name.into(), | ||
91 | root: PathBuf::from(meta_tgt.src_path), | ||
92 | kind: TargetKind::new(meta_tgt.kind.as_slice()), | ||
93 | }); | ||
94 | pkg_data.targets.push(tgt); | ||
95 | } | ||
96 | packages.push(pkg_data) | ||
97 | } | ||
98 | let ws_members = meta.workspace_members | ||
99 | .iter() | ||
100 | .map(|it| pkg_by_id[&it.raw]) | ||
101 | .collect(); | ||
102 | |||
103 | Ok(CargoWorkspace { packages, targets, ws_members }) | ||
104 | } | ||
105 | pub fn packages<'a>(&'a self) -> impl Iterator<Item=Package> + 'a { | ||
106 | (0..self.packages.len()).map(Package) | ||
107 | } | ||
108 | pub fn ws_members<'a>(&'a self) -> impl Iterator<Item=Package> + 'a { | ||
109 | self.ws_members.iter().cloned() | ||
110 | } | ||
111 | pub fn target_by_roo(&self, root: &Path) -> Option<Target> { | ||
112 | self.packages() | ||
113 | .filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)) | ||
114 | .next() | ||
115 | } | ||
116 | fn pkg(&self, pkg: Package) -> &PackageData { | ||
117 | &self.packages[pkg.0] | ||
118 | } | ||
119 | fn tgt(&self, tgt: Target) -> &TargetData { | ||
120 | &self.targets[tgt.0] | ||
121 | } | ||
122 | } | ||
123 | |||
124 | impl TargetKind { | ||
125 | fn new(kinds: &[String]) -> TargetKind { | ||
126 | for kind in kinds { | ||
127 | return match kind.as_str() { | ||
128 | "bin" => TargetKind::Bin, | ||
129 | "test" => TargetKind::Test, | ||
130 | "bench" => TargetKind::Bench, | ||
131 | "example" => TargetKind::Example, | ||
132 | _ if kind.contains("lib") => TargetKind::Lib, | ||
133 | _ => continue, | ||
134 | } | ||
135 | } | ||
136 | TargetKind::Other | ||
137 | } | ||
138 | } | ||