aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server/src/main_loop.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_lsp_server/src/main_loop.rs')
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs141
1 files changed, 76 insertions, 65 deletions
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 45bd52769..fb357b36b 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -5,9 +5,7 @@ pub(crate) mod pending_requests;
5use std::{error::Error, fmt, path::PathBuf, sync::Arc, time::Instant}; 5use std::{error::Error, fmt, path::PathBuf, sync::Arc, time::Instant};
6 6
7use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender}; 7use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender};
8use gen_lsp_server::{ 8use lsp_server::{handle_shutdown, ErrorCode, Message, Notification, Request, RequestId, Response};
9 handle_shutdown, ErrorCode, RawMessage, RawNotification, RawRequest, RawResponse,
10};
11use lsp_types::{ClientCapabilities, NumberOrString}; 9use lsp_types::{ClientCapabilities, NumberOrString};
12use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData}; 10use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData};
13use ra_prof::profile; 11use ra_prof::profile;
@@ -53,8 +51,8 @@ pub fn main_loop(
53 ws_roots: Vec<PathBuf>, 51 ws_roots: Vec<PathBuf>,
54 client_caps: ClientCapabilities, 52 client_caps: ClientCapabilities,
55 config: ServerConfig, 53 config: ServerConfig,
56 msg_receiver: &Receiver<RawMessage>, 54 msg_receiver: &Receiver<Message>,
57 msg_sender: &Sender<RawMessage>, 55 msg_sender: &Sender<Message>,
58) -> Result<()> { 56) -> Result<()> {
59 log::info!("server_config: {:#?}", config); 57 log::info!("server_config: {:#?}", config);
60 // FIXME: support dynamic workspace loading. 58 // FIXME: support dynamic workspace loading.
@@ -146,12 +144,12 @@ pub fn main_loop(
146 144
147#[derive(Debug)] 145#[derive(Debug)]
148enum Task { 146enum Task {
149 Respond(RawResponse), 147 Respond(Response),
150 Notify(RawNotification), 148 Notify(Notification),
151} 149}
152 150
153enum Event { 151enum Event {
154 Msg(RawMessage), 152 Msg(Message),
155 Task(Task), 153 Task(Task),
156 Vfs(VfsTask), 154 Vfs(VfsTask),
157 Lib(LibraryData), 155 Lib(LibraryData),
@@ -159,24 +157,28 @@ enum Event {
159 157
160impl fmt::Debug for Event { 158impl fmt::Debug for Event {
161 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 159 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162 let debug_verbose_not = |not: &RawNotification, f: &mut fmt::Formatter| { 160 let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| {
163 f.debug_struct("RawNotification").field("method", &not.method).finish() 161 f.debug_struct("Notification").field("method", &not.method).finish()
164 }; 162 };
165 163
166 match self { 164 match self {
167 Event::Msg(RawMessage::Notification(not)) => { 165 Event::Msg(Message::Notification(not)) => {
168 if not.is::<req::DidOpenTextDocument>() || not.is::<req::DidChangeTextDocument>() { 166 if notification_is::<req::DidOpenTextDocument>(not)
167 || notification_is::<req::DidChangeTextDocument>(not)
168 {
169 return debug_verbose_not(not, f); 169 return debug_verbose_not(not, f);
170 } 170 }
171 } 171 }
172 Event::Task(Task::Notify(not)) => { 172 Event::Task(Task::Notify(not)) => {
173 if not.is::<req::PublishDecorations>() || not.is::<req::PublishDiagnostics>() { 173 if notification_is::<req::PublishDecorations>(not)
174 || notification_is::<req::PublishDiagnostics>(not)
175 {
174 return debug_verbose_not(not, f); 176 return debug_verbose_not(not, f);
175 } 177 }
176 } 178 }
177 Event::Task(Task::Respond(resp)) => { 179 Event::Task(Task::Respond(resp)) => {
178 return f 180 return f
179 .debug_struct("RawResponse") 181 .debug_struct("Response")
180 .field("id", &resp.id) 182 .field("id", &resp.id)
181 .field("error", &resp.error) 183 .field("error", &resp.error)
182 .finish(); 184 .finish();
@@ -194,8 +196,8 @@ impl fmt::Debug for Event {
194 196
195fn main_loop_inner( 197fn main_loop_inner(
196 pool: &ThreadPool, 198 pool: &ThreadPool,
197 msg_sender: &Sender<RawMessage>, 199 msg_sender: &Sender<Message>,
198 msg_receiver: &Receiver<RawMessage>, 200 msg_receiver: &Receiver<Message>,
199 task_sender: Sender<Task>, 201 task_sender: Sender<Task>,
200 task_receiver: Receiver<Task>, 202 task_receiver: Receiver<Task>,
201 state: &mut WorldState, 203 state: &mut WorldState,
@@ -249,10 +251,9 @@ fn main_loop_inner(
249 in_flight_libraries -= 1; 251 in_flight_libraries -= 1;
250 } 252 }
251 Event::Msg(msg) => match msg { 253 Event::Msg(msg) => match msg {
252 RawMessage::Request(req) => { 254 Message::Request(req) => {
253 let req = match handle_shutdown(req, msg_sender) { 255 if handle_shutdown(&req, msg_sender) {
254 Some(req) => req, 256 return Ok(());
255 None => return Ok(()),
256 }; 257 };
257 on_request( 258 on_request(
258 state, 259 state,
@@ -264,11 +265,11 @@ fn main_loop_inner(
264 req, 265 req,
265 )? 266 )?
266 } 267 }
267 RawMessage::Notification(not) => { 268 Message::Notification(not) => {
268 on_notification(msg_sender, state, pending_requests, &mut subs, not)?; 269 on_notification(msg_sender, state, pending_requests, &mut subs, not)?;
269 state_changed = true; 270 state_changed = true;
270 } 271 }
271 RawMessage::Response(resp) => log::error!("unexpected response: {:?}", resp), 272 Message::Response(resp) => log::error!("unexpected response: {:?}", resp),
272 }, 273 },
273 }; 274 };
274 275
@@ -313,13 +314,13 @@ fn main_loop_inner(
313 314
314fn on_task( 315fn on_task(
315 task: Task, 316 task: Task,
316 msg_sender: &Sender<RawMessage>, 317 msg_sender: &Sender<Message>,
317 pending_requests: &mut PendingRequests, 318 pending_requests: &mut PendingRequests,
318 state: &mut WorldState, 319 state: &mut WorldState,
319) { 320) {
320 match task { 321 match task {
321 Task::Respond(response) => { 322 Task::Respond(response) => {
322 if let Some(completed) = pending_requests.finish(response.id) { 323 if let Some(completed) = pending_requests.finish(&response.id) {
323 log::info!("handled req#{} in {:?}", completed.id, completed.duration); 324 log::info!("handled req#{} in {:?}", completed.id, completed.duration);
324 state.complete_request(completed); 325 state.complete_request(completed);
325 msg_sender.send(response.into()).unwrap(); 326 msg_sender.send(response.into()).unwrap();
@@ -336,9 +337,9 @@ fn on_request(
336 pending_requests: &mut PendingRequests, 337 pending_requests: &mut PendingRequests,
337 pool: &ThreadPool, 338 pool: &ThreadPool,
338 sender: &Sender<Task>, 339 sender: &Sender<Task>,
339 msg_sender: &Sender<RawMessage>, 340 msg_sender: &Sender<Message>,
340 request_received: Instant, 341 request_received: Instant,
341 req: RawRequest, 342 req: Request,
342) -> Result<()> { 343) -> Result<()> {
343 let mut pool_dispatcher = PoolDispatcher { 344 let mut pool_dispatcher = PoolDispatcher {
344 req: Some(req), 345 req: Some(req),
@@ -388,22 +389,20 @@ fn on_request(
388} 389}
389 390
390fn on_notification( 391fn on_notification(
391 msg_sender: &Sender<RawMessage>, 392 msg_sender: &Sender<Message>,
392 state: &mut WorldState, 393 state: &mut WorldState,
393 pending_requests: &mut PendingRequests, 394 pending_requests: &mut PendingRequests,
394 subs: &mut Subscriptions, 395 subs: &mut Subscriptions,
395 not: RawNotification, 396 not: Notification,
396) -> Result<()> { 397) -> Result<()> {
397 let not = match not.cast::<req::Cancel>() { 398 let not = match notification_cast::<req::Cancel>(not) {
398 Ok(params) => { 399 Ok(params) => {
399 let id = match params.id { 400 let id: RequestId = match params.id {
400 NumberOrString::Number(id) => id, 401 NumberOrString::Number(id) => id.into(),
401 NumberOrString::String(id) => { 402 NumberOrString::String(id) => id.into(),
402 panic!("string id's not supported: {:?}", id);
403 }
404 }; 403 };
405 if pending_requests.cancel(id) { 404 if pending_requests.cancel(&id) {
406 let response = RawResponse::err( 405 let response = Response::new_err(
407 id, 406 id,
408 ErrorCode::RequestCanceled as i32, 407 ErrorCode::RequestCanceled as i32,
409 "canceled by client".to_string(), 408 "canceled by client".to_string(),
@@ -414,7 +413,7 @@ fn on_notification(
414 } 413 }
415 Err(not) => not, 414 Err(not) => not,
416 }; 415 };
417 let not = match not.cast::<req::DidOpenTextDocument>() { 416 let not = match notification_cast::<req::DidOpenTextDocument>(not) {
418 Ok(params) => { 417 Ok(params) => {
419 let uri = params.text_document.uri; 418 let uri = params.text_document.uri;
420 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; 419 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
@@ -427,7 +426,7 @@ fn on_notification(
427 } 426 }
428 Err(not) => not, 427 Err(not) => not,
429 }; 428 };
430 let not = match not.cast::<req::DidChangeTextDocument>() { 429 let not = match notification_cast::<req::DidChangeTextDocument>(not) {
431 Ok(mut params) => { 430 Ok(mut params) => {
432 let uri = params.text_document.uri; 431 let uri = params.text_document.uri;
433 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; 432 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
@@ -438,7 +437,7 @@ fn on_notification(
438 } 437 }
439 Err(not) => not, 438 Err(not) => not,
440 }; 439 };
441 let not = match not.cast::<req::DidCloseTextDocument>() { 440 let not = match notification_cast::<req::DidCloseTextDocument>(not) {
442 Ok(params) => { 441 Ok(params) => {
443 let uri = params.text_document.uri; 442 let uri = params.text_document.uri;
444 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; 443 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
@@ -446,13 +445,13 @@ fn on_notification(
446 subs.remove_sub(FileId(file_id.0)); 445 subs.remove_sub(FileId(file_id.0));
447 } 446 }
448 let params = req::PublishDiagnosticsParams { uri, diagnostics: Vec::new() }; 447 let params = req::PublishDiagnosticsParams { uri, diagnostics: Vec::new() };
449 let not = RawNotification::new::<req::PublishDiagnostics>(&params); 448 let not = notification_new::<req::PublishDiagnostics>(params);
450 msg_sender.send(not.into()).unwrap(); 449 msg_sender.send(not.into()).unwrap();
451 return Ok(()); 450 return Ok(());
452 } 451 }
453 Err(not) => not, 452 Err(not) => not,
454 }; 453 };
455 let not = match not.cast::<req::DidChangeConfiguration>() { 454 let not = match notification_cast::<req::DidChangeConfiguration>(not) {
456 Ok(_params) => { 455 Ok(_params) => {
457 return Ok(()); 456 return Ok(());
458 } 457 }
@@ -463,11 +462,11 @@ fn on_notification(
463} 462}
464 463
465struct PoolDispatcher<'a> { 464struct PoolDispatcher<'a> {
466 req: Option<RawRequest>, 465 req: Option<Request>,
467 pool: &'a ThreadPool, 466 pool: &'a ThreadPool,
468 world: &'a mut WorldState, 467 world: &'a mut WorldState,
469 pending_requests: &'a mut PendingRequests, 468 pending_requests: &'a mut PendingRequests,
470 msg_sender: &'a Sender<RawMessage>, 469 msg_sender: &'a Sender<Message>,
471 sender: &'a Sender<Task>, 470 sender: &'a Sender<Task>,
472 request_received: Instant, 471 request_received: Instant,
473} 472}
@@ -522,13 +521,13 @@ impl<'a> PoolDispatcher<'a> {
522 Ok(self) 521 Ok(self)
523 } 522 }
524 523
525 fn parse<R>(&mut self) -> Option<(u64, R::Params)> 524 fn parse<R>(&mut self) -> Option<(RequestId, R::Params)>
526 where 525 where
527 R: req::Request + 'static, 526 R: req::Request + 'static,
528 R::Params: DeserializeOwned + Send + 'static, 527 R::Params: DeserializeOwned + Send + 'static,
529 { 528 {
530 let req = self.req.take()?; 529 let req = self.req.take()?;
531 let (id, params) = match req.cast::<R>() { 530 let (id, params) = match req.extract::<R::Params>(R::METHOD) {
532 Ok(it) => it, 531 Ok(it) => it,
533 Err(req) => { 532 Err(req) => {
534 self.req = Some(req); 533 self.req = Some(req);
@@ -536,7 +535,7 @@ impl<'a> PoolDispatcher<'a> {
536 } 535 }
537 }; 536 };
538 self.pending_requests.start(PendingRequest { 537 self.pending_requests.start(PendingRequest {
539 id, 538 id: id.clone(),
540 method: R::METHOD.to_string(), 539 method: R::METHOD.to_string(),
541 received: self.request_received, 540 received: self.request_received,
542 }); 541 });
@@ -548,7 +547,7 @@ impl<'a> PoolDispatcher<'a> {
548 None => (), 547 None => (),
549 Some(req) => { 548 Some(req) => {
550 log::error!("unknown request: {:?}", req); 549 log::error!("unknown request: {:?}", req);
551 let resp = RawResponse::err( 550 let resp = Response::new_err(
552 req.id, 551 req.id,
553 ErrorCode::MethodNotFound as i32, 552 ErrorCode::MethodNotFound as i32,
554 "unknown request".to_string(), 553 "unknown request".to_string(),
@@ -559,34 +558,30 @@ impl<'a> PoolDispatcher<'a> {
559 } 558 }
560} 559}
561 560
562fn result_to_task<R>(id: u64, result: Result<R::Result>) -> Task 561fn result_to_task<R>(id: RequestId, result: Result<R::Result>) -> Task
563where 562where
564 R: req::Request + 'static, 563 R: req::Request + 'static,
565 R::Params: DeserializeOwned + Send + 'static, 564 R::Params: DeserializeOwned + Send + 'static,
566 R::Result: Serialize + 'static, 565 R::Result: Serialize + 'static,
567{ 566{
568 let response = match result { 567 let response = match result {
569 Ok(resp) => RawResponse::ok::<R>(id, &resp), 568 Ok(resp) => Response::new_ok(id, &resp),
570 Err(e) => match e.downcast::<LspError>() { 569 Err(e) => match e.downcast::<LspError>() {
571 Ok(lsp_error) => RawResponse::err(id, lsp_error.code, lsp_error.message), 570 Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message),
572 Err(e) => { 571 Err(e) => {
573 if is_canceled(&e) { 572 if is_canceled(&e) {
574 // FIXME: When https://github.com/Microsoft/vscode-languageserver-node/issues/457 573 // FIXME: When https://github.com/Microsoft/vscode-languageserver-node/issues/457
575 // gets fixed, we can return the proper response. 574 // gets fixed, we can return the proper response.
576 // This works around the issue where "content modified" error would continuously 575 // This works around the issue where "content modified" error would continuously
577 // show an message pop-up in VsCode 576 // show an message pop-up in VsCode
578 // RawResponse::err( 577 // Response::err(
579 // id, 578 // id,
580 // ErrorCode::ContentModified as i32, 579 // ErrorCode::ContentModified as i32,
581 // "content modified".to_string(), 580 // "content modified".to_string(),
582 // ) 581 // )
583 RawResponse { 582 Response::new_ok(id, ())
584 id,
585 result: Some(serde_json::to_value(&()).unwrap()),
586 error: None,
587 }
588 } else { 583 } else {
589 RawResponse::err(id, ErrorCode::InternalError as i32, e.to_string()) 584 Response::new_err(id, ErrorCode::InternalError as i32, e.to_string())
590 } 585 }
591 } 586 }
592 }, 587 },
@@ -613,7 +608,7 @@ fn update_file_notifications_on_threadpool(
613 } 608 }
614 } 609 }
615 Ok(params) => { 610 Ok(params) => {
616 let not = RawNotification::new::<req::PublishDiagnostics>(&params); 611 let not = notification_new::<req::PublishDiagnostics>(params);
617 sender.send(Task::Notify(not)).unwrap(); 612 sender.send(Task::Notify(not)).unwrap();
618 } 613 }
619 } 614 }
@@ -626,7 +621,7 @@ fn update_file_notifications_on_threadpool(
626 } 621 }
627 } 622 }
628 Ok(params) => { 623 Ok(params) => {
629 let not = RawNotification::new::<req::PublishDecorations>(&params); 624 let not = notification_new::<req::PublishDecorations>(params);
630 sender.send(Task::Notify(not)).unwrap(); 625 sender.send(Task::Notify(not)).unwrap();
631 } 626 }
632 } 627 }
@@ -635,17 +630,33 @@ fn update_file_notifications_on_threadpool(
635 }); 630 });
636} 631}
637 632
638pub fn show_message( 633pub fn show_message(typ: req::MessageType, message: impl Into<String>, sender: &Sender<Message>) {
639 typ: req::MessageType,
640 message: impl Into<String>,
641 sender: &Sender<RawMessage>,
642) {
643 let message = message.into(); 634 let message = message.into();
644 let params = req::ShowMessageParams { typ, message }; 635 let params = req::ShowMessageParams { typ, message };
645 let not = RawNotification::new::<req::ShowMessage>(&params); 636 let not = notification_new::<req::ShowMessage>(params);
646 sender.send(not.into()).unwrap(); 637 sender.send(not.into()).unwrap();
647} 638}
648 639
649fn is_canceled(e: &Box<dyn std::error::Error + Send + Sync>) -> bool { 640fn is_canceled(e: &Box<dyn std::error::Error + Send + Sync>) -> bool {
650 e.downcast_ref::<Canceled>().is_some() 641 e.downcast_ref::<Canceled>().is_some()
651} 642}
643
644fn notification_is<N: lsp_types::notification::Notification>(notification: &Notification) -> bool {
645 notification.method == N::METHOD
646}
647
648fn notification_cast<N>(notification: Notification) -> std::result::Result<N::Params, Notification>
649where
650 N: lsp_types::notification::Notification,
651 N::Params: DeserializeOwned,
652{
653 notification.extract(N::METHOD)
654}
655
656fn notification_new<N>(params: N::Params) -> Notification
657where
658 N: lsp_types::notification::Notification,
659 N::Params: Serialize,
660{
661 Notification::new(N::METHOD.to_string(), params)
662}