diff options
Diffstat (limited to 'crates/ra_lsp_server/src/main_loop.rs')
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 147 |
1 files changed, 58 insertions, 89 deletions
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index eab82ee85..d2f16ea97 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -1,7 +1,10 @@ | |||
1 | mod handlers; | 1 | mod handlers; |
2 | mod subscriptions; | 2 | mod subscriptions; |
3 | 3 | ||
4 | use std::path::PathBuf; | 4 | use std::{ |
5 | path::PathBuf, | ||
6 | sync::Arc, | ||
7 | }; | ||
5 | 8 | ||
6 | use crossbeam_channel::{unbounded, select, Receiver, Sender}; | 9 | use crossbeam_channel::{unbounded, select, Receiver, Sender}; |
7 | use gen_lsp_server::{ | 10 | use gen_lsp_server::{ |
@@ -9,8 +12,8 @@ use gen_lsp_server::{ | |||
9 | }; | 12 | }; |
10 | use languageserver_types::NumberOrString; | 13 | use languageserver_types::NumberOrString; |
11 | use ra_analysis::{Canceled, FileId, LibraryData}; | 14 | use ra_analysis::{Canceled, FileId, LibraryData}; |
15 | use ra_vfs::{VfsTask}; | ||
12 | use rayon; | 16 | use rayon; |
13 | use thread_worker::Worker; | ||
14 | use threadpool::ThreadPool; | 17 | use threadpool::ThreadPool; |
15 | use rustc_hash::FxHashSet; | 18 | use rustc_hash::FxHashSet; |
16 | use serde::{de::DeserializeOwned, Serialize}; | 19 | use serde::{de::DeserializeOwned, Serialize}; |
@@ -19,10 +22,9 @@ use failure_derive::Fail; | |||
19 | 22 | ||
20 | use crate::{ | 23 | use crate::{ |
21 | main_loop::subscriptions::Subscriptions, | 24 | main_loop::subscriptions::Subscriptions, |
22 | project_model::{workspace_loader, CargoWorkspace}, | 25 | project_model::{workspace_loader}, |
23 | req, | 26 | req, |
24 | server_world::{ServerWorld, ServerWorldState}, | 27 | server_world::{ServerWorld, ServerWorldState}, |
25 | vfs::{self, FileEvent}, | ||
26 | Result, | 28 | Result, |
27 | }; | 29 | }; |
28 | 30 | ||
@@ -50,32 +52,42 @@ enum Task { | |||
50 | 52 | ||
51 | pub fn main_loop( | 53 | pub fn main_loop( |
52 | internal_mode: bool, | 54 | internal_mode: bool, |
53 | root: PathBuf, | 55 | ws_root: PathBuf, |
54 | publish_decorations: bool, | 56 | publish_decorations: bool, |
55 | msg_receiver: &Receiver<RawMessage>, | 57 | msg_receiver: &Receiver<RawMessage>, |
56 | msg_sender: &Sender<RawMessage>, | 58 | msg_sender: &Sender<RawMessage>, |
57 | ) -> Result<()> { | 59 | ) -> Result<()> { |
58 | let pool = ThreadPool::new(8); | 60 | let pool = ThreadPool::new(8); |
59 | let (task_sender, task_receiver) = unbounded::<Task>(); | 61 | let (task_sender, task_receiver) = unbounded::<Task>(); |
60 | let (fs_worker, fs_watcher) = vfs::roots_loader(); | ||
61 | let (ws_worker, ws_watcher) = workspace_loader(); | 62 | let (ws_worker, ws_watcher) = workspace_loader(); |
62 | 63 | ||
64 | ws_worker.send(ws_root.clone()); | ||
65 | // FIXME: support dynamic workspace loading. | ||
66 | let workspaces = match ws_worker.recv().unwrap() { | ||
67 | Ok(ws) => vec![ws], | ||
68 | Err(e) => { | ||
69 | log::warn!("loading workspace failed: {}", e); | ||
70 | Vec::new() | ||
71 | } | ||
72 | }; | ||
73 | ws_worker.shutdown(); | ||
74 | ws_watcher | ||
75 | .shutdown() | ||
76 | .map_err(|_| format_err!("ws watcher died"))?; | ||
77 | let mut state = ServerWorldState::new(ws_root.clone(), workspaces); | ||
78 | |||
63 | log::info!("server initialized, serving requests"); | 79 | log::info!("server initialized, serving requests"); |
64 | let mut state = ServerWorldState::default(); | ||
65 | 80 | ||
66 | let mut pending_requests = FxHashSet::default(); | 81 | let mut pending_requests = FxHashSet::default(); |
67 | let mut subs = Subscriptions::new(); | 82 | let mut subs = Subscriptions::new(); |
68 | let main_res = main_loop_inner( | 83 | let main_res = main_loop_inner( |
69 | internal_mode, | 84 | internal_mode, |
70 | publish_decorations, | 85 | publish_decorations, |
71 | root, | ||
72 | &pool, | 86 | &pool, |
73 | msg_sender, | 87 | msg_sender, |
74 | msg_receiver, | 88 | msg_receiver, |
75 | task_sender, | 89 | task_sender, |
76 | task_receiver.clone(), | 90 | task_receiver.clone(), |
77 | fs_worker, | ||
78 | ws_worker, | ||
79 | &mut state, | 91 | &mut state, |
80 | &mut pending_requests, | 92 | &mut pending_requests, |
81 | &mut subs, | 93 | &mut subs, |
@@ -88,12 +100,11 @@ pub fn main_loop( | |||
88 | drop(pool); | 100 | drop(pool); |
89 | log::info!("...threadpool has finished"); | 101 | log::info!("...threadpool has finished"); |
90 | 102 | ||
91 | let fs_res = fs_watcher.stop(); | 103 | let vfs = Arc::try_unwrap(state.vfs).expect("all snapshots should be dead"); |
92 | let ws_res = ws_watcher.stop(); | 104 | let vfs_res = vfs.into_inner().shutdown(); |
93 | 105 | ||
94 | main_res?; | 106 | main_res?; |
95 | fs_res.map_err(|_| format_err!("fs watcher died"))?; | 107 | vfs_res.map_err(|_| format_err!("fs watcher died"))?; |
96 | ws_res.map_err(|_| format_err!("ws watcher died"))?; | ||
97 | 108 | ||
98 | Ok(()) | 109 | Ok(()) |
99 | } | 110 | } |
@@ -101,28 +112,22 @@ pub fn main_loop( | |||
101 | fn main_loop_inner( | 112 | fn main_loop_inner( |
102 | internal_mode: bool, | 113 | internal_mode: bool, |
103 | publish_decorations: bool, | 114 | publish_decorations: bool, |
104 | ws_root: PathBuf, | ||
105 | pool: &ThreadPool, | 115 | pool: &ThreadPool, |
106 | msg_sender: &Sender<RawMessage>, | 116 | msg_sender: &Sender<RawMessage>, |
107 | msg_receiver: &Receiver<RawMessage>, | 117 | msg_receiver: &Receiver<RawMessage>, |
108 | task_sender: Sender<Task>, | 118 | task_sender: Sender<Task>, |
109 | task_receiver: Receiver<Task>, | 119 | task_receiver: Receiver<Task>, |
110 | fs_worker: Worker<PathBuf, (PathBuf, Vec<FileEvent>)>, | ||
111 | ws_worker: Worker<PathBuf, Result<CargoWorkspace>>, | ||
112 | state: &mut ServerWorldState, | 120 | state: &mut ServerWorldState, |
113 | pending_requests: &mut FxHashSet<u64>, | 121 | pending_requests: &mut FxHashSet<u64>, |
114 | subs: &mut Subscriptions, | 122 | subs: &mut Subscriptions, |
115 | ) -> Result<()> { | 123 | ) -> Result<()> { |
116 | let (libdata_sender, libdata_receiver) = unbounded(); | 124 | let (libdata_sender, libdata_receiver) = unbounded(); |
117 | ws_worker.send(ws_root.clone()); | ||
118 | fs_worker.send(ws_root.clone()); | ||
119 | loop { | 125 | loop { |
120 | #[derive(Debug)] | 126 | #[derive(Debug)] |
121 | enum Event { | 127 | enum Event { |
122 | Msg(RawMessage), | 128 | Msg(RawMessage), |
123 | Task(Task), | 129 | Task(Task), |
124 | Fs(PathBuf, Vec<FileEvent>), | 130 | Vfs(VfsTask), |
125 | Ws(Result<CargoWorkspace>), | ||
126 | Lib(LibraryData), | 131 | Lib(LibraryData), |
127 | } | 132 | } |
128 | log::trace!("selecting"); | 133 | log::trace!("selecting"); |
@@ -132,77 +137,20 @@ fn main_loop_inner( | |||
132 | None => bail!("client exited without shutdown"), | 137 | None => bail!("client exited without shutdown"), |
133 | }, | 138 | }, |
134 | recv(task_receiver, task) => Event::Task(task.unwrap()), | 139 | recv(task_receiver, task) => Event::Task(task.unwrap()), |
135 | recv(fs_worker.out, events) => match events { | 140 | recv(state.vfs.read().task_receiver(), task) => match task { |
136 | None => bail!("roots watcher died"), | 141 | None => bail!("vfs died"), |
137 | Some((pb, events)) => Event::Fs(pb, events), | 142 | Some(task) => Event::Vfs(task), |
138 | } | ||
139 | recv(ws_worker.out, ws) => match ws { | ||
140 | None => bail!("workspace watcher died"), | ||
141 | Some(ws) => Event::Ws(ws), | ||
142 | } | 143 | } |
143 | recv(libdata_receiver, data) => Event::Lib(data.unwrap()) | 144 | recv(libdata_receiver, data) => Event::Lib(data.unwrap()) |
144 | }; | 145 | }; |
146 | log::info!("{:?}", event); | ||
145 | let mut state_changed = false; | 147 | let mut state_changed = false; |
146 | match event { | 148 | match event { |
147 | Event::Task(task) => on_task(task, msg_sender, pending_requests), | 149 | Event::Task(task) => on_task(task, msg_sender, pending_requests), |
148 | Event::Fs(root, events) => { | 150 | Event::Vfs(task) => { |
149 | log::info!("fs change, {}, {} events", root.display(), events.len()); | 151 | state.vfs.write().handle_task(task); |
150 | if root == ws_root { | ||
151 | state.apply_fs_changes(events); | ||
152 | } else { | ||
153 | let (files, resolver) = state.events_to_files(events); | ||
154 | let sender = libdata_sender.clone(); | ||
155 | pool.execute(move || { | ||
156 | let start = ::std::time::Instant::now(); | ||
157 | log::info!("indexing {} ... ", root.display()); | ||
158 | let data = LibraryData::prepare(files, resolver); | ||
159 | log::info!("indexed {:?} {}", start.elapsed(), root.display()); | ||
160 | sender.send(data); | ||
161 | }); | ||
162 | } | ||
163 | state_changed = true; | 152 | state_changed = true; |
164 | } | 153 | } |
165 | Event::Ws(ws) => match ws { | ||
166 | Ok(ws) => { | ||
167 | let workspaces = vec![ws]; | ||
168 | feedback(internal_mode, "workspace loaded", msg_sender); | ||
169 | for ws in workspaces.iter() { | ||
170 | // Add each library as constant input. If library is | ||
171 | // within the workspace, don't treat it as a library. | ||
172 | // | ||
173 | // HACK: If source roots are nested, pick the outer one. | ||
174 | |||
175 | let mut roots = ws | ||
176 | .packages() | ||
177 | .filter(|pkg| !pkg.is_member(ws)) | ||
178 | .filter_map(|pkg| { | ||
179 | let root = pkg.root(ws).to_path_buf(); | ||
180 | if root.starts_with(&ws_root) { | ||
181 | None | ||
182 | } else { | ||
183 | Some(root) | ||
184 | } | ||
185 | }) | ||
186 | .collect::<Vec<_>>(); | ||
187 | roots.sort_by_key(|it| it.as_os_str().len()); | ||
188 | let unique = roots | ||
189 | .iter() | ||
190 | .enumerate() | ||
191 | .filter(|&(idx, long)| { | ||
192 | !roots[..idx].iter().any(|short| long.starts_with(short)) | ||
193 | }) | ||
194 | .map(|(_idx, root)| root); | ||
195 | |||
196 | for root in unique { | ||
197 | log::debug!("sending root, {}", root.display()); | ||
198 | fs_worker.send(root.to_owned()); | ||
199 | } | ||
200 | } | ||
201 | state.set_workspaces(workspaces); | ||
202 | state_changed = true; | ||
203 | } | ||
204 | Err(e) => log::warn!("loading workspace failed: {}", e), | ||
205 | }, | ||
206 | Event::Lib(lib) => { | 154 | Event::Lib(lib) => { |
207 | feedback(internal_mode, "library loaded", msg_sender); | 155 | feedback(internal_mode, "library loaded", msg_sender); |
208 | state.add_lib(lib); | 156 | state.add_lib(lib); |
@@ -234,6 +182,21 @@ fn main_loop_inner( | |||
234 | }, | 182 | }, |
235 | }; | 183 | }; |
236 | 184 | ||
185 | for lib in state.process_changes() { | ||
186 | let (root, files) = lib; | ||
187 | let sender = libdata_sender.clone(); | ||
188 | pool.execute(move || { | ||
189 | let start = ::std::time::Instant::now(); | ||
190 | log::info!("indexing {:?} ... ", root); | ||
191 | let data = LibraryData::prepare(root, files); | ||
192 | log::info!("indexed {:?} {:?}", start.elapsed(), root); | ||
193 | sender.send(data); | ||
194 | }); | ||
195 | } | ||
196 | if state.roots_to_scan == 0 { | ||
197 | feedback(internal_mode, "workspace loaded", msg_sender); | ||
198 | } | ||
199 | |||
237 | if state_changed { | 200 | if state_changed { |
238 | update_file_notifications_on_threadpool( | 201 | update_file_notifications_on_threadpool( |
239 | pool, | 202 | pool, |
@@ -336,8 +299,13 @@ fn on_notification( | |||
336 | let path = uri | 299 | let path = uri |
337 | .to_file_path() | 300 | .to_file_path() |
338 | .map_err(|()| format_err!("invalid uri: {}", uri))?; | 301 | .map_err(|()| format_err!("invalid uri: {}", uri))?; |
339 | let file_id = state.add_mem_file(path, params.text_document.text); | 302 | if let Some(file_id) = state |
340 | subs.add_sub(file_id); | 303 | .vfs |
304 | .write() | ||
305 | .add_file_overlay(&path, params.text_document.text) | ||
306 | { | ||
307 | subs.add_sub(FileId(file_id.0)); | ||
308 | } | ||
341 | return Ok(()); | 309 | return Ok(()); |
342 | } | 310 | } |
343 | Err(not) => not, | 311 | Err(not) => not, |
@@ -353,7 +321,7 @@ fn on_notification( | |||
353 | .pop() | 321 | .pop() |
354 | .ok_or_else(|| format_err!("empty changes"))? | 322 | .ok_or_else(|| format_err!("empty changes"))? |
355 | .text; | 323 | .text; |
356 | state.change_mem_file(path.as_path(), text)?; | 324 | state.vfs.write().change_file_overlay(path.as_path(), text); |
357 | return Ok(()); | 325 | return Ok(()); |
358 | } | 326 | } |
359 | Err(not) => not, | 327 | Err(not) => not, |
@@ -364,8 +332,9 @@ fn on_notification( | |||
364 | let path = uri | 332 | let path = uri |
365 | .to_file_path() | 333 | .to_file_path() |
366 | .map_err(|()| format_err!("invalid uri: {}", uri))?; | 334 | .map_err(|()| format_err!("invalid uri: {}", uri))?; |
367 | let file_id = state.remove_mem_file(path.as_path())?; | 335 | if let Some(file_id) = state.vfs.write().remove_file_overlay(path.as_path()) { |
368 | subs.remove_sub(file_id); | 336 | subs.remove_sub(FileId(file_id.0)); |
337 | } | ||
369 | let params = req::PublishDiagnosticsParams { | 338 | let params = req::PublishDiagnosticsParams { |
370 | uri, | 339 | uri, |
371 | diagnostics: Vec::new(), | 340 | diagnostics: Vec::new(), |