diff options
author | Aleksey Kladov <[email protected]> | 2018-09-02 12:46:15 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-09-02 12:46:15 +0100 |
commit | 80be61ed78e8410e013cb94879435d54a4907c30 (patch) | |
tree | 3ab33c902644a3aa14b490be8a7caa9120da7537 /crates/server/src | |
parent | 7fad13de73ded5b8a332c5f50c18671d612bd1e3 (diff) |
project model
Diffstat (limited to 'crates/server/src')
-rw-r--r-- | crates/server/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/server/src/main_loop/mod.rs | 39 | ||||
-rw-r--r-- | crates/server/src/project_model.rs | 54 | ||||
-rw-r--r-- | crates/server/src/req.rs | 8 | ||||
-rw-r--r-- | crates/server/src/server_world.rs | 10 | ||||
-rw-r--r-- | crates/server/src/thread_watcher.rs | 33 | ||||
-rw-r--r-- | crates/server/src/vfs.rs | 28 |
7 files changed, 134 insertions, 39 deletions
diff --git a/crates/server/src/lib.rs b/crates/server/src/lib.rs index 096b94a6d..d874ecf84 100644 --- a/crates/server/src/lib.rs +++ b/crates/server/src/lib.rs | |||
@@ -30,6 +30,7 @@ mod vfs; | |||
30 | mod path_map; | 30 | mod path_map; |
31 | mod server_world; | 31 | mod server_world; |
32 | mod project_model; | 32 | mod project_model; |
33 | mod thread_watcher; | ||
33 | 34 | ||
34 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | 35 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; |
35 | pub use caps::server_capabilities; | 36 | pub use caps::server_capabilities; |
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index ff267fcad..52fc00c9c 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs | |||
@@ -22,6 +22,7 @@ use { | |||
22 | vfs::{self, FileEvent}, | 22 | vfs::{self, FileEvent}, |
23 | server_world::{ServerWorldState, ServerWorld}, | 23 | server_world::{ServerWorldState, ServerWorld}, |
24 | main_loop::subscriptions::{Subscriptions}, | 24 | main_loop::subscriptions::{Subscriptions}, |
25 | project_model::{CargoWorkspace, workspace_loader}, | ||
25 | }; | 26 | }; |
26 | 27 | ||
27 | #[derive(Debug)] | 28 | #[derive(Debug)] |
@@ -37,20 +38,24 @@ pub fn main_loop( | |||
37 | ) -> Result<()> { | 38 | ) -> Result<()> { |
38 | let pool = ThreadPool::new(4); | 39 | let pool = ThreadPool::new(4); |
39 | let (task_sender, task_receiver) = bounded::<Task>(16); | 40 | let (task_sender, task_receiver) = bounded::<Task>(16); |
40 | let (fs_events_receiver, watcher) = vfs::watch(vec![root]); | 41 | let (fs_events_receiver, watcher) = vfs::watch(vec![root.clone()]); |
42 | let (ws_root_sender, ws_receiver, ws_watcher) = workspace_loader(); | ||
43 | ws_root_sender.send(root); | ||
41 | 44 | ||
42 | info!("server initialized, serving requests"); | 45 | info!("server initialized, serving requests"); |
43 | let mut state = ServerWorldState::new(); | 46 | let mut state = ServerWorldState::new(); |
44 | 47 | ||
45 | let mut pending_requests = HashMap::new(); | 48 | let mut pending_requests = HashMap::new(); |
46 | let mut subs = Subscriptions::new(); | 49 | let mut subs = Subscriptions::new(); |
47 | let res = main_loop_inner( | 50 | let main_res = main_loop_inner( |
48 | &pool, | 51 | &pool, |
49 | msg_receriver, | 52 | msg_receriver, |
50 | msg_sender, | 53 | msg_sender, |
51 | task_receiver.clone(), | 54 | task_receiver.clone(), |
52 | task_sender, | 55 | task_sender, |
53 | fs_events_receiver, | 56 | fs_events_receiver, |
57 | ws_root_sender, | ||
58 | ws_receiver, | ||
54 | &mut state, | 59 | &mut state, |
55 | &mut pending_requests, | 60 | &mut pending_requests, |
56 | &mut subs, | 61 | &mut subs, |
@@ -63,10 +68,14 @@ pub fn main_loop( | |||
63 | pool.join(); | 68 | pool.join(); |
64 | info!("...threadpool has finished"); | 69 | info!("...threadpool has finished"); |
65 | 70 | ||
66 | info!("waiting for file watcher to finish..."); | 71 | let vfs_res = watcher.stop(); |
67 | watcher.stop()?; | 72 | let ws_res = ws_watcher.stop(); |
68 | info!("...file watcher has finished"); | 73 | |
69 | res | 74 | main_res?; |
75 | vfs_res?; | ||
76 | ws_res?; | ||
77 | |||
78 | Ok(()) | ||
70 | } | 79 | } |
71 | 80 | ||
72 | fn main_loop_inner( | 81 | fn main_loop_inner( |
@@ -76,6 +85,8 @@ fn main_loop_inner( | |||
76 | task_receiver: Receiver<Task>, | 85 | task_receiver: Receiver<Task>, |
77 | task_sender: Sender<Task>, | 86 | task_sender: Sender<Task>, |
78 | fs_receiver: Receiver<Vec<FileEvent>>, | 87 | fs_receiver: Receiver<Vec<FileEvent>>, |
88 | _ws_roots_sender: Sender<PathBuf>, | ||
89 | ws_receiver: Receiver<Result<CargoWorkspace>>, | ||
79 | state: &mut ServerWorldState, | 90 | state: &mut ServerWorldState, |
80 | pending_requests: &mut HashMap<u64, JobHandle>, | 91 | pending_requests: &mut HashMap<u64, JobHandle>, |
81 | subs: &mut Subscriptions, | 92 | subs: &mut Subscriptions, |
@@ -87,6 +98,7 @@ fn main_loop_inner( | |||
87 | Msg(RawMessage), | 98 | Msg(RawMessage), |
88 | Task(Task), | 99 | Task(Task), |
89 | Fs(Vec<FileEvent>), | 100 | Fs(Vec<FileEvent>), |
101 | Ws(Result<CargoWorkspace>), | ||
90 | FsWatcherDead, | 102 | FsWatcherDead, |
91 | } | 103 | } |
92 | trace!("selecting"); | 104 | trace!("selecting"); |
@@ -100,6 +112,10 @@ fn main_loop_inner( | |||
100 | Some(events) => Event::Fs(events), | 112 | Some(events) => Event::Fs(events), |
101 | None => Event::FsWatcherDead, | 113 | None => Event::FsWatcherDead, |
102 | } | 114 | } |
115 | recv(ws_receiver, ws) => match ws { | ||
116 | None => bail!("workspace watcher died"), | ||
117 | Some(ws) => Event::Ws(ws), | ||
118 | } | ||
103 | }; | 119 | }; |
104 | trace!("selected {:?}", event); | 120 | trace!("selected {:?}", event); |
105 | let mut state_changed = false; | 121 | let mut state_changed = false; |
@@ -111,6 +127,17 @@ fn main_loop_inner( | |||
111 | state.apply_fs_changes(events); | 127 | state.apply_fs_changes(events); |
112 | state_changed = true; | 128 | state_changed = true; |
113 | } | 129 | } |
130 | Event::Ws(ws) => { | ||
131 | match ws { | ||
132 | Ok(ws) => { | ||
133 | let not = RawNotification::new::<req::DidReloadWorkspace>(vec![ws.clone()]); | ||
134 | msg_sender.send(RawMessage::Notification(not)); | ||
135 | state.set_workspaces(vec![ws]); | ||
136 | state_changed = true; | ||
137 | } | ||
138 | Err(e) => warn!("loading workspace failed: {}", e), | ||
139 | } | ||
140 | } | ||
114 | Event::Msg(msg) => { | 141 | Event::Msg(msg) => { |
115 | match msg { | 142 | match msg { |
116 | RawMessage::Request(req) => { | 143 | RawMessage::Request(req) => { |
diff --git a/crates/server/src/project_model.rs b/crates/server/src/project_model.rs index a33b34dd0..1c5954dad 100644 --- a/crates/server/src/project_model.rs +++ b/crates/server/src/project_model.rs | |||
@@ -2,30 +2,35 @@ use std::{ | |||
2 | collections::HashMap, | 2 | collections::HashMap, |
3 | path::{Path, PathBuf}, | 3 | path::{Path, PathBuf}, |
4 | }; | 4 | }; |
5 | use libsyntax2::SmolStr; | ||
6 | use cargo_metadata::{metadata_run, CargoOpt}; | 5 | use cargo_metadata::{metadata_run, CargoOpt}; |
7 | use Result; | 6 | use crossbeam_channel::{bounded, Sender, Receiver}; |
7 | use libsyntax2::SmolStr; | ||
8 | |||
9 | use { | ||
10 | Result, | ||
11 | thread_watcher::ThreadWatcher, | ||
12 | }; | ||
8 | 13 | ||
9 | #[derive(Debug)] | 14 | #[derive(Debug, Serialize, Clone)] |
10 | pub struct CargoWorkspace { | 15 | pub struct CargoWorkspace { |
11 | ws_members: Vec<Package>, | 16 | ws_members: Vec<Package>, |
12 | packages: Vec<PackageData>, | 17 | packages: Vec<PackageData>, |
13 | targets: Vec<TargetData>, | 18 | targets: Vec<TargetData>, |
14 | } | 19 | } |
15 | 20 | ||
16 | #[derive(Clone, Copy, Debug)] | 21 | #[derive(Clone, Copy, Debug, Serialize)] |
17 | pub struct Package(usize); | 22 | pub struct Package(usize); |
18 | #[derive(Clone, Copy, Debug)] | 23 | #[derive(Clone, Copy, Debug, Serialize)] |
19 | pub struct Target(usize); | 24 | pub struct Target(usize); |
20 | 25 | ||
21 | #[derive(Debug)] | 26 | #[derive(Debug, Serialize, Clone)] |
22 | struct PackageData { | 27 | struct PackageData { |
23 | name: SmolStr, | 28 | name: SmolStr, |
24 | manifest: PathBuf, | 29 | manifest: PathBuf, |
25 | targets: Vec<Target> | 30 | targets: Vec<Target> |
26 | } | 31 | } |
27 | 32 | ||
28 | #[derive(Debug)] | 33 | #[derive(Debug, Serialize, Clone)] |
29 | struct TargetData { | 34 | struct TargetData { |
30 | pkg: Package, | 35 | pkg: Package, |
31 | name: SmolStr, | 36 | name: SmolStr, |
@@ -33,7 +38,7 @@ struct TargetData { | |||
33 | kind: TargetKind, | 38 | kind: TargetKind, |
34 | } | 39 | } |
35 | 40 | ||
36 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | 41 | #[derive(Debug, Serialize, Clone, Copy, PartialEq, Eq)] |
37 | pub enum TargetKind { | 42 | pub enum TargetKind { |
38 | Bin, Lib, Example, Test, Bench, Other, | 43 | Bin, Lib, Example, Test, Bench, Other, |
39 | } | 44 | } |
@@ -66,9 +71,10 @@ impl Target { | |||
66 | } | 71 | } |
67 | 72 | ||
68 | impl CargoWorkspace { | 73 | impl CargoWorkspace { |
69 | pub fn from_path(path: &Path) -> Result<CargoWorkspace> { | 74 | pub fn from_cargo_metadata(path: &Path) -> Result<CargoWorkspace> { |
75 | let cargo_toml = find_cargo_toml(path)?; | ||
70 | let meta = metadata_run( | 76 | let meta = metadata_run( |
71 | Some(path), | 77 | Some(cargo_toml.as_path()), |
72 | true, | 78 | true, |
73 | Some(CargoOpt::AllFeatures) | 79 | Some(CargoOpt::AllFeatures) |
74 | ).map_err(|e| format_err!("cargo metadata failed: {}", e))?; | 80 | ).map_err(|e| format_err!("cargo metadata failed: {}", e))?; |
@@ -121,6 +127,21 @@ impl CargoWorkspace { | |||
121 | } | 127 | } |
122 | } | 128 | } |
123 | 129 | ||
130 | fn find_cargo_toml(path: &Path) -> Result<PathBuf> { | ||
131 | if path.ends_with("Cargo.toml") { | ||
132 | return Ok(path.to_path_buf()); | ||
133 | } | ||
134 | let mut curr = Some(path); | ||
135 | while let Some(path) = curr { | ||
136 | let candidate = path.join("Cargo.toml"); | ||
137 | if candidate.exists() { | ||
138 | return Ok(candidate); | ||
139 | } | ||
140 | curr = path.parent(); | ||
141 | } | ||
142 | bail!("can't find Cargo.toml at {}", path.display()) | ||
143 | } | ||
144 | |||
124 | impl TargetKind { | 145 | impl TargetKind { |
125 | fn new(kinds: &[String]) -> TargetKind { | 146 | fn new(kinds: &[String]) -> TargetKind { |
126 | for kind in kinds { | 147 | for kind in kinds { |
@@ -136,3 +157,16 @@ impl TargetKind { | |||
136 | TargetKind::Other | 157 | TargetKind::Other |
137 | } | 158 | } |
138 | } | 159 | } |
160 | |||
161 | pub fn workspace_loader() -> (Sender<PathBuf>, Receiver<Result<CargoWorkspace>>, ThreadWatcher) { | ||
162 | let (path_sender, path_receiver) = bounded::<PathBuf>(16); | ||
163 | let (ws_sender, ws_receiver) = bounded::<Result<CargoWorkspace>>(1); | ||
164 | let thread = ThreadWatcher::spawn("workspace loader", move || { | ||
165 | path_receiver | ||
166 | .into_iter() | ||
167 | .map(|path| CargoWorkspace::from_cargo_metadata(path.as_path())) | ||
168 | .for_each(|it| ws_sender.send(it)) | ||
169 | }); | ||
170 | |||
171 | (path_sender, ws_receiver, thread) | ||
172 | } | ||
diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs index 893cbde81..b9e0c3796 100644 --- a/crates/server/src/req.rs +++ b/crates/server/src/req.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | use std::collections::HashMap; | 1 | use std::collections::HashMap; |
2 | use languageserver_types::{TextDocumentIdentifier, Range, Url, Position, Location}; | 2 | use languageserver_types::{TextDocumentIdentifier, Range, Url, Position, Location}; |
3 | use url_serde; | 3 | use url_serde; |
4 | use project_model::CargoWorkspace; | ||
4 | 5 | ||
5 | pub use languageserver_types::{ | 6 | pub use languageserver_types::{ |
6 | request::*, notification::*, | 7 | request::*, notification::*, |
@@ -167,3 +168,10 @@ pub enum FileSystemEdit { | |||
167 | dst: Url, | 168 | dst: Url, |
168 | } | 169 | } |
169 | } | 170 | } |
171 | |||
172 | pub enum DidReloadWorkspace {} | ||
173 | |||
174 | impl Notification for DidReloadWorkspace { | ||
175 | const METHOD: &'static str = "m/didReloadWorkspace"; | ||
176 | type Params = Vec<CargoWorkspace>; | ||
177 | } | ||
diff --git a/crates/server/src/server_world.rs b/crates/server/src/server_world.rs index d99ef661e..4d5c50428 100644 --- a/crates/server/src/server_world.rs +++ b/crates/server/src/server_world.rs | |||
@@ -2,6 +2,7 @@ use std::{ | |||
2 | fs, | 2 | fs, |
3 | path::{PathBuf, Path}, | 3 | path::{PathBuf, Path}, |
4 | collections::HashMap, | 4 | collections::HashMap, |
5 | sync::Arc, | ||
5 | }; | 6 | }; |
6 | 7 | ||
7 | use languageserver_types::Url; | 8 | use languageserver_types::Url; |
@@ -11,10 +12,12 @@ use { | |||
11 | Result, | 12 | Result, |
12 | path_map::PathMap, | 13 | path_map::PathMap, |
13 | vfs::{FileEvent, FileEventKind}, | 14 | vfs::{FileEvent, FileEventKind}, |
15 | project_model::CargoWorkspace, | ||
14 | }; | 16 | }; |
15 | 17 | ||
16 | #[derive(Debug)] | 18 | #[derive(Debug)] |
17 | pub struct ServerWorldState { | 19 | pub struct ServerWorldState { |
20 | pub workspaces: Arc<Vec<CargoWorkspace>>, | ||
18 | pub analysis_host: AnalysisHost, | 21 | pub analysis_host: AnalysisHost, |
19 | pub path_map: PathMap, | 22 | pub path_map: PathMap, |
20 | pub mem_map: HashMap<FileId, Option<String>>, | 23 | pub mem_map: HashMap<FileId, Option<String>>, |
@@ -22,6 +25,7 @@ pub struct ServerWorldState { | |||
22 | 25 | ||
23 | #[derive(Clone)] | 26 | #[derive(Clone)] |
24 | pub struct ServerWorld { | 27 | pub struct ServerWorld { |
28 | pub workspaces: Arc<Vec<CargoWorkspace>>, | ||
25 | pub analysis: Analysis, | 29 | pub analysis: Analysis, |
26 | pub path_map: PathMap, | 30 | pub path_map: PathMap, |
27 | } | 31 | } |
@@ -29,6 +33,7 @@ pub struct ServerWorld { | |||
29 | impl ServerWorldState { | 33 | impl ServerWorldState { |
30 | pub fn new() -> ServerWorldState { | 34 | pub fn new() -> ServerWorldState { |
31 | ServerWorldState { | 35 | ServerWorldState { |
36 | workspaces: Arc::new(Vec::new()), | ||
32 | analysis_host: AnalysisHost::new(), | 37 | analysis_host: AnalysisHost::new(), |
33 | path_map: PathMap::new(), | 38 | path_map: PathMap::new(), |
34 | mem_map: HashMap::new(), | 39 | mem_map: HashMap::new(), |
@@ -89,9 +94,12 @@ impl ServerWorldState { | |||
89 | self.analysis_host.change_file(file_id, text); | 94 | self.analysis_host.change_file(file_id, text); |
90 | Ok(file_id) | 95 | Ok(file_id) |
91 | } | 96 | } |
92 | 97 | pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) { | |
98 | self.workspaces = Arc::new(ws); | ||
99 | } | ||
93 | pub fn snapshot(&self) -> ServerWorld { | 100 | pub fn snapshot(&self) -> ServerWorld { |
94 | ServerWorld { | 101 | ServerWorld { |
102 | workspaces: Arc::clone(&self.workspaces), | ||
95 | analysis: self.analysis_host.analysis(self.path_map.clone()), | 103 | analysis: self.analysis_host.analysis(self.path_map.clone()), |
96 | path_map: self.path_map.clone() | 104 | path_map: self.path_map.clone() |
97 | } | 105 | } |
diff --git a/crates/server/src/thread_watcher.rs b/crates/server/src/thread_watcher.rs new file mode 100644 index 000000000..98bcdfd6c --- /dev/null +++ b/crates/server/src/thread_watcher.rs | |||
@@ -0,0 +1,33 @@ | |||
1 | use std::thread; | ||
2 | use drop_bomb::DropBomb; | ||
3 | use Result; | ||
4 | |||
5 | pub struct ThreadWatcher { | ||
6 | name: &'static str, | ||
7 | thread: thread::JoinHandle<()>, | ||
8 | bomb: DropBomb, | ||
9 | } | ||
10 | |||
11 | impl ThreadWatcher { | ||
12 | pub fn spawn(name: &'static str, f: impl FnOnce() + Send + 'static) -> ThreadWatcher { | ||
13 | let thread = thread::spawn(f); | ||
14 | ThreadWatcher { | ||
15 | name, | ||
16 | thread, | ||
17 | bomb: DropBomb::new(format!("ThreadWatcher {} was not stopped", name)), | ||
18 | } | ||
19 | } | ||
20 | |||
21 | pub fn stop(mut self) -> Result<()> { | ||
22 | info!("waiting for {} to finish ...", self.name); | ||
23 | let name = self.name; | ||
24 | self.bomb.defuse(); | ||
25 | let res = self.thread.join() | ||
26 | .map_err(|_| format_err!("ThreadWatcher {} died", name)); | ||
27 | match &res { | ||
28 | Ok(()) => info!("... {} terminated with ok", name), | ||
29 | Err(_) => error!("... {} terminated with err", name) | ||
30 | } | ||
31 | res | ||
32 | } | ||
33 | } | ||
diff --git a/crates/server/src/vfs.rs b/crates/server/src/vfs.rs index 2e4319cdb..2acc3f55f 100644 --- a/crates/server/src/vfs.rs +++ b/crates/server/src/vfs.rs | |||
@@ -1,14 +1,14 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | path::PathBuf, | 2 | path::PathBuf, |
3 | thread, | ||
4 | fs, | 3 | fs, |
5 | }; | 4 | }; |
6 | 5 | ||
7 | use crossbeam_channel::{Sender, Receiver, bounded}; | 6 | use crossbeam_channel::{Sender, Receiver, bounded}; |
8 | use drop_bomb::DropBomb; | ||
9 | use walkdir::WalkDir; | 7 | use walkdir::WalkDir; |
10 | 8 | ||
11 | use Result; | 9 | use { |
10 | thread_watcher::ThreadWatcher, | ||
11 | }; | ||
12 | 12 | ||
13 | 13 | ||
14 | #[derive(Debug)] | 14 | #[derive(Debug)] |
@@ -24,26 +24,10 @@ pub enum FileEventKind { | |||
24 | Remove, | 24 | Remove, |
25 | } | 25 | } |
26 | 26 | ||
27 | pub struct Watcher { | 27 | pub fn watch(roots: Vec<PathBuf>) -> (Receiver<Vec<FileEvent>>, ThreadWatcher) { |
28 | thread: thread::JoinHandle<()>, | ||
29 | bomb: DropBomb, | ||
30 | } | ||
31 | |||
32 | impl Watcher { | ||
33 | pub fn stop(mut self) -> Result<()> { | ||
34 | self.bomb.defuse(); | ||
35 | self.thread.join() | ||
36 | .map_err(|_| format_err!("file watcher died")) | ||
37 | } | ||
38 | } | ||
39 | |||
40 | pub fn watch(roots: Vec<PathBuf>) -> (Receiver<Vec<FileEvent>>, Watcher) { | ||
41 | let (sender, receiver) = bounded(16); | 28 | let (sender, receiver) = bounded(16); |
42 | let thread = thread::spawn(move || run(roots, sender)); | 29 | let watcher = ThreadWatcher::spawn("vfs", move || run(roots, sender)); |
43 | (receiver, Watcher { | 30 | (receiver, watcher) |
44 | thread, | ||
45 | bomb: DropBomb::new("Watcher should be stopped explicitly"), | ||
46 | }) | ||
47 | } | 31 | } |
48 | 32 | ||
49 | fn run(roots: Vec<PathBuf>, sender: Sender<Vec<FileEvent>>) { | 33 | fn run(roots: Vec<PathBuf>, sender: Sender<Vec<FileEvent>>) { |