diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-08-30 15:24:39 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-08-30 15:24:39 +0100 |
commit | ffc95759c7b538d8dacc336912b62857d4919cdd (patch) | |
tree | 3f0e4056ba2e4b3799b72d71d709783aa6dffc49 /crates/ra_lsp_server | |
parent | 7d72ca80003b7915ed7fc64907b5b6dc5c88dacd (diff) | |
parent | 72a3722470e5297c72dcaccaf2f113e7b758606d (diff) |
Merge #1739
1739: move lsp-server to a separate repository r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_lsp_server')
-rw-r--r-- | crates/ra_lsp_server/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main.rs | 14 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 141 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/pending_requests.rs | 19 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/world.rs | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/tests/heavy_tests/support.rs | 57 |
7 files changed, 127 insertions, 110 deletions
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index e271c257b..084a20cd9 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml | |||
@@ -21,7 +21,7 @@ thread_worker = { path = "../thread_worker" } | |||
21 | ra_syntax = { path = "../ra_syntax" } | 21 | ra_syntax = { path = "../ra_syntax" } |
22 | ra_text_edit = { path = "../ra_text_edit" } | 22 | ra_text_edit = { path = "../ra_text_edit" } |
23 | ra_ide_api = { path = "../ra_ide_api" } | 23 | ra_ide_api = { path = "../ra_ide_api" } |
24 | gen_lsp_server = { path = "../gen_lsp_server" } | 24 | lsp-server = "0.1.0" |
25 | ra_project_model = { path = "../ra_project_model" } | 25 | ra_project_model = { path = "../ra_project_model" } |
26 | ra_prof = { path = "../ra_prof" } | 26 | ra_prof = { path = "../ra_prof" } |
27 | ra_vfs_glob = { path = "../ra_vfs_glob" } | 27 | ra_vfs_glob = { path = "../ra_vfs_glob" } |
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index ae1392cb5..1debe7660 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use flexi_logger::{Duplicate, Logger}; | 1 | use flexi_logger::{Duplicate, Logger}; |
2 | use gen_lsp_server::{run_server, stdio_transport}; | 2 | use lsp_server::{run_server, stdio_transport, LspServerError}; |
3 | 3 | ||
4 | use ra_lsp_server::{show_message, Result, ServerConfig}; | 4 | use ra_lsp_server::{show_message, Result, ServerConfig}; |
5 | use ra_prof; | 5 | use ra_prof; |
@@ -29,9 +29,11 @@ fn main() -> Result<()> { | |||
29 | } | 29 | } |
30 | 30 | ||
31 | fn main_inner() -> Result<()> { | 31 | fn main_inner() -> Result<()> { |
32 | let (receiver, sender, threads) = stdio_transport(); | 32 | let (sender, receiver, io_threads) = stdio_transport(); |
33 | let cwd = std::env::current_dir()?; | 33 | let cwd = std::env::current_dir()?; |
34 | run_server(ra_lsp_server::server_capabilities(), receiver, sender, |params, r, s| { | 34 | let caps = serde_json::to_value(ra_lsp_server::server_capabilities()).unwrap(); |
35 | run_server(caps, sender, receiver, |params, s, r| { | ||
36 | let params: lsp_types::InitializeParams = serde_json::from_value(params)?; | ||
35 | let root = params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); | 37 | let root = params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); |
36 | 38 | ||
37 | let workspace_roots = params | 39 | let workspace_roots = params |
@@ -62,9 +64,13 @@ fn main_inner() -> Result<()> { | |||
62 | .unwrap_or_default(); | 64 | .unwrap_or_default(); |
63 | 65 | ||
64 | ra_lsp_server::main_loop(workspace_roots, params.capabilities, server_config, r, s) | 66 | ra_lsp_server::main_loop(workspace_roots, params.capabilities, server_config, r, s) |
67 | }) | ||
68 | .map_err(|err| match err { | ||
69 | LspServerError::ProtocolError(err) => err.into(), | ||
70 | LspServerError::ServerError(err) => err, | ||
65 | })?; | 71 | })?; |
66 | log::info!("shutting down IO..."); | 72 | log::info!("shutting down IO..."); |
67 | threads.join()?; | 73 | io_threads.join()?; |
68 | log::info!("... IO is down"); | 74 | log::info!("... IO is down"); |
69 | Ok(()) | 75 | Ok(()) |
70 | } | 76 | } |
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; | |||
5 | use std::{error::Error, fmt, path::PathBuf, sync::Arc, time::Instant}; | 5 | use std::{error::Error, fmt, path::PathBuf, sync::Arc, time::Instant}; |
6 | 6 | ||
7 | use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender}; | 7 | use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender}; |
8 | use gen_lsp_server::{ | 8 | use lsp_server::{handle_shutdown, ErrorCode, Message, Notification, Request, RequestId, Response}; |
9 | handle_shutdown, ErrorCode, RawMessage, RawNotification, RawRequest, RawResponse, | ||
10 | }; | ||
11 | use lsp_types::{ClientCapabilities, NumberOrString}; | 9 | use lsp_types::{ClientCapabilities, NumberOrString}; |
12 | use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData}; | 10 | use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData}; |
13 | use ra_prof::profile; | 11 | use 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)] |
148 | enum Task { | 146 | enum Task { |
149 | Respond(RawResponse), | 147 | Respond(Response), |
150 | Notify(RawNotification), | 148 | Notify(Notification), |
151 | } | 149 | } |
152 | 150 | ||
153 | enum Event { | 151 | enum 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 | ||
160 | impl fmt::Debug for Event { | 158 | impl 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", ¬.method).finish() | 161 | f.debug_struct("Notification").field("method", ¬.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 | ||
195 | fn main_loop_inner( | 197 | fn 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 | ||
314 | fn on_task( | 315 | fn 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 | ||
390 | fn on_notification( | 391 | fn 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>(¶ms); | 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 | ||
465 | struct PoolDispatcher<'a> { | 464 | struct 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 | ||
562 | fn result_to_task<R>(id: u64, result: Result<R::Result>) -> Task | 561 | fn result_to_task<R>(id: RequestId, result: Result<R::Result>) -> Task |
563 | where | 562 | where |
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>(¶ms); | 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>(¶ms); | 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 | ||
638 | pub fn show_message( | 633 | pub 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>(¶ms); | 636 | let not = notification_new::<req::ShowMessage>(params); |
646 | sender.send(not.into()).unwrap(); | 637 | sender.send(not.into()).unwrap(); |
647 | } | 638 | } |
648 | 639 | ||
649 | fn is_canceled(e: &Box<dyn std::error::Error + Send + Sync>) -> bool { | 640 | fn 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 | |||
644 | fn notification_is<N: lsp_types::notification::Notification>(notification: &Notification) -> bool { | ||
645 | notification.method == N::METHOD | ||
646 | } | ||
647 | |||
648 | fn notification_cast<N>(notification: Notification) -> std::result::Result<N::Params, Notification> | ||
649 | where | ||
650 | N: lsp_types::notification::Notification, | ||
651 | N::Params: DeserializeOwned, | ||
652 | { | ||
653 | notification.extract(N::METHOD) | ||
654 | } | ||
655 | |||
656 | fn notification_new<N>(params: N::Params) -> Notification | ||
657 | where | ||
658 | N: lsp_types::notification::Notification, | ||
659 | N::Params: Serialize, | ||
660 | { | ||
661 | Notification::new(N::METHOD.to_string(), params) | ||
662 | } | ||
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 3a559e845..eb805a6d3 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::{fmt::Write as _, io::Write as _}; | 1 | use std::{fmt::Write as _, io::Write as _}; |
2 | 2 | ||
3 | use gen_lsp_server::ErrorCode; | 3 | use lsp_server::ErrorCode; |
4 | use lsp_types::{ | 4 | use lsp_types::{ |
5 | CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, | 5 | CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, |
6 | DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeKind, | 6 | DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeKind, |
diff --git a/crates/ra_lsp_server/src/main_loop/pending_requests.rs b/crates/ra_lsp_server/src/main_loop/pending_requests.rs index 741770e45..7a99fc679 100644 --- a/crates/ra_lsp_server/src/main_loop/pending_requests.rs +++ b/crates/ra_lsp_server/src/main_loop/pending_requests.rs | |||
@@ -1,17 +1,18 @@ | |||
1 | use std::time::{Duration, Instant}; | 1 | use std::time::{Duration, Instant}; |
2 | 2 | ||
3 | use lsp_server::RequestId; | ||
3 | use rustc_hash::FxHashMap; | 4 | use rustc_hash::FxHashMap; |
4 | 5 | ||
5 | #[derive(Debug)] | 6 | #[derive(Debug)] |
6 | pub struct CompletedRequest { | 7 | pub struct CompletedRequest { |
7 | pub id: u64, | 8 | pub id: RequestId, |
8 | pub method: String, | 9 | pub method: String, |
9 | pub duration: Duration, | 10 | pub duration: Duration, |
10 | } | 11 | } |
11 | 12 | ||
12 | #[derive(Debug)] | 13 | #[derive(Debug)] |
13 | pub(crate) struct PendingRequest { | 14 | pub(crate) struct PendingRequest { |
14 | pub(crate) id: u64, | 15 | pub(crate) id: RequestId, |
15 | pub(crate) method: String, | 16 | pub(crate) method: String, |
16 | pub(crate) received: Instant, | 17 | pub(crate) received: Instant, |
17 | } | 18 | } |
@@ -28,20 +29,20 @@ impl From<PendingRequest> for CompletedRequest { | |||
28 | 29 | ||
29 | #[derive(Debug, Default)] | 30 | #[derive(Debug, Default)] |
30 | pub(crate) struct PendingRequests { | 31 | pub(crate) struct PendingRequests { |
31 | map: FxHashMap<u64, PendingRequest>, | 32 | map: FxHashMap<RequestId, PendingRequest>, |
32 | } | 33 | } |
33 | 34 | ||
34 | impl PendingRequests { | 35 | impl PendingRequests { |
35 | pub(crate) fn start(&mut self, request: PendingRequest) { | 36 | pub(crate) fn start(&mut self, request: PendingRequest) { |
36 | let id = request.id; | 37 | let id = request.id.clone(); |
37 | let prev = self.map.insert(id, request); | 38 | let prev = self.map.insert(id.clone(), request); |
38 | assert!(prev.is_none(), "duplicate request with id {}", id); | 39 | assert!(prev.is_none(), "duplicate request with id {}", id); |
39 | } | 40 | } |
40 | pub(crate) fn cancel(&mut self, id: u64) -> bool { | 41 | pub(crate) fn cancel(&mut self, id: &RequestId) -> bool { |
41 | self.map.remove(&id).is_some() | 42 | self.map.remove(id).is_some() |
42 | } | 43 | } |
43 | pub(crate) fn finish(&mut self, id: u64) -> Option<CompletedRequest> { | 44 | pub(crate) fn finish(&mut self, id: &RequestId) -> Option<CompletedRequest> { |
44 | self.map.remove(&id).map(CompletedRequest::from) | 45 | self.map.remove(id).map(CompletedRequest::from) |
45 | } | 46 | } |
46 | } | 47 | } |
47 | 48 | ||
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index cc7964469..73d7f8fb9 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs | |||
@@ -4,7 +4,7 @@ use std::{ | |||
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crossbeam_channel::{unbounded, Receiver}; | 6 | use crossbeam_channel::{unbounded, Receiver}; |
7 | use gen_lsp_server::ErrorCode; | 7 | use lsp_server::ErrorCode; |
8 | use lsp_types::Url; | 8 | use lsp_types::Url; |
9 | use parking_lot::RwLock; | 9 | use parking_lot::RwLock; |
10 | use ra_ide_api::{ | 10 | use ra_ide_api::{ |
diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs index 055c8fff2..45b4cacf6 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/support.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs | |||
@@ -8,13 +8,10 @@ use std::{ | |||
8 | 8 | ||
9 | use crossbeam_channel::{after, select, Receiver}; | 9 | use crossbeam_channel::{after, select, Receiver}; |
10 | use flexi_logger::Logger; | 10 | use flexi_logger::Logger; |
11 | use gen_lsp_server::{RawMessage, RawNotification, RawRequest}; | 11 | use lsp_server::{Message, Notification, Request}; |
12 | use lsp_types::{ | 12 | use lsp_types::{ |
13 | notification::DidOpenTextDocument, | 13 | request::Shutdown, ClientCapabilities, DidOpenTextDocumentParams, GotoCapability, |
14 | notification::{Notification, ShowMessage}, | 14 | TextDocumentClientCapabilities, TextDocumentIdentifier, TextDocumentItem, Url, |
15 | request::{Request, Shutdown}, | ||
16 | ClientCapabilities, DidOpenTextDocumentParams, GotoCapability, TextDocumentClientCapabilities, | ||
17 | TextDocumentIdentifier, TextDocumentItem, Url, | ||
18 | }; | 15 | }; |
19 | use serde::Serialize; | 16 | use serde::Serialize; |
20 | use serde_json::{to_string_pretty, Value}; | 17 | use serde_json::{to_string_pretty, Value}; |
@@ -84,9 +81,9 @@ pub fn project(fixture: &str) -> Server { | |||
84 | 81 | ||
85 | pub struct Server { | 82 | pub struct Server { |
86 | req_id: Cell<u64>, | 83 | req_id: Cell<u64>, |
87 | messages: RefCell<Vec<RawMessage>>, | 84 | messages: RefCell<Vec<Message>>, |
88 | dir: TempDir, | 85 | dir: TempDir, |
89 | worker: Worker<RawMessage, RawMessage>, | 86 | worker: Worker<Message, Message>, |
90 | } | 87 | } |
91 | 88 | ||
92 | impl Server { | 89 | impl Server { |
@@ -100,7 +97,7 @@ impl Server { | |||
100 | 97 | ||
101 | let roots = if roots.is_empty() { vec![path] } else { roots }; | 98 | let roots = if roots.is_empty() { vec![path] } else { roots }; |
102 | 99 | ||
103 | let worker = Worker::<RawMessage, RawMessage>::spawn( | 100 | let worker = Worker::<Message, Message>::spawn( |
104 | "test server", | 101 | "test server", |
105 | 128, | 102 | 128, |
106 | move |msg_receiver, msg_sender| { | 103 | move |msg_receiver, msg_sender| { |
@@ -128,7 +125,8 @@ impl Server { | |||
128 | let res = Server { req_id: Cell::new(1), dir, messages: Default::default(), worker }; | 125 | let res = Server { req_id: Cell::new(1), dir, messages: Default::default(), worker }; |
129 | 126 | ||
130 | for (path, text) in files { | 127 | for (path, text) in files { |
131 | res.send_notification(RawNotification::new::<DidOpenTextDocument>( | 128 | res.send_notification(Notification::new( |
129 | "textDocument/didOpen".to_string(), | ||
132 | &DidOpenTextDocumentParams { | 130 | &DidOpenTextDocumentParams { |
133 | text_document: TextDocumentItem { | 131 | text_document: TextDocumentItem { |
134 | uri: Url::from_file_path(path).unwrap(), | 132 | uri: Url::from_file_path(path).unwrap(), |
@@ -149,16 +147,16 @@ impl Server { | |||
149 | 147 | ||
150 | pub fn notification<N>(&self, params: N::Params) | 148 | pub fn notification<N>(&self, params: N::Params) |
151 | where | 149 | where |
152 | N: Notification, | 150 | N: lsp_types::notification::Notification, |
153 | N::Params: Serialize, | 151 | N::Params: Serialize, |
154 | { | 152 | { |
155 | let r = RawNotification::new::<N>(¶ms); | 153 | let r = Notification::new(N::METHOD.to_string(), params); |
156 | self.send_notification(r) | 154 | self.send_notification(r) |
157 | } | 155 | } |
158 | 156 | ||
159 | pub fn request<R>(&self, params: R::Params, expected_resp: Value) | 157 | pub fn request<R>(&self, params: R::Params, expected_resp: Value) |
160 | where | 158 | where |
161 | R: Request, | 159 | R: lsp_types::request::Request, |
162 | R::Params: Serialize, | 160 | R::Params: Serialize, |
163 | { | 161 | { |
164 | let actual = self.send_request::<R>(params); | 162 | let actual = self.send_request::<R>(params); |
@@ -175,23 +173,23 @@ impl Server { | |||
175 | 173 | ||
176 | pub fn send_request<R>(&self, params: R::Params) -> Value | 174 | pub fn send_request<R>(&self, params: R::Params) -> Value |
177 | where | 175 | where |
178 | R: Request, | 176 | R: lsp_types::request::Request, |
179 | R::Params: Serialize, | 177 | R::Params: Serialize, |
180 | { | 178 | { |
181 | let id = self.req_id.get(); | 179 | let id = self.req_id.get(); |
182 | self.req_id.set(id + 1); | 180 | self.req_id.set(id + 1); |
183 | 181 | ||
184 | let r = RawRequest::new::<R>(id, ¶ms); | 182 | let r = Request::new(id.into(), R::METHOD.to_string(), params); |
185 | self.send_request_(r) | 183 | self.send_request_(r) |
186 | } | 184 | } |
187 | fn send_request_(&self, r: RawRequest) -> Value { | 185 | fn send_request_(&self, r: Request) -> Value { |
188 | let id = r.id; | 186 | let id = r.id.clone(); |
189 | self.worker.sender().send(RawMessage::Request(r)).unwrap(); | 187 | self.worker.sender().send(r.into()).unwrap(); |
190 | while let Some(msg) = self.recv() { | 188 | while let Some(msg) = self.recv() { |
191 | match msg { | 189 | match msg { |
192 | RawMessage::Request(req) => panic!("unexpected request: {:?}", req), | 190 | Message::Request(req) => panic!("unexpected request: {:?}", req), |
193 | RawMessage::Notification(_) => (), | 191 | Message::Notification(_) => (), |
194 | RawMessage::Response(res) => { | 192 | Message::Response(res) => { |
195 | assert_eq!(res.id, id); | 193 | assert_eq!(res.id, id); |
196 | if let Some(err) = res.error { | 194 | if let Some(err) = res.error { |
197 | panic!("error response: {:#?}", err); | 195 | panic!("error response: {:#?}", err); |
@@ -203,15 +201,16 @@ impl Server { | |||
203 | panic!("no response"); | 201 | panic!("no response"); |
204 | } | 202 | } |
205 | pub fn wait_until_workspace_is_loaded(&self) { | 203 | pub fn wait_until_workspace_is_loaded(&self) { |
206 | self.wait_for_message_cond(1, &|msg: &RawMessage| match msg { | 204 | self.wait_for_message_cond(1, &|msg: &Message| match msg { |
207 | RawMessage::Notification(n) if n.method == ShowMessage::METHOD => { | 205 | Message::Notification(n) if n.method == "window/showMessage" => { |
208 | let msg = n.clone().cast::<req::ShowMessage>().unwrap(); | 206 | let msg = |
207 | n.clone().extract::<req::ShowMessageParams>("window/showMessage").unwrap(); | ||
209 | msg.message.starts_with("workspace loaded") | 208 | msg.message.starts_with("workspace loaded") |
210 | } | 209 | } |
211 | _ => false, | 210 | _ => false, |
212 | }) | 211 | }) |
213 | } | 212 | } |
214 | fn wait_for_message_cond(&self, n: usize, cond: &dyn Fn(&RawMessage) -> bool) { | 213 | fn wait_for_message_cond(&self, n: usize, cond: &dyn Fn(&Message) -> bool) { |
215 | let mut total = 0; | 214 | let mut total = 0; |
216 | for msg in self.messages.borrow().iter() { | 215 | for msg in self.messages.borrow().iter() { |
217 | if cond(msg) { | 216 | if cond(msg) { |
@@ -225,14 +224,14 @@ impl Server { | |||
225 | } | 224 | } |
226 | } | 225 | } |
227 | } | 226 | } |
228 | fn recv(&self) -> Option<RawMessage> { | 227 | fn recv(&self) -> Option<Message> { |
229 | recv_timeout(&self.worker.receiver()).map(|msg| { | 228 | recv_timeout(&self.worker.receiver()).map(|msg| { |
230 | self.messages.borrow_mut().push(msg.clone()); | 229 | self.messages.borrow_mut().push(msg.clone()); |
231 | msg | 230 | msg |
232 | }) | 231 | }) |
233 | } | 232 | } |
234 | fn send_notification(&self, not: RawNotification) { | 233 | fn send_notification(&self, not: Notification) { |
235 | self.worker.sender().send(RawMessage::Notification(not)).unwrap(); | 234 | self.worker.sender().send(Message::Notification(not)).unwrap(); |
236 | } | 235 | } |
237 | 236 | ||
238 | pub fn path(&self) -> &Path { | 237 | pub fn path(&self) -> &Path { |
@@ -246,7 +245,7 @@ impl Drop for Server { | |||
246 | } | 245 | } |
247 | } | 246 | } |
248 | 247 | ||
249 | fn recv_timeout(receiver: &Receiver<RawMessage>) -> Option<RawMessage> { | 248 | fn recv_timeout(receiver: &Receiver<Message>) -> Option<Message> { |
250 | let timeout = Duration::from_secs(120); | 249 | let timeout = Duration::from_secs(120); |
251 | select! { | 250 | select! { |
252 | recv(receiver) -> msg => msg.ok(), | 251 | recv(receiver) -> msg => msg.ok(), |