diff options
-rw-r--r-- | Cargo.lock | 46 | ||||
-rw-r--r-- | crates/gen_lsp_server/Cargo.toml | 18 | ||||
-rw-r--r-- | crates/gen_lsp_server/examples/01_gen_lsp_server.rs | 47 | ||||
-rw-r--r-- | crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs | 120 | ||||
-rw-r--r-- | crates/gen_lsp_server/src/lib.rs | 136 | ||||
-rw-r--r-- | crates/gen_lsp_server/src/msg.rs | 205 | ||||
-rw-r--r-- | crates/gen_lsp_server/src/stdio.rs | 57 | ||||
-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 |
14 files changed, 141 insertions, 725 deletions
diff --git a/Cargo.lock b/Cargo.lock index 93814bedc..17e60aa82 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -216,23 +216,6 @@ dependencies = [ | |||
216 | 216 | ||
217 | [[package]] | 217 | [[package]] |
218 | name = "console" | 218 | name = "console" |
219 | version = "0.7.7" | ||
220 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
221 | dependencies = [ | ||
222 | "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", | ||
223 | "clicolors-control 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
224 | "encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||
225 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
226 | "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", | ||
227 | "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
228 | "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||
229 | "termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||
230 | "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||
231 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||
232 | ] | ||
233 | |||
234 | [[package]] | ||
235 | name = "console" | ||
236 | version = "0.8.0" | 219 | version = "0.8.0" |
237 | source = "registry+https://github.com/rust-lang/crates.io-index" | 220 | source = "registry+https://github.com/rust-lang/crates.io-index" |
238 | dependencies = [ | 221 | dependencies = [ |
@@ -473,18 +456,6 @@ version = "0.3.3" | |||
473 | source = "registry+https://github.com/rust-lang/crates.io-index" | 456 | source = "registry+https://github.com/rust-lang/crates.io-index" |
474 | 457 | ||
475 | [[package]] | 458 | [[package]] |
476 | name = "gen_lsp_server" | ||
477 | version = "0.2.0" | ||
478 | dependencies = [ | ||
479 | "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", | ||
480 | "flexi_logger 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||
481 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||
482 | "lsp-types 0.60.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
483 | "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", | ||
484 | "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", | ||
485 | ] | ||
486 | |||
487 | [[package]] | ||
488 | name = "getrandom" | 459 | name = "getrandom" |
489 | version = "0.1.10" | 460 | version = "0.1.10" |
490 | source = "registry+https://github.com/rust-lang/crates.io-index" | 461 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -539,7 +510,7 @@ name = "indicatif" | |||
539 | version = "0.11.0" | 510 | version = "0.11.0" |
540 | source = "registry+https://github.com/rust-lang/crates.io-index" | 511 | source = "registry+https://github.com/rust-lang/crates.io-index" |
541 | dependencies = [ | 512 | dependencies = [ |
542 | "console 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", | 513 | "console 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", |
543 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | 514 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", |
544 | "number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | 515 | "number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", |
545 | "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", | 516 | "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", |
@@ -690,6 +661,17 @@ dependencies = [ | |||
690 | ] | 661 | ] |
691 | 662 | ||
692 | [[package]] | 663 | [[package]] |
664 | name = "lsp-server" | ||
665 | version = "0.1.0" | ||
666 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
667 | dependencies = [ | ||
668 | "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", | ||
669 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||
670 | "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", | ||
671 | "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", | ||
672 | ] | ||
673 | |||
674 | [[package]] | ||
693 | name = "lsp-types" | 675 | name = "lsp-types" |
694 | version = "0.60.0" | 676 | version = "0.60.0" |
695 | source = "registry+https://github.com/rust-lang/crates.io-index" | 677 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1092,8 +1074,8 @@ version = "0.1.0" | |||
1092 | dependencies = [ | 1074 | dependencies = [ |
1093 | "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", | 1075 | "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", |
1094 | "flexi_logger 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", | 1076 | "flexi_logger 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", |
1095 | "gen_lsp_server 0.2.0", | ||
1096 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", | 1077 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", |
1078 | "lsp-server 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
1097 | "lsp-types 0.60.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1079 | "lsp-types 0.60.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1098 | "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1080 | "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1099 | "ra_ide_api 0.1.0", | 1081 | "ra_ide_api 0.1.0", |
@@ -1899,7 +1881,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
1899 | "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" | 1881 | "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" |
1900 | "checksum clicolors-control 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "73abfd4c73d003a674ce5d2933fca6ce6c42480ea84a5ffe0a2dc39ed56300f9" | 1882 | "checksum clicolors-control 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "73abfd4c73d003a674ce5d2933fca6ce6c42480ea84a5ffe0a2dc39ed56300f9" |
1901 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" | 1883 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" |
1902 | "checksum console 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ca57c2c14b8a2bf3105bc9d15574aad80babf6a9c44b1058034cdf8bd169628" | ||
1903 | "checksum console 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b147390a412132d75d10dd3b7b175a69cf5fd95032f7503c7091b8831ba10242" | 1884 | "checksum console 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b147390a412132d75d10dd3b7b175a69cf5fd95032f7503c7091b8831ba10242" |
1904 | "checksum crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c" | 1885 | "checksum crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c" |
1905 | "checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" | 1886 | "checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" |
@@ -1956,6 +1937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
1956 | "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" | 1937 | "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" |
1957 | "checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" | 1938 | "checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" |
1958 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" | 1939 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" |
1940 | "checksum lsp-server 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "47632ec528046c1a39f14448f1ee7d66d4b7b83e1771590b62e6c08665dea053" | ||
1959 | "checksum lsp-types 0.60.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fe3edefcd66dde1f7f1df706f46520a3c93adc5ca4bc5747da6621195e894efd" | 1941 | "checksum lsp-types 0.60.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fe3edefcd66dde1f7f1df706f46520a3c93adc5ca4bc5747da6621195e894efd" |
1960 | "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" | 1942 | "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" |
1961 | "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" | 1943 | "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" |
diff --git a/crates/gen_lsp_server/Cargo.toml b/crates/gen_lsp_server/Cargo.toml deleted file mode 100644 index 7011aa1bf..000000000 --- a/crates/gen_lsp_server/Cargo.toml +++ /dev/null | |||
@@ -1,18 +0,0 @@ | |||
1 | [package] | ||
2 | edition = "2018" | ||
3 | name = "gen_lsp_server" | ||
4 | version = "0.2.0" | ||
5 | authors = ["rust-analyzer developers"] | ||
6 | repository = "https://github.com/rust-analyzer/rust-analyzer" | ||
7 | license = "MIT OR Apache-2.0" | ||
8 | description = "Generic LSP server scaffold." | ||
9 | |||
10 | [dependencies] | ||
11 | lsp-types = "0.60.0" | ||
12 | log = "0.4.3" | ||
13 | serde_json = "1.0.34" | ||
14 | serde = { version = "1.0.83", features = ["derive"] } | ||
15 | crossbeam-channel = "0.3.5" | ||
16 | |||
17 | [dev-dependencies] | ||
18 | flexi_logger = "0.14.0" | ||
diff --git a/crates/gen_lsp_server/examples/01_gen_lsp_server.rs b/crates/gen_lsp_server/examples/01_gen_lsp_server.rs deleted file mode 100644 index f49965064..000000000 --- a/crates/gen_lsp_server/examples/01_gen_lsp_server.rs +++ /dev/null | |||
@@ -1,47 +0,0 @@ | |||
1 | use std::error::Error; | ||
2 | |||
3 | use crossbeam_channel::{Receiver, Sender}; | ||
4 | use gen_lsp_server::{handle_shutdown, run_server, stdio_transport, RawMessage, RawResponse}; | ||
5 | use lsp_types::{ | ||
6 | request::{GotoDefinition, GotoDefinitionResponse}, | ||
7 | InitializeParams, ServerCapabilities, | ||
8 | }; | ||
9 | |||
10 | fn main() -> Result<(), Box<dyn Error + Sync + Send>> { | ||
11 | let (receiver, sender, io_threads) = stdio_transport(); | ||
12 | run_server(ServerCapabilities::default(), receiver, sender, main_loop)?; | ||
13 | io_threads.join()?; | ||
14 | Ok(()) | ||
15 | } | ||
16 | |||
17 | fn main_loop( | ||
18 | _params: InitializeParams, | ||
19 | receiver: &Receiver<RawMessage>, | ||
20 | sender: &Sender<RawMessage>, | ||
21 | ) -> Result<(), Box<dyn Error + Sync + Send>> { | ||
22 | for msg in receiver { | ||
23 | match msg { | ||
24 | RawMessage::Request(req) => { | ||
25 | let req = match handle_shutdown(req, sender) { | ||
26 | None => return Ok(()), | ||
27 | Some(req) => req, | ||
28 | }; | ||
29 | match req.cast::<GotoDefinition>() { | ||
30 | Ok((id, _params)) => { | ||
31 | let resp = RawResponse::ok::<GotoDefinition>( | ||
32 | id, | ||
33 | &Some(GotoDefinitionResponse::Array(Vec::new())), | ||
34 | ); | ||
35 | sender.send(RawMessage::Response(resp))?; | ||
36 | continue; | ||
37 | } | ||
38 | Err(req) => req, | ||
39 | }; | ||
40 | // ... | ||
41 | } | ||
42 | RawMessage::Response(_resp) => (), | ||
43 | RawMessage::Notification(_not) => (), | ||
44 | } | ||
45 | } | ||
46 | Ok(()) | ||
47 | } | ||
diff --git a/crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs b/crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs deleted file mode 100644 index 3c48106c5..000000000 --- a/crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs +++ /dev/null | |||
@@ -1,120 +0,0 @@ | |||
1 | //! A minimal example LSP server that can only respond to the `gotoDefinition` request. To use | ||
2 | //! this example, execute it and then send an `initialize` request. | ||
3 | //! | ||
4 | //! ```no_run | ||
5 | //! Content-Length: 85 | ||
6 | //! | ||
7 | //! {"jsonrpc": "2.0", "method": "initialize", "id": 1, "params": {"capabilities": {}}} | ||
8 | //! ``` | ||
9 | //! | ||
10 | //! This will respond with a server respose. Then send it a `initialized` notification which will | ||
11 | //! have no response. | ||
12 | //! | ||
13 | //! ```no_run | ||
14 | //! Content-Length: 59 | ||
15 | //! | ||
16 | //! {"jsonrpc": "2.0", "method": "initialized", "params": {}} | ||
17 | //! ``` | ||
18 | //! | ||
19 | //! Once these two are sent, then we enter the main loop of the server. The only request this | ||
20 | //! example can handle is `gotoDefinition`: | ||
21 | //! | ||
22 | //! ```no_run | ||
23 | //! Content-Length: 159 | ||
24 | //! | ||
25 | //! {"jsonrpc": "2.0", "method": "textDocument/definition", "id": 2, "params": {"textDocument": {"uri": "file://temp"}, "position": {"line": 1, "character": 1}}} | ||
26 | //! ``` | ||
27 | //! | ||
28 | //! To finish up without errors, send a shutdown request: | ||
29 | //! | ||
30 | //! ```no_run | ||
31 | //! Content-Length: 67 | ||
32 | //! | ||
33 | //! {"jsonrpc": "2.0", "method": "shutdown", "id": 3, "params": null} | ||
34 | //! ``` | ||
35 | //! | ||
36 | //! The server will exit the main loop and finally we send a `shutdown` notification to stop | ||
37 | //! the server. | ||
38 | //! | ||
39 | //! ``` | ||
40 | //! Content-Length: 54 | ||
41 | //! | ||
42 | //! {"jsonrpc": "2.0", "method": "exit", "params": null} | ||
43 | //! ``` | ||
44 | |||
45 | use std::error::Error; | ||
46 | |||
47 | use crossbeam_channel::{Receiver, Sender}; | ||
48 | use gen_lsp_server::{ | ||
49 | handle_shutdown, run_server, stdio_transport, RawMessage, RawRequest, RawResponse, | ||
50 | }; | ||
51 | use log::info; | ||
52 | use lsp_types::{ | ||
53 | request::{GotoDefinition, GotoDefinitionResponse}, | ||
54 | InitializeParams, ServerCapabilities, | ||
55 | }; | ||
56 | |||
57 | fn main() -> Result<(), Box<dyn Error + Sync + Send>> { | ||
58 | // Set up logging. Because `stdio_transport` gets a lock on stdout and stdin, we must have | ||
59 | // our logging only write out to stderr. | ||
60 | flexi_logger::Logger::with_str("info").start().unwrap(); | ||
61 | info!("starting generic LSP server"); | ||
62 | |||
63 | // Create the transport. Includes the stdio (stdin and stdout) versions but this could | ||
64 | // also be implemented to use sockets or HTTP. | ||
65 | let (receiver, sender, io_threads) = stdio_transport(); | ||
66 | |||
67 | // Run the server and wait for the two threads to end (typically by trigger LSP Exit event). | ||
68 | run_server(ServerCapabilities::default(), receiver, sender, main_loop)?; | ||
69 | io_threads.join()?; | ||
70 | |||
71 | // Shut down gracefully. | ||
72 | info!("shutting down server"); | ||
73 | Ok(()) | ||
74 | } | ||
75 | |||
76 | fn main_loop( | ||
77 | _params: InitializeParams, | ||
78 | receiver: &Receiver<RawMessage>, | ||
79 | sender: &Sender<RawMessage>, | ||
80 | ) -> Result<(), Box<dyn Error + Sync + Send>> { | ||
81 | info!("starting example main loop"); | ||
82 | for msg in receiver { | ||
83 | info!("got msg: {:?}", msg); | ||
84 | match msg { | ||
85 | RawMessage::Request(req) => { | ||
86 | let req = match log_handle_shutdown(req, sender) { | ||
87 | None => return Ok(()), | ||
88 | Some(req) => req, | ||
89 | }; | ||
90 | info!("got request: {:?}", req); | ||
91 | match req.cast::<GotoDefinition>() { | ||
92 | Ok((id, params)) => { | ||
93 | info!("got gotoDefinition request #{}: {:?}", id, params); | ||
94 | let resp = RawResponse::ok::<GotoDefinition>( | ||
95 | id, | ||
96 | &Some(GotoDefinitionResponse::Array(Vec::new())), | ||
97 | ); | ||
98 | info!("sending gotoDefinition response: {:?}", resp); | ||
99 | sender.send(RawMessage::Response(resp))?; | ||
100 | continue; | ||
101 | } | ||
102 | Err(req) => req, | ||
103 | }; | ||
104 | // ... | ||
105 | } | ||
106 | RawMessage::Response(resp) => { | ||
107 | info!("got response: {:?}", resp); | ||
108 | } | ||
109 | RawMessage::Notification(not) => { | ||
110 | info!("got notification: {:?}", not); | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | Ok(()) | ||
115 | } | ||
116 | |||
117 | pub fn log_handle_shutdown(req: RawRequest, sender: &Sender<RawMessage>) -> Option<RawRequest> { | ||
118 | info!("handle_shutdown: {:?}", req); | ||
119 | handle_shutdown(req, sender) | ||
120 | } | ||
diff --git a/crates/gen_lsp_server/src/lib.rs b/crates/gen_lsp_server/src/lib.rs deleted file mode 100644 index 0984e3e25..000000000 --- a/crates/gen_lsp_server/src/lib.rs +++ /dev/null | |||
@@ -1,136 +0,0 @@ | |||
1 | //! A language server scaffold, exposing a synchronous crossbeam-channel based API. | ||
2 | //! This crate handles protocol handshaking and parsing messages, while you | ||
3 | //! control the message dispatch loop yourself. | ||
4 | //! | ||
5 | //! Run with `RUST_LOG=gen_lsp_server=debug` to see all the messages. | ||
6 | //! | ||
7 | //! ```no_run | ||
8 | //! use std::error::Error; | ||
9 | //! use crossbeam_channel::{Sender, Receiver}; | ||
10 | //! use lsp_types::{ServerCapabilities, InitializeParams, request::{GotoDefinition, GotoDefinitionResponse}}; | ||
11 | //! use gen_lsp_server::{run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse}; | ||
12 | //! | ||
13 | //! fn main() -> Result<(), Box<dyn Error + Send + Sync>> { | ||
14 | //! let (receiver, sender, io_threads) = stdio_transport(); | ||
15 | //! run_server( | ||
16 | //! ServerCapabilities::default(), | ||
17 | //! receiver, | ||
18 | //! sender, | ||
19 | //! main_loop, | ||
20 | //! )?; | ||
21 | //! io_threads.join()?; | ||
22 | //! Ok(()) | ||
23 | //! } | ||
24 | //! | ||
25 | //! fn main_loop( | ||
26 | //! _params: InitializeParams, | ||
27 | //! receiver: &Receiver<RawMessage>, | ||
28 | //! sender: &Sender<RawMessage>, | ||
29 | //! ) -> Result<(), Box<dyn Error + Send + Sync>> { | ||
30 | //! for msg in receiver { | ||
31 | //! match msg { | ||
32 | //! RawMessage::Request(req) => { | ||
33 | //! let req = match handle_shutdown(req, sender) { | ||
34 | //! None => return Ok(()), | ||
35 | //! Some(req) => req, | ||
36 | //! }; | ||
37 | //! match req.cast::<GotoDefinition>() { | ||
38 | //! Ok((id, _params)) => { | ||
39 | //! let resp = RawResponse::ok::<GotoDefinition>( | ||
40 | //! id, | ||
41 | //! &Some(GotoDefinitionResponse::Array(Vec::new())), | ||
42 | //! ); | ||
43 | //! sender.send(RawMessage::Response(resp))?; | ||
44 | //! continue; | ||
45 | //! }, | ||
46 | //! Err(req) => req, | ||
47 | //! }; | ||
48 | //! // ... | ||
49 | //! } | ||
50 | //! RawMessage::Response(_resp) => (), | ||
51 | //! RawMessage::Notification(_not) => (), | ||
52 | //! } | ||
53 | //! } | ||
54 | //! Ok(()) | ||
55 | //! } | ||
56 | //! ``` | ||
57 | |||
58 | use std::error::Error; | ||
59 | |||
60 | mod msg; | ||
61 | mod stdio; | ||
62 | |||
63 | use crossbeam_channel::{Receiver, Sender}; | ||
64 | use lsp_types::{ | ||
65 | notification::{Exit, Initialized}, | ||
66 | request::{Initialize, Shutdown}, | ||
67 | InitializeParams, InitializeResult, ServerCapabilities, | ||
68 | }; | ||
69 | |||
70 | pub type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>; | ||
71 | pub use crate::{ | ||
72 | msg::{ErrorCode, RawMessage, RawNotification, RawRequest, RawResponse, RawResponseError}, | ||
73 | stdio::{stdio_transport, Threads}, | ||
74 | }; | ||
75 | |||
76 | /// Main entry point: runs the server from initialization to shutdown. | ||
77 | /// To attach server to standard input/output streams, use the `stdio_transport` | ||
78 | /// function to create corresponding `sender` and `receiver` pair. | ||
79 | /// | ||
80 | /// `server` should use the `handle_shutdown` function to handle the `Shutdown` | ||
81 | /// request. | ||
82 | pub fn run_server( | ||
83 | caps: ServerCapabilities, | ||
84 | receiver: Receiver<RawMessage>, | ||
85 | sender: Sender<RawMessage>, | ||
86 | server: impl FnOnce(InitializeParams, &Receiver<RawMessage>, &Sender<RawMessage>) -> Result<()>, | ||
87 | ) -> Result<()> { | ||
88 | log::info!("lsp server initializes"); | ||
89 | let params = initialize(&receiver, &sender, caps)?; | ||
90 | log::info!("lsp server initialized, serving requests"); | ||
91 | server(params, &receiver, &sender)?; | ||
92 | log::info!("lsp server waiting for exit notification"); | ||
93 | match receiver.recv() { | ||
94 | Ok(RawMessage::Notification(n)) => n | ||
95 | .cast::<Exit>() | ||
96 | .map_err(|n| format!("unexpected notification during shutdown: {:?}", n))?, | ||
97 | m => Err(format!("unexpected message during shutdown: {:?}", m))?, | ||
98 | } | ||
99 | log::info!("lsp server shutdown complete"); | ||
100 | Ok(()) | ||
101 | } | ||
102 | |||
103 | /// If `req` is `Shutdown`, respond to it and return `None`, otherwise return `Some(req)` | ||
104 | pub fn handle_shutdown(req: RawRequest, sender: &Sender<RawMessage>) -> Option<RawRequest> { | ||
105 | match req.cast::<Shutdown>() { | ||
106 | Ok((id, ())) => { | ||
107 | let resp = RawResponse::ok::<Shutdown>(id, &()); | ||
108 | let _ = sender.send(RawMessage::Response(resp)); | ||
109 | None | ||
110 | } | ||
111 | Err(req) => Some(req), | ||
112 | } | ||
113 | } | ||
114 | |||
115 | fn initialize( | ||
116 | receiver: &Receiver<RawMessage>, | ||
117 | sender: &Sender<RawMessage>, | ||
118 | caps: ServerCapabilities, | ||
119 | ) -> Result<InitializeParams> { | ||
120 | let (id, params) = match receiver.recv() { | ||
121 | Ok(RawMessage::Request(req)) => match req.cast::<Initialize>() { | ||
122 | Err(req) => Err(format!("expected initialize request, got {:?}", req))?, | ||
123 | Ok(req) => req, | ||
124 | }, | ||
125 | msg => Err(format!("expected initialize request, got {:?}", msg))?, | ||
126 | }; | ||
127 | let resp = RawResponse::ok::<Initialize>(id, &InitializeResult { capabilities: caps }); | ||
128 | sender.send(RawMessage::Response(resp)).unwrap(); | ||
129 | match receiver.recv() { | ||
130 | Ok(RawMessage::Notification(n)) => { | ||
131 | n.cast::<Initialized>().map_err(|_| "expected initialized notification")?; | ||
132 | } | ||
133 | _ => Err("expected initialized notification".to_string())?, | ||
134 | } | ||
135 | Ok(params) | ||
136 | } | ||
diff --git a/crates/gen_lsp_server/src/msg.rs b/crates/gen_lsp_server/src/msg.rs deleted file mode 100644 index 2928e4f8b..000000000 --- a/crates/gen_lsp_server/src/msg.rs +++ /dev/null | |||
@@ -1,205 +0,0 @@ | |||
1 | use std::io::{BufRead, Write}; | ||
2 | |||
3 | use lsp_types::{notification::Notification, request::Request}; | ||
4 | use serde::{Deserialize, Serialize}; | ||
5 | use serde_json::{from_str, from_value, to_string, to_value, Value}; | ||
6 | |||
7 | use crate::Result; | ||
8 | |||
9 | #[derive(Serialize, Deserialize, Debug, Clone)] | ||
10 | #[serde(untagged)] | ||
11 | pub enum RawMessage { | ||
12 | Request(RawRequest), | ||
13 | Notification(RawNotification), | ||
14 | Response(RawResponse), | ||
15 | } | ||
16 | |||
17 | impl From<RawRequest> for RawMessage { | ||
18 | fn from(raw: RawRequest) -> RawMessage { | ||
19 | RawMessage::Request(raw) | ||
20 | } | ||
21 | } | ||
22 | |||
23 | impl From<RawNotification> for RawMessage { | ||
24 | fn from(raw: RawNotification) -> RawMessage { | ||
25 | RawMessage::Notification(raw) | ||
26 | } | ||
27 | } | ||
28 | |||
29 | impl From<RawResponse> for RawMessage { | ||
30 | fn from(raw: RawResponse) -> RawMessage { | ||
31 | RawMessage::Response(raw) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
36 | pub struct RawRequest { | ||
37 | pub id: u64, | ||
38 | pub method: String, | ||
39 | pub params: Value, | ||
40 | } | ||
41 | |||
42 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
43 | pub struct RawResponse { | ||
44 | // JSON RPC allows this to be null if it was impossible | ||
45 | // to decode the request's id. Ignore this special case | ||
46 | // and just die horribly. | ||
47 | pub id: u64, | ||
48 | #[serde(skip_serializing_if = "Option::is_none")] | ||
49 | pub result: Option<Value>, | ||
50 | #[serde(skip_serializing_if = "Option::is_none")] | ||
51 | pub error: Option<RawResponseError>, | ||
52 | } | ||
53 | |||
54 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
55 | pub struct RawResponseError { | ||
56 | pub code: i32, | ||
57 | pub message: String, | ||
58 | #[serde(skip_serializing_if = "Option::is_none")] | ||
59 | pub data: Option<Value>, | ||
60 | } | ||
61 | |||
62 | #[derive(Clone, Copy, Debug)] | ||
63 | #[allow(unused)] | ||
64 | pub enum ErrorCode { | ||
65 | ParseError = -32700, | ||
66 | InvalidRequest = -32600, | ||
67 | MethodNotFound = -32601, | ||
68 | InvalidParams = -32602, | ||
69 | InternalError = -32603, | ||
70 | ServerErrorStart = -32099, | ||
71 | ServerErrorEnd = -32000, | ||
72 | ServerNotInitialized = -32002, | ||
73 | UnknownErrorCode = -32001, | ||
74 | RequestCanceled = -32800, | ||
75 | ContentModified = -32801, | ||
76 | } | ||
77 | |||
78 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
79 | pub struct RawNotification { | ||
80 | pub method: String, | ||
81 | pub params: Value, | ||
82 | } | ||
83 | |||
84 | impl RawMessage { | ||
85 | pub fn read(r: &mut impl BufRead) -> Result<Option<RawMessage>> { | ||
86 | let text = match read_msg_text(r)? { | ||
87 | None => return Ok(None), | ||
88 | Some(text) => text, | ||
89 | }; | ||
90 | let msg = from_str(&text)?; | ||
91 | Ok(Some(msg)) | ||
92 | } | ||
93 | pub fn write(self, w: &mut impl Write) -> Result<()> { | ||
94 | #[derive(Serialize)] | ||
95 | struct JsonRpc { | ||
96 | jsonrpc: &'static str, | ||
97 | #[serde(flatten)] | ||
98 | msg: RawMessage, | ||
99 | } | ||
100 | let text = to_string(&JsonRpc { jsonrpc: "2.0", msg: self })?; | ||
101 | write_msg_text(w, &text)?; | ||
102 | Ok(()) | ||
103 | } | ||
104 | } | ||
105 | |||
106 | impl RawRequest { | ||
107 | pub fn new<R>(id: u64, params: &R::Params) -> RawRequest | ||
108 | where | ||
109 | R: Request, | ||
110 | R::Params: serde::Serialize, | ||
111 | { | ||
112 | RawRequest { id, method: R::METHOD.to_string(), params: to_value(params).unwrap() } | ||
113 | } | ||
114 | pub fn cast<R>(self) -> ::std::result::Result<(u64, R::Params), RawRequest> | ||
115 | where | ||
116 | R: Request, | ||
117 | R::Params: serde::de::DeserializeOwned, | ||
118 | { | ||
119 | if self.method != R::METHOD { | ||
120 | return Err(self); | ||
121 | } | ||
122 | let id = self.id; | ||
123 | let params: R::Params = from_value(self.params).unwrap(); | ||
124 | Ok((id, params)) | ||
125 | } | ||
126 | } | ||
127 | |||
128 | impl RawResponse { | ||
129 | pub fn ok<R>(id: u64, result: &R::Result) -> RawResponse | ||
130 | where | ||
131 | R: Request, | ||
132 | R::Result: serde::Serialize, | ||
133 | { | ||
134 | RawResponse { id, result: Some(to_value(&result).unwrap()), error: None } | ||
135 | } | ||
136 | pub fn err(id: u64, code: i32, message: String) -> RawResponse { | ||
137 | let error = RawResponseError { code, message, data: None }; | ||
138 | RawResponse { id, result: None, error: Some(error) } | ||
139 | } | ||
140 | } | ||
141 | |||
142 | impl RawNotification { | ||
143 | pub fn new<N>(params: &N::Params) -> RawNotification | ||
144 | where | ||
145 | N: Notification, | ||
146 | N::Params: serde::Serialize, | ||
147 | { | ||
148 | RawNotification { method: N::METHOD.to_string(), params: to_value(params).unwrap() } | ||
149 | } | ||
150 | pub fn is<N>(&self) -> bool | ||
151 | where | ||
152 | N: Notification, | ||
153 | { | ||
154 | self.method == N::METHOD | ||
155 | } | ||
156 | pub fn cast<N>(self) -> ::std::result::Result<N::Params, RawNotification> | ||
157 | where | ||
158 | N: Notification, | ||
159 | N::Params: serde::de::DeserializeOwned, | ||
160 | { | ||
161 | if !self.is::<N>() { | ||
162 | return Err(self); | ||
163 | } | ||
164 | Ok(from_value(self.params).unwrap()) | ||
165 | } | ||
166 | } | ||
167 | |||
168 | fn read_msg_text(inp: &mut impl BufRead) -> Result<Option<String>> { | ||
169 | let mut size = None; | ||
170 | let mut buf = String::new(); | ||
171 | loop { | ||
172 | buf.clear(); | ||
173 | if inp.read_line(&mut buf)? == 0 { | ||
174 | return Ok(None); | ||
175 | } | ||
176 | if !buf.ends_with("\r\n") { | ||
177 | Err(format!("malformed header: {:?}", buf))?; | ||
178 | } | ||
179 | let buf = &buf[..buf.len() - 2]; | ||
180 | if buf.is_empty() { | ||
181 | break; | ||
182 | } | ||
183 | let mut parts = buf.splitn(2, ": "); | ||
184 | let header_name = parts.next().unwrap(); | ||
185 | let header_value = parts.next().ok_or_else(|| format!("malformed header: {:?}", buf))?; | ||
186 | if header_name == "Content-Length" { | ||
187 | size = Some(header_value.parse::<usize>()?); | ||
188 | } | ||
189 | } | ||
190 | let size = size.ok_or("no Content-Length")?; | ||
191 | let mut buf = buf.into_bytes(); | ||
192 | buf.resize(size, 0); | ||
193 | inp.read_exact(&mut buf)?; | ||
194 | let buf = String::from_utf8(buf)?; | ||
195 | log::debug!("< {}", buf); | ||
196 | Ok(Some(buf)) | ||
197 | } | ||
198 | |||
199 | fn write_msg_text(out: &mut impl Write, msg: &str) -> Result<()> { | ||
200 | log::debug!("> {}", msg); | ||
201 | write!(out, "Content-Length: {}\r\n\r\n", msg.len())?; | ||
202 | out.write_all(msg.as_bytes())?; | ||
203 | out.flush()?; | ||
204 | Ok(()) | ||
205 | } | ||
diff --git a/crates/gen_lsp_server/src/stdio.rs b/crates/gen_lsp_server/src/stdio.rs deleted file mode 100644 index f8931f2dc..000000000 --- a/crates/gen_lsp_server/src/stdio.rs +++ /dev/null | |||
@@ -1,57 +0,0 @@ | |||
1 | use std::{ | ||
2 | io::{stdin, stdout}, | ||
3 | thread, | ||
4 | }; | ||
5 | |||
6 | use crossbeam_channel::{bounded, Receiver, Sender}; | ||
7 | use lsp_types::notification::Exit; | ||
8 | |||
9 | use crate::{RawMessage, Result}; | ||
10 | |||
11 | pub fn stdio_transport() -> (Receiver<RawMessage>, Sender<RawMessage>, Threads) { | ||
12 | let (writer_sender, writer_receiver) = bounded::<RawMessage>(16); | ||
13 | let writer = thread::spawn(move || { | ||
14 | let stdout = stdout(); | ||
15 | let mut stdout = stdout.lock(); | ||
16 | writer_receiver.into_iter().try_for_each(|it| it.write(&mut stdout))?; | ||
17 | Ok(()) | ||
18 | }); | ||
19 | let (reader_sender, reader_receiver) = bounded::<RawMessage>(16); | ||
20 | let reader = thread::spawn(move || { | ||
21 | let stdin = stdin(); | ||
22 | let mut stdin = stdin.lock(); | ||
23 | while let Some(msg) = RawMessage::read(&mut stdin)? { | ||
24 | let is_exit = match &msg { | ||
25 | RawMessage::Notification(n) => n.is::<Exit>(), | ||
26 | _ => false, | ||
27 | }; | ||
28 | |||
29 | reader_sender.send(msg).unwrap(); | ||
30 | |||
31 | if is_exit { | ||
32 | break; | ||
33 | } | ||
34 | } | ||
35 | Ok(()) | ||
36 | }); | ||
37 | let threads = Threads { reader, writer }; | ||
38 | (reader_receiver, writer_sender, threads) | ||
39 | } | ||
40 | |||
41 | pub struct Threads { | ||
42 | reader: thread::JoinHandle<Result<()>>, | ||
43 | writer: thread::JoinHandle<Result<()>>, | ||
44 | } | ||
45 | |||
46 | impl Threads { | ||
47 | pub fn join(self) -> Result<()> { | ||
48 | match self.reader.join() { | ||
49 | Ok(r) => r?, | ||
50 | Err(_) => Err("reader panicked")?, | ||
51 | } | ||
52 | match self.writer.join() { | ||
53 | Ok(r) => r, | ||
54 | Err(_) => Err("writer panicked")?, | ||
55 | } | ||
56 | } | ||
57 | } | ||
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(), |