diff options
author | Aleksey Kladov <[email protected]> | 2020-06-25 16:50:47 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-06-25 17:28:58 +0100 |
commit | f5ea35a2710c78e77c81f491cc6f8abd40e33981 (patch) | |
tree | 8ba08993d86c5c93b9a0db373b22444d25bfab65 | |
parent | 22098127c4f4b7414f0695c7788f07d0a1c43892 (diff) |
Add NotificationDispatcher
-rw-r--r-- | crates/rust-analyzer/src/dispatch.rs | 39 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_utils.rs | 13 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 114 |
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. | ||
1 | use std::{panic, time::Instant}; | 2 | use std::{panic, time::Instant}; |
2 | 3 | ||
3 | use serde::{de::DeserializeOwned, Serialize}; | 4 | use serde::{de::DeserializeOwned, Serialize}; |
@@ -135,3 +136,41 @@ where | |||
135 | }; | 136 | }; |
136 | Task::Respond(response) | 137 | Task::Respond(response) |
137 | } | 138 | } |
139 | |||
140 | pub(crate) struct NotificationDispatcher<'a> { | ||
141 | pub(crate) not: Option<lsp_server::Notification>, | ||
142 | pub(crate) global_state: &'a mut GlobalState, | ||
143 | } | ||
144 | |||
145 | impl<'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. |
2 | use std::{error::Error, ops::Range}; | 2 | use std::{error::Error, ops::Range}; |
3 | 3 | ||
4 | use crate::from_proto; | ||
5 | use crossbeam_channel::Sender; | 4 | use crossbeam_channel::Sender; |
6 | use lsp_server::{Message, Notification}; | 5 | use lsp_server::{Message, Notification}; |
7 | use ra_db::Canceled; | 6 | use ra_db::Canceled; |
8 | use ra_ide::LineIndex; | 7 | use ra_ide::LineIndex; |
9 | use serde::{de::DeserializeOwned, Serialize}; | 8 | use serde::Serialize; |
9 | |||
10 | use crate::from_proto; | ||
10 | 11 | ||
11 | pub fn show_message( | 12 | pub 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 | ||
32 | pub(crate) fn notification_cast<N>(notification: Notification) -> Result<N::Params, Notification> | ||
33 | where | ||
34 | N: lsp_types::notification::Notification, | ||
35 | N::Params: DeserializeOwned, | ||
36 | { | ||
37 | notification.extract(N::METHOD) | ||
38 | } | ||
39 | |||
40 | pub(crate) fn notification_new<N>(params: N::Params) -> Notification | 33 | pub(crate) fn notification_new<N>(params: N::Params) -> Notification |
41 | where | 34 | where |
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 | ||
8 | use crossbeam_channel::{never, select, Receiver}; | 8 | use crossbeam_channel::{never, select, Receiver}; |
9 | use lsp_server::{Connection, Notification, Request, RequestId, Response}; | 9 | use lsp_server::{Connection, Notification, Request, Response}; |
10 | use lsp_types::{notification::Notification as _, request::Request as _, NumberOrString}; | 10 | use lsp_types::{notification::Notification as _, request::Request as _}; |
11 | use ra_db::VfsPath; | 11 | use ra_db::VfsPath; |
12 | use ra_ide::{Canceled, FileId}; | 12 | use ra_ide::{Canceled, FileId}; |
13 | use ra_prof::profile; | 13 | use ra_prof::profile; |
@@ -16,13 +16,12 @@ use ra_project_model::{PackageRoot, ProjectWorkspace}; | |||
16 | use crate::{ | 16 | use 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(¶ms.text_document.uri) { | 310 | if let Ok(path) = from_proto::vfs_path(¶ms.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(¶ms.text_document.uri) { | 322 | if let Ok(path) = from_proto::vfs_path(¶ms.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(¶ms.text_document.uri) { | 333 | if let Ok(path) = from_proto::vfs_path(¶ms.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)] |
486 | pub(crate) enum Task { | 458 | pub(crate) enum Task { |
487 | Respond(Response), | 459 | Respond(Response), |