aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/rust-analyzer/src/dispatch.rs39
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs13
-rw-r--r--crates/rust-analyzer/src/main_loop.rs114
3 files changed, 85 insertions, 81 deletions
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs
index 0a9b0428d..5fdbed8ef 100644
--- a/crates/rust-analyzer/src/dispatch.rs
+++ b/crates/rust-analyzer/src/dispatch.rs
@@ -1,3 +1,4 @@
1//! A visitor for downcasting arbitrary request (JSON) into a specific type.
1use std::{panic, time::Instant}; 2use std::{panic, time::Instant};
2 3
3use serde::{de::DeserializeOwned, Serialize}; 4use serde::{de::DeserializeOwned, Serialize};
@@ -135,3 +136,41 @@ where
135 }; 136 };
136 Task::Respond(response) 137 Task::Respond(response)
137} 138}
139
140pub(crate) struct NotificationDispatcher<'a> {
141 pub(crate) not: Option<lsp_server::Notification>,
142 pub(crate) global_state: &'a mut GlobalState,
143}
144
145impl<'a> NotificationDispatcher<'a> {
146 pub(crate) fn on<N>(
147 &mut self,
148 f: fn(&mut GlobalState, N::Params) -> Result<()>,
149 ) -> Result<&mut Self>
150 where
151 N: lsp_types::notification::Notification + 'static,
152 N::Params: DeserializeOwned + Send + 'static,
153 {
154 let not = match self.not.take() {
155 Some(it) => it,
156 None => return Ok(self),
157 };
158 let params = match not.extract::<N::Params>(N::METHOD) {
159 Ok(it) => it,
160 Err(not) => {
161 self.not = Some(not);
162 return Ok(self);
163 }
164 };
165 f(self.global_state, params)?;
166 Ok(self)
167 }
168
169 pub(crate) fn finish(&mut self) {
170 if let Some(not) = &self.not {
171 if !not.method.starts_with("$/") {
172 log::error!("unhandled notification: {:?}", not);
173 }
174 }
175 }
176}
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 14adb8ae7..35917030c 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -1,12 +1,13 @@
1//! Utilities for LSP-related boilerplate code. 1//! Utilities for LSP-related boilerplate code.
2use std::{error::Error, ops::Range}; 2use std::{error::Error, ops::Range};
3 3
4use crate::from_proto;
5use crossbeam_channel::Sender; 4use crossbeam_channel::Sender;
6use lsp_server::{Message, Notification}; 5use lsp_server::{Message, Notification};
7use ra_db::Canceled; 6use ra_db::Canceled;
8use ra_ide::LineIndex; 7use ra_ide::LineIndex;
9use serde::{de::DeserializeOwned, Serialize}; 8use serde::Serialize;
9
10use crate::from_proto;
10 11
11pub fn show_message( 12pub fn show_message(
12 typ: lsp_types::MessageType, 13 typ: lsp_types::MessageType,
@@ -29,14 +30,6 @@ pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
29 notification.method == N::METHOD 30 notification.method == N::METHOD
30} 31}
31 32
32pub(crate) fn notification_cast<N>(notification: Notification) -> Result<N::Params, Notification>
33where
34 N: lsp_types::notification::Notification,
35 N::Params: DeserializeOwned,
36{
37 notification.extract(N::METHOD)
38}
39
40pub(crate) fn notification_new<N>(params: N::Params) -> Notification 33pub(crate) fn notification_new<N>(params: N::Params) -> Notification
41where 34where
42 N: lsp_types::notification::Notification, 35 N: lsp_types::notification::Notification,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index ebc232736..c2f43df1d 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -6,8 +6,8 @@ use std::{
6}; 6};
7 7
8use crossbeam_channel::{never, select, Receiver}; 8use crossbeam_channel::{never, select, Receiver};
9use lsp_server::{Connection, Notification, Request, RequestId, Response}; 9use lsp_server::{Connection, Notification, Request, Response};
10use lsp_types::{notification::Notification as _, request::Request as _, NumberOrString}; 10use lsp_types::{notification::Notification as _, request::Request as _};
11use ra_db::VfsPath; 11use ra_db::VfsPath;
12use ra_ide::{Canceled, FileId}; 12use ra_ide::{Canceled, FileId};
13use ra_prof::profile; 13use ra_prof::profile;
@@ -16,13 +16,12 @@ use ra_project_model::{PackageRoot, ProjectWorkspace};
16use crate::{ 16use crate::{
17 config::{Config, FilesWatcher, LinkedProject}, 17 config::{Config, FilesWatcher, LinkedProject},
18 diagnostics::DiagnosticTask, 18 diagnostics::DiagnosticTask,
19 dispatch::RequestDispatcher, 19 dispatch::{NotificationDispatcher, RequestDispatcher},
20 from_proto, 20 from_proto,
21 global_state::{file_id_to_url, GlobalState, Status}, 21 global_state::{file_id_to_url, GlobalState, Status},
22 handlers, lsp_ext, 22 handlers, lsp_ext,
23 lsp_utils::{ 23 lsp_utils::{
24 apply_document_changes, is_canceled, notification_cast, notification_is, notification_new, 24 apply_document_changes, is_canceled, notification_is, notification_new, show_message,
25 show_message,
26 }, 25 },
27 request_metrics::RequestMetrics, 26 request_metrics::RequestMetrics,
28 Result, 27 Result,
@@ -240,9 +239,7 @@ impl GlobalState {
240 } 239 }
241 240
242 fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { 241 fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
243 let mut pool_dispatcher = 242 RequestDispatcher { req: Some(req), global_state: self, request_received }
244 RequestDispatcher { req: Some(req), global_state: self, request_received };
245 pool_dispatcher
246 .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.collect_garbage()))? 243 .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.collect_garbage()))?
247 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? 244 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
248 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? 245 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
@@ -298,56 +295,47 @@ impl GlobalState {
298 Ok(()) 295 Ok(())
299 } 296 }
300 fn on_notification(&mut self, not: Notification) -> Result<()> { 297 fn on_notification(&mut self, not: Notification) -> Result<()> {
301 let not = match notification_cast::<lsp_types::notification::Cancel>(not) { 298 NotificationDispatcher { not: Some(not), global_state: self }
302 Ok(params) => { 299 .on::<lsp_types::notification::Cancel>(|this, params| {
303 let id: RequestId = match params.id { 300 let id: lsp_server::RequestId = match params.id {
304 NumberOrString::Number(id) => id.into(), 301 lsp_types::NumberOrString::Number(id) => id.into(),
305 NumberOrString::String(id) => id.into(), 302 lsp_types::NumberOrString::String(id) => id.into(),
306 }; 303 };
307 if let Some(response) = self.req_queue.incoming.cancel(id) { 304 if let Some(response) = this.req_queue.incoming.cancel(id) {
308 self.send(response.into()) 305 this.send(response.into());
309 } 306 }
310 return Ok(()); 307 Ok(())
311 } 308 })?
312 Err(not) => not, 309 .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
313 };
314 let not = match notification_cast::<lsp_types::notification::DidOpenTextDocument>(not) {
315 Ok(params) => {
316 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) { 310 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
317 if !self.mem_docs.insert(path.clone()) { 311 if !this.mem_docs.insert(path.clone()) {
318 log::error!("duplicate DidOpenTextDocument: {}", path) 312 log::error!("duplicate DidOpenTextDocument: {}", path)
319 } 313 }
320 self.vfs 314 this.vfs
321 .write() 315 .write()
322 .0 316 .0
323 .set_file_contents(path, Some(params.text_document.text.into_bytes())); 317 .set_file_contents(path, Some(params.text_document.text.into_bytes()));
324 } 318 }
325 return Ok(()); 319 Ok(())
326 } 320 })?
327 Err(not) => not, 321 .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| {
328 };
329 let not = match notification_cast::<lsp_types::notification::DidChangeTextDocument>(not) {
330 Ok(params) => {
331 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) { 322 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
332 assert!(self.mem_docs.contains(&path)); 323 assert!(this.mem_docs.contains(&path));
333 let vfs = &mut self.vfs.write().0; 324 let vfs = &mut this.vfs.write().0;
334 let file_id = vfs.file_id(&path).unwrap(); 325 let file_id = vfs.file_id(&path).unwrap();
335 let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap(); 326 let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap();
336 apply_document_changes(&mut text, params.content_changes); 327 apply_document_changes(&mut text, params.content_changes);
337 vfs.set_file_contents(path, Some(text.into_bytes())) 328 vfs.set_file_contents(path, Some(text.into_bytes()))
338 } 329 }
339 return Ok(()); 330 Ok(())
340 } 331 })?
341 Err(not) => not, 332 .on::<lsp_types::notification::DidCloseTextDocument>(|this, params| {
342 };
343 let not = match notification_cast::<lsp_types::notification::DidCloseTextDocument>(not) {
344 Ok(params) => {
345 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) { 333 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
346 if !self.mem_docs.remove(&path) { 334 if !this.mem_docs.remove(&path) {
347 log::error!("orphan DidCloseTextDocument: {}", path) 335 log::error!("orphan DidCloseTextDocument: {}", path)
348 } 336 }
349 if let Some(path) = path.as_path() { 337 if let Some(path) = path.as_path() {
350 self.loader.invalidate(path.to_path_buf()); 338 this.loader.invalidate(path.to_path_buf());
351 } 339 }
352 } 340 }
353 let params = lsp_types::PublishDiagnosticsParams { 341 let params = lsp_types::PublishDiagnosticsParams {
@@ -356,25 +344,19 @@ impl GlobalState {
356 version: None, 344 version: None,
357 }; 345 };
358 let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); 346 let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params);
359 self.send(not.into()); 347 this.send(not.into());
360 return Ok(()); 348 Ok(())
361 } 349 })?
362 Err(not) => not, 350 .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| {
363 }; 351 if let Some(flycheck) = &this.flycheck {
364 let not = match notification_cast::<lsp_types::notification::DidSaveTextDocument>(not) {
365 Ok(_params) => {
366 if let Some(flycheck) = &self.flycheck {
367 flycheck.0.update(); 352 flycheck.0.update();
368 } 353 }
369 return Ok(()); 354 Ok(())
370 } 355 })?
371 Err(not) => not, 356 .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
372 };
373 let not = match notification_cast::<lsp_types::notification::DidChangeConfiguration>(not) {
374 Ok(_) => {
375 // As stated in https://github.com/microsoft/language-server-protocol/issues/676, 357 // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
376 // this notification's parameters should be ignored and the actual config queried separately. 358 // this notification's parameters should be ignored and the actual config queried separately.
377 let request = self.req_queue.outgoing.register( 359 let request = this.req_queue.outgoing.register(
378 lsp_types::request::WorkspaceConfiguration::METHOD.to_string(), 360 lsp_types::request::WorkspaceConfiguration::METHOD.to_string(),
379 lsp_types::ConfigurationParams { 361 lsp_types::ConfigurationParams {
380 items: vec![lsp_types::ConfigurationItem { 362 items: vec![lsp_types::ConfigurationItem {
@@ -403,30 +385,21 @@ impl GlobalState {
403 } 385 }
404 }, 386 },
405 ); 387 );
406 self.send(request.into()); 388 this.send(request.into());
407 389
408 return Ok(()); 390 return Ok(());
409 } 391 })?
410 Err(not) => not, 392 .on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| {
411 };
412 let not = match notification_cast::<lsp_types::notification::DidChangeWatchedFiles>(not) {
413 Ok(params) => {
414 for change in params.changes { 393 for change in params.changes {
415 if let Ok(path) = from_proto::abs_path(&change.uri) { 394 if let Ok(path) = from_proto::abs_path(&change.uri) {
416 self.loader.invalidate(path) 395 this.loader.invalidate(path);
417 } 396 }
418 } 397 }
419 return Ok(()); 398 Ok(())
420 } 399 })?
421 Err(not) => not, 400 .finish();
422 };
423 if not.method.starts_with("$/") {
424 return Ok(());
425 }
426 log::error!("unhandled notification: {:?}", not);
427 Ok(()) 401 Ok(())
428 } 402 }
429 // TODO
430 pub(crate) fn on_task(&mut self, task: Task) { 403 pub(crate) fn on_task(&mut self, task: Task) {
431 match task { 404 match task {
432 Task::Respond(response) => { 405 Task::Respond(response) => {
@@ -481,7 +454,6 @@ impl GlobalState {
481 } 454 }
482} 455}
483 456
484// TODO
485#[derive(Debug)] 457#[derive(Debug)]
486pub(crate) enum Task { 458pub(crate) enum Task {
487 Respond(Response), 459 Respond(Response),