aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server/src/main_loop.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-12-08 16:30:35 +0000
committerAleksey Kladov <[email protected]>2018-12-08 16:30:35 +0000
commit4cbc902fcc9de79893779582dac01351d1137c7f (patch)
tree22b7e547f86270c6f238484fb43dbb523f2e5846 /crates/ra_lsp_server/src/main_loop.rs
parent93c0b7d794e55e255b102478e2c482c3037d0acb (diff)
grand module rename
Diffstat (limited to 'crates/ra_lsp_server/src/main_loop.rs')
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs495
1 files changed, 495 insertions, 0 deletions
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
new file mode 100644
index 000000000..0e1878906
--- /dev/null
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -0,0 +1,495 @@
1mod handlers;
2mod subscriptions;
3
4use std::path::PathBuf;
5
6use crossbeam_channel::{unbounded, select, Receiver, Sender};
7use gen_lsp_server::{
8 handle_shutdown, ErrorCode, RawMessage, RawNotification, RawRequest, RawResponse,
9};
10use languageserver_types::NumberOrString;
11use ra_analysis::{Canceled, FileId, LibraryData};
12use rayon::{self, ThreadPool};
13use rustc_hash::FxHashSet;
14use serde::{de::DeserializeOwned, Serialize};
15use failure::{format_err, bail};
16use failure_derive::Fail;
17
18use crate::{
19 main_loop::subscriptions::Subscriptions,
20 project_model::{workspace_loader, CargoWorkspace},
21 req,
22 server_world::{ServerWorld, ServerWorldState},
23 thread_watcher::Worker,
24 vfs::{self, FileEvent},
25 Result,
26};
27
28#[derive(Debug, Fail)]
29#[fail(
30 display = "Language Server request failed with {}. ({})",
31 code, message
32)]
33pub struct LspError {
34 pub code: i32,
35 pub message: String,
36}
37
38impl LspError {
39 pub fn new(code: i32, message: String) -> LspError {
40 LspError { code, message }
41 }
42}
43
44#[derive(Debug)]
45enum Task {
46 Respond(RawResponse),
47 Notify(RawNotification),
48}
49
50pub fn main_loop(
51 internal_mode: bool,
52 root: PathBuf,
53 publish_decorations: bool,
54 msg_receiver: &Receiver<RawMessage>,
55 msg_sender: &Sender<RawMessage>,
56) -> Result<()> {
57 let pool = rayon::ThreadPoolBuilder::new()
58 .num_threads(4)
59 .panic_handler(|_| log::error!("thread panicked :("))
60 .build()
61 .unwrap();
62 let (task_sender, task_receiver) = unbounded::<Task>();
63 let (fs_worker, fs_watcher) = vfs::roots_loader();
64 let (ws_worker, ws_watcher) = workspace_loader();
65
66 log::info!("server initialized, serving requests");
67 let mut state = ServerWorldState::default();
68
69 let mut pending_requests = FxHashSet::default();
70 let mut subs = Subscriptions::new();
71 let main_res = main_loop_inner(
72 internal_mode,
73 publish_decorations,
74 root,
75 &pool,
76 msg_sender,
77 msg_receiver,
78 task_sender,
79 task_receiver.clone(),
80 fs_worker,
81 ws_worker,
82 &mut state,
83 &mut pending_requests,
84 &mut subs,
85 );
86
87 log::info!("waiting for tasks to finish...");
88 task_receiver.for_each(|task| on_task(task, msg_sender, &mut pending_requests));
89 log::info!("...tasks have finished");
90 log::info!("joining threadpool...");
91 drop(pool);
92 log::info!("...threadpool has finished");
93
94 let fs_res = fs_watcher.stop();
95 let ws_res = ws_watcher.stop();
96
97 main_res?;
98 fs_res?;
99 ws_res?;
100
101 Ok(())
102}
103
104fn main_loop_inner(
105 internal_mode: bool,
106 publish_decorations: bool,
107 ws_root: PathBuf,
108 pool: &ThreadPool,
109 msg_sender: &Sender<RawMessage>,
110 msg_receiver: &Receiver<RawMessage>,
111 task_sender: Sender<Task>,
112 task_receiver: Receiver<Task>,
113 fs_worker: Worker<PathBuf, (PathBuf, Vec<FileEvent>)>,
114 ws_worker: Worker<PathBuf, Result<CargoWorkspace>>,
115 state: &mut ServerWorldState,
116 pending_requests: &mut FxHashSet<u64>,
117 subs: &mut Subscriptions,
118) -> Result<()> {
119 let (libdata_sender, libdata_receiver) = unbounded();
120 ws_worker.send(ws_root.clone());
121 fs_worker.send(ws_root.clone());
122 loop {
123 #[derive(Debug)]
124 enum Event {
125 Msg(RawMessage),
126 Task(Task),
127 Fs(PathBuf, Vec<FileEvent>),
128 Ws(Result<CargoWorkspace>),
129 Lib(LibraryData),
130 }
131 log::trace!("selecting");
132 let event = select! {
133 recv(msg_receiver, msg) => match msg {
134 Some(msg) => Event::Msg(msg),
135 None => bail!("client exited without shutdown"),
136 },
137 recv(task_receiver, task) => Event::Task(task.unwrap()),
138 recv(fs_worker.out, events) => match events {
139 None => bail!("roots watcher died"),
140 Some((pb, events)) => Event::Fs(pb, events),
141 }
142 recv(ws_worker.out, ws) => match ws {
143 None => bail!("workspace watcher died"),
144 Some(ws) => Event::Ws(ws),
145 }
146 recv(libdata_receiver, data) => Event::Lib(data.unwrap())
147 };
148 let mut state_changed = false;
149 match event {
150 Event::Task(task) => on_task(task, msg_sender, pending_requests),
151 Event::Fs(root, events) => {
152 log::info!("fs change, {}, {} events", root.display(), events.len());
153 if root == ws_root {
154 state.apply_fs_changes(events);
155 } else {
156 let (files, resolver) = state.events_to_files(events);
157 let sender = libdata_sender.clone();
158 pool.spawn(move || {
159 let start = ::std::time::Instant::now();
160 log::info!("indexing {} ... ", root.display());
161 let data = LibraryData::prepare(files, resolver);
162 log::info!("indexed {:?} {}", start.elapsed(), root.display());
163 sender.send(data);
164 });
165 }
166 state_changed = true;
167 }
168 Event::Ws(ws) => match ws {
169 Ok(ws) => {
170 let workspaces = vec![ws];
171 feedback(internal_mode, "workspace loaded", msg_sender);
172 for ws in workspaces.iter() {
173 // Add each library as constant input. If library is
174 // within the workspace, don't treat it as a library.
175 //
176 // HACK: If source roots are nested, pick the outer one.
177
178 let mut roots = ws
179 .packages()
180 .filter(|pkg| !pkg.is_member(ws))
181 .filter_map(|pkg| {
182 let root = pkg.root(ws).to_path_buf();
183 if root.starts_with(&ws_root) {
184 None
185 } else {
186 Some(root)
187 }
188 })
189 .collect::<Vec<_>>();
190 roots.sort_by_key(|it| it.as_os_str().len());
191 let unique = roots
192 .iter()
193 .enumerate()
194 .filter(|&(idx, long)| {
195 !roots[..idx].iter().any(|short| long.starts_with(short))
196 })
197 .map(|(_idx, root)| root);
198
199 for root in unique {
200 log::debug!("sending root, {}", root.display());
201 fs_worker.send(root.to_owned());
202 }
203 }
204 state.set_workspaces(workspaces);
205 state_changed = true;
206 }
207 Err(e) => log::warn!("loading workspace failed: {}", e),
208 },
209 Event::Lib(lib) => {
210 feedback(internal_mode, "library loaded", msg_sender);
211 state.add_lib(lib);
212 }
213 Event::Msg(msg) => match msg {
214 RawMessage::Request(req) => {
215 let req = match handle_shutdown(req, msg_sender) {
216 Some(req) => req,
217 None => return Ok(()),
218 };
219 match on_request(state, pending_requests, pool, &task_sender, req)? {
220 None => (),
221 Some(req) => {
222 log::error!("unknown request: {:?}", req);
223 let resp = RawResponse::err(
224 req.id,
225 ErrorCode::MethodNotFound as i32,
226 "unknown request".to_string(),
227 );
228 msg_sender.send(RawMessage::Response(resp))
229 }
230 }
231 }
232 RawMessage::Notification(not) => {
233 on_notification(msg_sender, state, pending_requests, subs, not)?;
234 state_changed = true;
235 }
236 RawMessage::Response(resp) => log::error!("unexpected response: {:?}", resp),
237 },
238 };
239
240 if state_changed {
241 update_file_notifications_on_threadpool(
242 pool,
243 state.snapshot(),
244 publish_decorations,
245 task_sender.clone(),
246 subs.subscriptions(),
247 )
248 }
249 }
250}
251
252fn on_task(task: Task, msg_sender: &Sender<RawMessage>, pending_requests: &mut FxHashSet<u64>) {
253 match task {
254 Task::Respond(response) => {
255 if pending_requests.remove(&response.id) {
256 msg_sender.send(RawMessage::Response(response))
257 }
258 }
259 Task::Notify(n) => msg_sender.send(RawMessage::Notification(n)),
260 }
261}
262
263fn on_request(
264 world: &mut ServerWorldState,
265 pending_requests: &mut FxHashSet<u64>,
266 pool: &ThreadPool,
267 sender: &Sender<Task>,
268 req: RawRequest,
269) -> Result<Option<RawRequest>> {
270 let mut pool_dispatcher = PoolDispatcher {
271 req: Some(req),
272 res: None,
273 pool,
274 world,
275 sender,
276 };
277 let req = pool_dispatcher
278 .on::<req::SyntaxTree>(handlers::handle_syntax_tree)?
279 .on::<req::ExtendSelection>(handlers::handle_extend_selection)?
280 .on::<req::FindMatchingBrace>(handlers::handle_find_matching_brace)?
281 .on::<req::JoinLines>(handlers::handle_join_lines)?
282 .on::<req::OnEnter>(handlers::handle_on_enter)?
283 .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)?
284 .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)?
285 .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
286 .on::<req::GotoDefinition>(handlers::handle_goto_definition)?
287 .on::<req::ParentModule>(handlers::handle_parent_module)?
288 .on::<req::Runnables>(handlers::handle_runnables)?
289 .on::<req::DecorationsRequest>(handlers::handle_decorations)?
290 .on::<req::Completion>(handlers::handle_completion)?
291 .on::<req::CodeActionRequest>(handlers::handle_code_action)?
292 .on::<req::FoldingRangeRequest>(handlers::handle_folding_range)?
293 .on::<req::SignatureHelpRequest>(handlers::handle_signature_help)?
294 .on::<req::HoverRequest>(handlers::handle_hover)?
295 .on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)?
296 .on::<req::Rename>(handlers::handle_rename)?
297 .on::<req::References>(handlers::handle_references)?
298 .finish();
299 match req {
300 Ok(id) => {
301 let inserted = pending_requests.insert(id);
302 assert!(inserted, "duplicate request: {}", id);
303 Ok(None)
304 }
305 Err(req) => Ok(Some(req)),
306 }
307}
308
309fn on_notification(
310 msg_sender: &Sender<RawMessage>,
311 state: &mut ServerWorldState,
312 pending_requests: &mut FxHashSet<u64>,
313 subs: &mut Subscriptions,
314 not: RawNotification,
315) -> Result<()> {
316 let not = match not.cast::<req::Cancel>() {
317 Ok(params) => {
318 let id = match params.id {
319 NumberOrString::Number(id) => id,
320 NumberOrString::String(id) => {
321 panic!("string id's not supported: {:?}", id);
322 }
323 };
324 pending_requests.remove(&id);
325 return Ok(());
326 }
327 Err(not) => not,
328 };
329 let not = match not.cast::<req::DidOpenTextDocument>() {
330 Ok(params) => {
331 let uri = params.text_document.uri;
332 let path = uri
333 .to_file_path()
334 .map_err(|()| format_err!("invalid uri: {}", uri))?;
335 let file_id = state.add_mem_file(path, params.text_document.text);
336 subs.add_sub(file_id);
337 return Ok(());
338 }
339 Err(not) => not,
340 };
341 let not = match not.cast::<req::DidChangeTextDocument>() {
342 Ok(mut params) => {
343 let uri = params.text_document.uri;
344 let path = uri
345 .to_file_path()
346 .map_err(|()| format_err!("invalid uri: {}", uri))?;
347 let text = params
348 .content_changes
349 .pop()
350 .ok_or_else(|| format_err!("empty changes"))?
351 .text;
352 state.change_mem_file(path.as_path(), text)?;
353 return Ok(());
354 }
355 Err(not) => not,
356 };
357 let not = match not.cast::<req::DidCloseTextDocument>() {
358 Ok(params) => {
359 let uri = params.text_document.uri;
360 let path = uri
361 .to_file_path()
362 .map_err(|()| format_err!("invalid uri: {}", uri))?;
363 let file_id = state.remove_mem_file(path.as_path())?;
364 subs.remove_sub(file_id);
365 let params = req::PublishDiagnosticsParams {
366 uri,
367 diagnostics: Vec::new(),
368 };
369 let not = RawNotification::new::<req::PublishDiagnostics>(&params);
370 msg_sender.send(RawMessage::Notification(not));
371 return Ok(());
372 }
373 Err(not) => not,
374 };
375 log::error!("unhandled notification: {:?}", not);
376 Ok(())
377}
378
379struct PoolDispatcher<'a> {
380 req: Option<RawRequest>,
381 res: Option<u64>,
382 pool: &'a ThreadPool,
383 world: &'a ServerWorldState,
384 sender: &'a Sender<Task>,
385}
386
387impl<'a> PoolDispatcher<'a> {
388 fn on<'b, R>(
389 &'b mut self,
390 f: fn(ServerWorld, R::Params) -> Result<R::Result>,
391 ) -> Result<&'b mut Self>
392 where
393 R: req::Request,
394 R::Params: DeserializeOwned + Send + 'static,
395 R::Result: Serialize + 'static,
396 {
397 let req = match self.req.take() {
398 None => return Ok(self),
399 Some(req) => req,
400 };
401 match req.cast::<R>() {
402 Ok((id, params)) => {
403 let world = self.world.snapshot();
404 let sender = self.sender.clone();
405 self.pool.spawn(move || {
406 let resp = match f(world, params) {
407 Ok(resp) => RawResponse::ok::<R>(id, &resp),
408 Err(e) => match e.downcast::<LspError>() {
409 Ok(lsp_error) => {
410 RawResponse::err(id, lsp_error.code, lsp_error.message)
411 }
412 Err(e) => {
413 if is_canceled(&e) {
414 RawResponse::err(
415 id,
416 ErrorCode::RequestCancelled as i32,
417 e.to_string(),
418 )
419 } else {
420 RawResponse::err(
421 id,
422 ErrorCode::InternalError as i32,
423 format!("{}\n{}", e, e.backtrace()),
424 )
425 }
426 }
427 },
428 };
429 let task = Task::Respond(resp);
430 sender.send(task);
431 });
432 self.res = Some(id);
433 }
434 Err(req) => self.req = Some(req),
435 }
436 Ok(self)
437 }
438
439 fn finish(&mut self) -> ::std::result::Result<u64, RawRequest> {
440 match (self.res.take(), self.req.take()) {
441 (Some(res), None) => Ok(res),
442 (None, Some(req)) => Err(req),
443 _ => unreachable!(),
444 }
445 }
446}
447
448fn update_file_notifications_on_threadpool(
449 pool: &ThreadPool,
450 world: ServerWorld,
451 publish_decorations: bool,
452 sender: Sender<Task>,
453 subscriptions: Vec<FileId>,
454) {
455 pool.spawn(move || {
456 for file_id in subscriptions {
457 match handlers::publish_diagnostics(&world, file_id) {
458 Err(e) => {
459 if !is_canceled(&e) {
460 log::error!("failed to compute diagnostics: {:?}", e);
461 }
462 }
463 Ok(params) => {
464 let not = RawNotification::new::<req::PublishDiagnostics>(&params);
465 sender.send(Task::Notify(not));
466 }
467 }
468 if publish_decorations {
469 match handlers::publish_decorations(&world, file_id) {
470 Err(e) => {
471 if !is_canceled(&e) {
472 log::error!("failed to compute decorations: {:?}", e);
473 }
474 }
475 Ok(params) => {
476 let not = RawNotification::new::<req::PublishDecorations>(&params);
477 sender.send(Task::Notify(not))
478 }
479 }
480 }
481 }
482 });
483}
484
485fn feedback(intrnal_mode: bool, msg: &str, sender: &Sender<RawMessage>) {
486 if !intrnal_mode {
487 return;
488 }
489 let not = RawNotification::new::<req::InternalFeedback>(&msg.to_string());
490 sender.send(RawMessage::Notification(not));
491}
492
493fn is_canceled(e: &failure::Error) -> bool {
494 e.downcast_ref::<Canceled>().is_some()
495}