aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2019-02-05 22:10:49 +0000
committerFlorian Diebold <[email protected]>2019-02-09 10:15:25 +0000
commitfcd615e4b76264b4fff7b5be454787bb6a4252ea (patch)
tree112d17480bd065551a9d18c21ab51326f3e3a295 /crates/ra_lsp_server
parent34398a8756b56c323d3b4b2ef32fbca32d88a105 (diff)
Extract project model to separate crate
Diffstat (limited to 'crates/ra_lsp_server')
-rw-r--r--crates/ra_lsp_server/Cargo.toml2
-rw-r--r--crates/ra_lsp_server/src/project_model.rs42
-rw-r--r--crates/ra_lsp_server/src/project_model/cargo_workspace.rs171
-rw-r--r--crates/ra_lsp_server/src/project_model/sysroot.rs137
4 files changed, 4 insertions, 348 deletions
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index bb92747f2..f46d77893 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -19,7 +19,6 @@ url_serde = "0.2.0"
19lsp-types = "0.55.0" 19lsp-types = "0.55.0"
20walkdir = "2.2.7" 20walkdir = "2.2.7"
21im = "12.0.0" 21im = "12.0.0"
22cargo_metadata = "0.7.0"
23rustc-hash = "1.0" 22rustc-hash = "1.0"
24parking_lot = "0.7.0" 23parking_lot = "0.7.0"
25 24
@@ -30,6 +29,7 @@ ra_ide_api = { path = "../ra_ide_api" }
30ra_arena = { path = "../ra_arena" } 29ra_arena = { path = "../ra_arena" }
31gen_lsp_server = { path = "../gen_lsp_server" } 30gen_lsp_server = { path = "../gen_lsp_server" }
32ra_vfs = { path = "../ra_vfs" } 31ra_vfs = { path = "../ra_vfs" }
32ra_project_model = { path = "../ra_project_model" }
33 33
34[dev-dependencies] 34[dev-dependencies]
35tempfile = "3" 35tempfile = "3"
diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs
index fd5875a0a..6800eb138 100644
--- a/crates/ra_lsp_server/src/project_model.rs
+++ b/crates/ra_lsp_server/src/project_model.rs
@@ -1,34 +1,13 @@
1mod cargo_workspace; 1use std::path::PathBuf;
2mod sysroot;
3 2
4use std::path::{Path, PathBuf};
5
6use failure::bail;
7use thread_worker::{WorkerHandle, Worker}; 3use thread_worker::{WorkerHandle, Worker};
8 4
9use crate::Result; 5use crate::Result;
10 6
11pub use crate::project_model::{ 7pub use ra_project_model::{
12 cargo_workspace::{CargoWorkspace, Package, Target, TargetKind}, 8 ProjectWorkspace, CargoWorkspace, Package, Target, TargetKind, Sysroot,
13 sysroot::Sysroot,
14}; 9};
15 10
16#[derive(Debug, Clone)]
17pub struct ProjectWorkspace {
18 pub(crate) cargo: CargoWorkspace,
19 pub(crate) sysroot: Sysroot,
20}
21
22impl ProjectWorkspace {
23 pub fn discover(path: &Path) -> Result<ProjectWorkspace> {
24 let cargo_toml = find_cargo_toml(path)?;
25 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml)?;
26 let sysroot = Sysroot::discover(&cargo_toml)?;
27 let res = ProjectWorkspace { cargo, sysroot };
28 Ok(res)
29 }
30}
31
32pub fn workspace_loader() -> (Worker<PathBuf, Result<ProjectWorkspace>>, WorkerHandle) { 11pub fn workspace_loader() -> (Worker<PathBuf, Result<ProjectWorkspace>>, WorkerHandle) {
33 thread_worker::spawn::<PathBuf, Result<ProjectWorkspace>, _>( 12 thread_worker::spawn::<PathBuf, Result<ProjectWorkspace>, _>(
34 "workspace loader", 13 "workspace loader",
@@ -42,18 +21,3 @@ pub fn workspace_loader() -> (Worker<PathBuf, Result<ProjectWorkspace>>, WorkerH
42 }, 21 },
43 ) 22 )
44} 23}
45
46fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
47 if path.ends_with("Cargo.toml") {
48 return Ok(path.to_path_buf());
49 }
50 let mut curr = Some(path);
51 while let Some(path) = curr {
52 let candidate = path.join("Cargo.toml");
53 if candidate.exists() {
54 return Ok(candidate);
55 }
56 curr = path.parent();
57 }
58 bail!("can't find Cargo.toml at {}", path.display())
59}
diff --git a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs b/crates/ra_lsp_server/src/project_model/cargo_workspace.rs
deleted file mode 100644
index 3b76389d2..000000000
--- a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs
+++ /dev/null
@@ -1,171 +0,0 @@
1use std::path::{Path, PathBuf};
2
3use cargo_metadata::{MetadataCommand, CargoOpt};
4use ra_syntax::SmolStr;
5use ra_arena::{Arena, RawId, impl_arena_id};
6use rustc_hash::FxHashMap;
7use failure::format_err;
8
9use crate::Result;
10
11/// `CargoWorksapce` represents the logical structure of, well, a Cargo
12/// workspace. It pretty closely mirrors `cargo metadata` output.
13///
14/// Note that internally, rust analyzer uses a differnet structure:
15/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
16/// while this knows about `Pacakges` & `Targets`: purely cargo-related
17/// concepts.
18#[derive(Debug, Clone)]
19pub struct CargoWorkspace {
20 packages: Arena<Package, PackageData>,
21 targets: Arena<Target, TargetData>,
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
25pub struct Package(RawId);
26impl_arena_id!(Package);
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
29pub struct Target(RawId);
30impl_arena_id!(Target);
31
32#[derive(Debug, Clone)]
33struct PackageData {
34 name: SmolStr,
35 manifest: PathBuf,
36 targets: Vec<Target>,
37 is_member: bool,
38 dependencies: Vec<PackageDependency>,
39}
40
41#[derive(Debug, Clone)]
42pub struct PackageDependency {
43 pub pkg: Package,
44 pub name: SmolStr,
45}
46
47#[derive(Debug, Clone)]
48struct TargetData {
49 pkg: Package,
50 name: SmolStr,
51 root: PathBuf,
52 kind: TargetKind,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum TargetKind {
57 Bin,
58 Lib,
59 Example,
60 Test,
61 Bench,
62 Other,
63}
64
65impl TargetKind {
66 fn new(kinds: &[String]) -> TargetKind {
67 for kind in kinds {
68 return match kind.as_str() {
69 "bin" => TargetKind::Bin,
70 "test" => TargetKind::Test,
71 "bench" => TargetKind::Bench,
72 "example" => TargetKind::Example,
73 _ if kind.contains("lib") => TargetKind::Lib,
74 _ => continue,
75 };
76 }
77 TargetKind::Other
78 }
79}
80
81impl Package {
82 pub fn name(self, ws: &CargoWorkspace) -> &str {
83 ws.packages[self].name.as_str()
84 }
85 pub fn root(self, ws: &CargoWorkspace) -> &Path {
86 ws.packages[self].manifest.parent().unwrap()
87 }
88 pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a {
89 ws.packages[self].targets.iter().cloned()
90 }
91 #[allow(unused)]
92 pub fn is_member(self, ws: &CargoWorkspace) -> bool {
93 ws.packages[self].is_member
94 }
95 pub fn dependencies<'a>(
96 self,
97 ws: &'a CargoWorkspace,
98 ) -> impl Iterator<Item = &'a PackageDependency> + 'a {
99 ws.packages[self].dependencies.iter()
100 }
101}
102
103impl Target {
104 pub fn package(self, ws: &CargoWorkspace) -> Package {
105 ws.targets[self].pkg
106 }
107 pub fn name(self, ws: &CargoWorkspace) -> &str {
108 ws.targets[self].name.as_str()
109 }
110 pub fn root(self, ws: &CargoWorkspace) -> &Path {
111 ws.targets[self].root.as_path()
112 }
113 pub fn kind(self, ws: &CargoWorkspace) -> TargetKind {
114 ws.targets[self].kind
115 }
116}
117
118impl CargoWorkspace {
119 pub fn from_cargo_metadata(cargo_toml: &Path) -> Result<CargoWorkspace> {
120 let mut meta = MetadataCommand::new();
121 meta.manifest_path(cargo_toml).features(CargoOpt::AllFeatures);
122 if let Some(parent) = cargo_toml.parent() {
123 meta.current_dir(parent);
124 }
125 let meta = meta.exec().map_err(|e| format_err!("cargo metadata failed: {}", e))?;
126 let mut pkg_by_id = FxHashMap::default();
127 let mut packages = Arena::default();
128 let mut targets = Arena::default();
129
130 let ws_members = &meta.workspace_members;
131
132 for meta_pkg in meta.packages {
133 let is_member = ws_members.contains(&meta_pkg.id);
134 let pkg = packages.alloc(PackageData {
135 name: meta_pkg.name.into(),
136 manifest: meta_pkg.manifest_path.clone(),
137 targets: Vec::new(),
138 is_member,
139 dependencies: Vec::new(),
140 });
141 let pkg_data = &mut packages[pkg];
142 pkg_by_id.insert(meta_pkg.id.clone(), pkg);
143 for meta_tgt in meta_pkg.targets {
144 let tgt = targets.alloc(TargetData {
145 pkg,
146 name: meta_tgt.name.into(),
147 root: meta_tgt.src_path.clone(),
148 kind: TargetKind::new(meta_tgt.kind.as_slice()),
149 });
150 pkg_data.targets.push(tgt);
151 }
152 }
153 let resolve = meta.resolve.expect("metadata executed with deps");
154 for node in resolve.nodes {
155 let source = pkg_by_id[&node.id];
156 for dep_node in node.deps {
157 let dep =
158 PackageDependency { name: dep_node.name.into(), pkg: pkg_by_id[&dep_node.pkg] };
159 packages[source].dependencies.push(dep);
160 }
161 }
162
163 Ok(CargoWorkspace { packages, targets })
164 }
165 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + 'a {
166 self.packages.iter().map(|(id, _pkg)| id)
167 }
168 pub fn target_by_root(&self, root: &Path) -> Option<Target> {
169 self.packages().filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)).next()
170 }
171}
diff --git a/crates/ra_lsp_server/src/project_model/sysroot.rs b/crates/ra_lsp_server/src/project_model/sysroot.rs
deleted file mode 100644
index 49210ac7a..000000000
--- a/crates/ra_lsp_server/src/project_model/sysroot.rs
+++ /dev/null
@@ -1,137 +0,0 @@
1use std::{
2 path::{Path, PathBuf},
3 process::Command,
4};
5
6use ra_syntax::SmolStr;
7use ra_arena::{Arena, RawId, impl_arena_id};
8
9use crate::Result;
10
11#[derive(Debug, Clone)]
12pub struct Sysroot {
13 crates: Arena<SysrootCrate, SysrootCrateData>,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct SysrootCrate(RawId);
18impl_arena_id!(SysrootCrate);
19
20#[derive(Debug, Clone)]
21struct SysrootCrateData {
22 name: SmolStr,
23 root: PathBuf,
24 deps: Vec<SysrootCrate>,
25}
26
27impl Sysroot {
28 pub(crate) fn std(&self) -> Option<SysrootCrate> {
29 self.by_name("std")
30 }
31
32 pub(crate) fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + 'a {
33 self.crates.iter().map(|(id, _data)| id)
34 }
35
36 pub(super) fn discover(cargo_toml: &Path) -> Result<Sysroot> {
37 let rustc_output = Command::new("rustc")
38 .current_dir(cargo_toml.parent().unwrap())
39 .args(&["--print", "sysroot"])
40 .output()?;
41 if !rustc_output.status.success() {
42 failure::bail!("failed to locate sysroot")
43 }
44 let stdout = String::from_utf8(rustc_output.stdout)?;
45 let sysroot_path = Path::new(stdout.trim());
46 let src = sysroot_path.join("lib/rustlib/src/rust/src");
47 if !src.exists() {
48 failure::bail!(
49 "can't load standard library from sysroot\n\
50 {:?}\n\
51 try running `rustup component add rust-src`",
52 src,
53 );
54 }
55
56 let mut sysroot = Sysroot { crates: Arena::default() };
57 for name in SYSROOT_CRATES.trim().lines() {
58 let root = src.join(format!("lib{}", name)).join("lib.rs");
59 if root.exists() {
60 sysroot.crates.alloc(SysrootCrateData {
61 name: name.into(),
62 root,
63 deps: Vec::new(),
64 });
65 }
66 }
67 if let Some(std) = sysroot.std() {
68 for dep in STD_DEPS.trim().lines() {
69 if let Some(dep) = sysroot.by_name(dep) {
70 sysroot.crates[std].deps.push(dep)
71 }
72 }
73 }
74 Ok(sysroot)
75 }
76
77 fn by_name(&self, name: &str) -> Option<SysrootCrate> {
78 self.crates.iter().find(|(_id, data)| data.name == name).map(|(id, _data)| id)
79 }
80}
81
82impl SysrootCrate {
83 pub(crate) fn name(self, sysroot: &Sysroot) -> &SmolStr {
84 &sysroot.crates[self].name
85 }
86 pub(crate) fn root(self, sysroot: &Sysroot) -> &Path {
87 sysroot.crates[self].root.as_path()
88 }
89 pub(crate) fn root_dir(self, sysroot: &Sysroot) -> &Path {
90 self.root(sysroot).parent().unwrap()
91 }
92 pub(crate) fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator<Item = SysrootCrate> + 'a {
93 sysroot.crates[self].deps.iter().map(|&it| it)
94 }
95}
96
97const SYSROOT_CRATES: &str = "
98std
99core
100alloc
101collections
102libc
103panic_unwind
104proc_macro
105rustc_unicode
106std_unicode
107test
108alloc_jemalloc
109alloc_system
110compiler_builtins
111getopts
112panic_unwind
113panic_abort
114rand
115term
116unwind
117build_helper
118rustc_asan
119rustc_lsan
120rustc_msan
121rustc_tsan
122syntax";
123
124const STD_DEPS: &str = "
125alloc
126alloc_jemalloc
127alloc_system
128core
129panic_abort
130rand
131compiler_builtins
132unwind
133rustc_asan
134rustc_lsan
135rustc_msan
136rustc_tsan
137build_helper";