aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock46
-rw-r--r--crates/gen_lsp_server/Cargo.toml18
-rw-r--r--crates/gen_lsp_server/examples/01_gen_lsp_server.rs47
-rw-r--r--crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs120
-rw-r--r--crates/gen_lsp_server/src/lib.rs136
-rw-r--r--crates/gen_lsp_server/src/msg.rs205
-rw-r--r--crates/gen_lsp_server/src/stdio.rs57
-rw-r--r--crates/ra_lsp_server/Cargo.toml2
-rw-r--r--crates/ra_lsp_server/src/main.rs14
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs141
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs2
-rw-r--r--crates/ra_lsp_server/src/main_loop/pending_requests.rs19
-rw-r--r--crates/ra_lsp_server/src/world.rs2
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/support.rs57
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]]
218name = "console" 218name = "console"
219version = "0.7.7"
220source = "registry+https://github.com/rust-lang/crates.io-index"
221dependencies = [
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]]
235name = "console"
236version = "0.8.0" 219version = "0.8.0"
237source = "registry+https://github.com/rust-lang/crates.io-index" 220source = "registry+https://github.com/rust-lang/crates.io-index"
238dependencies = [ 221dependencies = [
@@ -473,18 +456,6 @@ version = "0.3.3"
473source = "registry+https://github.com/rust-lang/crates.io-index" 456source = "registry+https://github.com/rust-lang/crates.io-index"
474 457
475[[package]] 458[[package]]
476name = "gen_lsp_server"
477version = "0.2.0"
478dependencies = [
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]]
488name = "getrandom" 459name = "getrandom"
489version = "0.1.10" 460version = "0.1.10"
490source = "registry+https://github.com/rust-lang/crates.io-index" 461source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -539,7 +510,7 @@ name = "indicatif"
539version = "0.11.0" 510version = "0.11.0"
540source = "registry+https://github.com/rust-lang/crates.io-index" 511source = "registry+https://github.com/rust-lang/crates.io-index"
541dependencies = [ 512dependencies = [
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]]
664name = "lsp-server"
665version = "0.1.0"
666source = "registry+https://github.com/rust-lang/crates.io-index"
667dependencies = [
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]]
693name = "lsp-types" 675name = "lsp-types"
694version = "0.60.0" 676version = "0.60.0"
695source = "registry+https://github.com/rust-lang/crates.io-index" 677source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1092,8 +1074,8 @@ version = "0.1.0"
1092dependencies = [ 1074dependencies = [
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]
2edition = "2018"
3name = "gen_lsp_server"
4version = "0.2.0"
5authors = ["rust-analyzer developers"]
6repository = "https://github.com/rust-analyzer/rust-analyzer"
7license = "MIT OR Apache-2.0"
8description = "Generic LSP server scaffold."
9
10[dependencies]
11lsp-types = "0.60.0"
12log = "0.4.3"
13serde_json = "1.0.34"
14serde = { version = "1.0.83", features = ["derive"] }
15crossbeam-channel = "0.3.5"
16
17[dev-dependencies]
18flexi_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 @@
1use std::error::Error;
2
3use crossbeam_channel::{Receiver, Sender};
4use gen_lsp_server::{handle_shutdown, run_server, stdio_transport, RawMessage, RawResponse};
5use lsp_types::{
6 request::{GotoDefinition, GotoDefinitionResponse},
7 InitializeParams, ServerCapabilities,
8};
9
10fn 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
17fn 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
45use std::error::Error;
46
47use crossbeam_channel::{Receiver, Sender};
48use gen_lsp_server::{
49 handle_shutdown, run_server, stdio_transport, RawMessage, RawRequest, RawResponse,
50};
51use log::info;
52use lsp_types::{
53 request::{GotoDefinition, GotoDefinitionResponse},
54 InitializeParams, ServerCapabilities,
55};
56
57fn 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
76fn 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
117pub 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
58use std::error::Error;
59
60mod msg;
61mod stdio;
62
63use crossbeam_channel::{Receiver, Sender};
64use lsp_types::{
65 notification::{Exit, Initialized},
66 request::{Initialize, Shutdown},
67 InitializeParams, InitializeResult, ServerCapabilities,
68};
69
70pub type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;
71pub 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.
82pub 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)`
104pub 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
115fn 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 @@
1use std::io::{BufRead, Write};
2
3use lsp_types::{notification::Notification, request::Request};
4use serde::{Deserialize, Serialize};
5use serde_json::{from_str, from_value, to_string, to_value, Value};
6
7use crate::Result;
8
9#[derive(Serialize, Deserialize, Debug, Clone)]
10#[serde(untagged)]
11pub enum RawMessage {
12 Request(RawRequest),
13 Notification(RawNotification),
14 Response(RawResponse),
15}
16
17impl From<RawRequest> for RawMessage {
18 fn from(raw: RawRequest) -> RawMessage {
19 RawMessage::Request(raw)
20 }
21}
22
23impl From<RawNotification> for RawMessage {
24 fn from(raw: RawNotification) -> RawMessage {
25 RawMessage::Notification(raw)
26 }
27}
28
29impl From<RawResponse> for RawMessage {
30 fn from(raw: RawResponse) -> RawMessage {
31 RawMessage::Response(raw)
32 }
33}
34
35#[derive(Debug, Serialize, Deserialize, Clone)]
36pub struct RawRequest {
37 pub id: u64,
38 pub method: String,
39 pub params: Value,
40}
41
42#[derive(Debug, Serialize, Deserialize, Clone)]
43pub 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)]
55pub 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)]
64pub 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)]
79pub struct RawNotification {
80 pub method: String,
81 pub params: Value,
82}
83
84impl 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
106impl 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
128impl 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
142impl 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
168fn 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
199fn 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 @@
1use std::{
2 io::{stdin, stdout},
3 thread,
4};
5
6use crossbeam_channel::{bounded, Receiver, Sender};
7use lsp_types::notification::Exit;
8
9use crate::{RawMessage, Result};
10
11pub 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
41pub struct Threads {
42 reader: thread::JoinHandle<Result<()>>,
43 writer: thread::JoinHandle<Result<()>>,
44}
45
46impl 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" }
21ra_syntax = { path = "../ra_syntax" } 21ra_syntax = { path = "../ra_syntax" }
22ra_text_edit = { path = "../ra_text_edit" } 22ra_text_edit = { path = "../ra_text_edit" }
23ra_ide_api = { path = "../ra_ide_api" } 23ra_ide_api = { path = "../ra_ide_api" }
24gen_lsp_server = { path = "../gen_lsp_server" } 24lsp-server = "0.1.0"
25ra_project_model = { path = "../ra_project_model" } 25ra_project_model = { path = "../ra_project_model" }
26ra_prof = { path = "../ra_prof" } 26ra_prof = { path = "../ra_prof" }
27ra_vfs_glob = { path = "../ra_vfs_glob" } 27ra_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 @@
1use flexi_logger::{Duplicate, Logger}; 1use flexi_logger::{Duplicate, Logger};
2use gen_lsp_server::{run_server, stdio_transport}; 2use lsp_server::{run_server, stdio_transport, LspServerError};
3 3
4use ra_lsp_server::{show_message, Result, ServerConfig}; 4use ra_lsp_server::{show_message, Result, ServerConfig};
5use ra_prof; 5use ra_prof;
@@ -29,9 +29,11 @@ fn main() -> Result<()> {
29} 29}
30 30
31fn main_inner() -> Result<()> { 31fn 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;
5use std::{error::Error, fmt, path::PathBuf, sync::Arc, time::Instant}; 5use std::{error::Error, fmt, path::PathBuf, sync::Arc, time::Instant};
6 6
7use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender}; 7use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender};
8use gen_lsp_server::{ 8use lsp_server::{handle_shutdown, ErrorCode, Message, Notification, Request, RequestId, Response};
9 handle_shutdown, ErrorCode, RawMessage, RawNotification, RawRequest, RawResponse,
10};
11use lsp_types::{ClientCapabilities, NumberOrString}; 9use lsp_types::{ClientCapabilities, NumberOrString};
12use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData}; 10use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData};
13use ra_prof::profile; 11use ra_prof::profile;
@@ -53,8 +51,8 @@ pub fn main_loop(
53 ws_roots: Vec<PathBuf>, 51 ws_roots: Vec<PathBuf>,
54 client_caps: ClientCapabilities, 52 client_caps: ClientCapabilities,
55 config: ServerConfig, 53 config: ServerConfig,
56 msg_receiver: &Receiver<RawMessage>, 54 msg_receiver: &Receiver<Message>,
57 msg_sender: &Sender<RawMessage>, 55 msg_sender: &Sender<Message>,
58) -> Result<()> { 56) -> Result<()> {
59 log::info!("server_config: {:#?}", config); 57 log::info!("server_config: {:#?}", config);
60 // FIXME: support dynamic workspace loading. 58 // FIXME: support dynamic workspace loading.
@@ -146,12 +144,12 @@ pub fn main_loop(
146 144
147#[derive(Debug)] 145#[derive(Debug)]
148enum Task { 146enum Task {
149 Respond(RawResponse), 147 Respond(Response),
150 Notify(RawNotification), 148 Notify(Notification),
151} 149}
152 150
153enum Event { 151enum Event {
154 Msg(RawMessage), 152 Msg(Message),
155 Task(Task), 153 Task(Task),
156 Vfs(VfsTask), 154 Vfs(VfsTask),
157 Lib(LibraryData), 155 Lib(LibraryData),
@@ -159,24 +157,28 @@ enum Event {
159 157
160impl fmt::Debug for Event { 158impl fmt::Debug for Event {
161 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 159 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162 let debug_verbose_not = |not: &RawNotification, f: &mut fmt::Formatter| { 160 let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| {
163 f.debug_struct("RawNotification").field("method", &not.method).finish() 161 f.debug_struct("Notification").field("method", &not.method).finish()
164 }; 162 };
165 163
166 match self { 164 match self {
167 Event::Msg(RawMessage::Notification(not)) => { 165 Event::Msg(Message::Notification(not)) => {
168 if not.is::<req::DidOpenTextDocument>() || not.is::<req::DidChangeTextDocument>() { 166 if notification_is::<req::DidOpenTextDocument>(not)
167 || notification_is::<req::DidChangeTextDocument>(not)
168 {
169 return debug_verbose_not(not, f); 169 return debug_verbose_not(not, f);
170 } 170 }
171 } 171 }
172 Event::Task(Task::Notify(not)) => { 172 Event::Task(Task::Notify(not)) => {
173 if not.is::<req::PublishDecorations>() || not.is::<req::PublishDiagnostics>() { 173 if notification_is::<req::PublishDecorations>(not)
174 || notification_is::<req::PublishDiagnostics>(not)
175 {
174 return debug_verbose_not(not, f); 176 return debug_verbose_not(not, f);
175 } 177 }
176 } 178 }
177 Event::Task(Task::Respond(resp)) => { 179 Event::Task(Task::Respond(resp)) => {
178 return f 180 return f
179 .debug_struct("RawResponse") 181 .debug_struct("Response")
180 .field("id", &resp.id) 182 .field("id", &resp.id)
181 .field("error", &resp.error) 183 .field("error", &resp.error)
182 .finish(); 184 .finish();
@@ -194,8 +196,8 @@ impl fmt::Debug for Event {
194 196
195fn main_loop_inner( 197fn main_loop_inner(
196 pool: &ThreadPool, 198 pool: &ThreadPool,
197 msg_sender: &Sender<RawMessage>, 199 msg_sender: &Sender<Message>,
198 msg_receiver: &Receiver<RawMessage>, 200 msg_receiver: &Receiver<Message>,
199 task_sender: Sender<Task>, 201 task_sender: Sender<Task>,
200 task_receiver: Receiver<Task>, 202 task_receiver: Receiver<Task>,
201 state: &mut WorldState, 203 state: &mut WorldState,
@@ -249,10 +251,9 @@ fn main_loop_inner(
249 in_flight_libraries -= 1; 251 in_flight_libraries -= 1;
250 } 252 }
251 Event::Msg(msg) => match msg { 253 Event::Msg(msg) => match msg {
252 RawMessage::Request(req) => { 254 Message::Request(req) => {
253 let req = match handle_shutdown(req, msg_sender) { 255 if handle_shutdown(&req, msg_sender) {
254 Some(req) => req, 256 return Ok(());
255 None => return Ok(()),
256 }; 257 };
257 on_request( 258 on_request(
258 state, 259 state,
@@ -264,11 +265,11 @@ fn main_loop_inner(
264 req, 265 req,
265 )? 266 )?
266 } 267 }
267 RawMessage::Notification(not) => { 268 Message::Notification(not) => {
268 on_notification(msg_sender, state, pending_requests, &mut subs, not)?; 269 on_notification(msg_sender, state, pending_requests, &mut subs, not)?;
269 state_changed = true; 270 state_changed = true;
270 } 271 }
271 RawMessage::Response(resp) => log::error!("unexpected response: {:?}", resp), 272 Message::Response(resp) => log::error!("unexpected response: {:?}", resp),
272 }, 273 },
273 }; 274 };
274 275
@@ -313,13 +314,13 @@ fn main_loop_inner(
313 314
314fn on_task( 315fn on_task(
315 task: Task, 316 task: Task,
316 msg_sender: &Sender<RawMessage>, 317 msg_sender: &Sender<Message>,
317 pending_requests: &mut PendingRequests, 318 pending_requests: &mut PendingRequests,
318 state: &mut WorldState, 319 state: &mut WorldState,
319) { 320) {
320 match task { 321 match task {
321 Task::Respond(response) => { 322 Task::Respond(response) => {
322 if let Some(completed) = pending_requests.finish(response.id) { 323 if let Some(completed) = pending_requests.finish(&response.id) {
323 log::info!("handled req#{} in {:?}", completed.id, completed.duration); 324 log::info!("handled req#{} in {:?}", completed.id, completed.duration);
324 state.complete_request(completed); 325 state.complete_request(completed);
325 msg_sender.send(response.into()).unwrap(); 326 msg_sender.send(response.into()).unwrap();
@@ -336,9 +337,9 @@ fn on_request(
336 pending_requests: &mut PendingRequests, 337 pending_requests: &mut PendingRequests,
337 pool: &ThreadPool, 338 pool: &ThreadPool,
338 sender: &Sender<Task>, 339 sender: &Sender<Task>,
339 msg_sender: &Sender<RawMessage>, 340 msg_sender: &Sender<Message>,
340 request_received: Instant, 341 request_received: Instant,
341 req: RawRequest, 342 req: Request,
342) -> Result<()> { 343) -> Result<()> {
343 let mut pool_dispatcher = PoolDispatcher { 344 let mut pool_dispatcher = PoolDispatcher {
344 req: Some(req), 345 req: Some(req),
@@ -388,22 +389,20 @@ fn on_request(
388} 389}
389 390
390fn on_notification( 391fn on_notification(
391 msg_sender: &Sender<RawMessage>, 392 msg_sender: &Sender<Message>,
392 state: &mut WorldState, 393 state: &mut WorldState,
393 pending_requests: &mut PendingRequests, 394 pending_requests: &mut PendingRequests,
394 subs: &mut Subscriptions, 395 subs: &mut Subscriptions,
395 not: RawNotification, 396 not: Notification,
396) -> Result<()> { 397) -> Result<()> {
397 let not = match not.cast::<req::Cancel>() { 398 let not = match notification_cast::<req::Cancel>(not) {
398 Ok(params) => { 399 Ok(params) => {
399 let id = match params.id { 400 let id: RequestId = match params.id {
400 NumberOrString::Number(id) => id, 401 NumberOrString::Number(id) => id.into(),
401 NumberOrString::String(id) => { 402 NumberOrString::String(id) => id.into(),
402 panic!("string id's not supported: {:?}", id);
403 }
404 }; 403 };
405 if pending_requests.cancel(id) { 404 if pending_requests.cancel(&id) {
406 let response = RawResponse::err( 405 let response = Response::new_err(
407 id, 406 id,
408 ErrorCode::RequestCanceled as i32, 407 ErrorCode::RequestCanceled as i32,
409 "canceled by client".to_string(), 408 "canceled by client".to_string(),
@@ -414,7 +413,7 @@ fn on_notification(
414 } 413 }
415 Err(not) => not, 414 Err(not) => not,
416 }; 415 };
417 let not = match not.cast::<req::DidOpenTextDocument>() { 416 let not = match notification_cast::<req::DidOpenTextDocument>(not) {
418 Ok(params) => { 417 Ok(params) => {
419 let uri = params.text_document.uri; 418 let uri = params.text_document.uri;
420 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; 419 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
@@ -427,7 +426,7 @@ fn on_notification(
427 } 426 }
428 Err(not) => not, 427 Err(not) => not,
429 }; 428 };
430 let not = match not.cast::<req::DidChangeTextDocument>() { 429 let not = match notification_cast::<req::DidChangeTextDocument>(not) {
431 Ok(mut params) => { 430 Ok(mut params) => {
432 let uri = params.text_document.uri; 431 let uri = params.text_document.uri;
433 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; 432 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
@@ -438,7 +437,7 @@ fn on_notification(
438 } 437 }
439 Err(not) => not, 438 Err(not) => not,
440 }; 439 };
441 let not = match not.cast::<req::DidCloseTextDocument>() { 440 let not = match notification_cast::<req::DidCloseTextDocument>(not) {
442 Ok(params) => { 441 Ok(params) => {
443 let uri = params.text_document.uri; 442 let uri = params.text_document.uri;
444 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; 443 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
@@ -446,13 +445,13 @@ fn on_notification(
446 subs.remove_sub(FileId(file_id.0)); 445 subs.remove_sub(FileId(file_id.0));
447 } 446 }
448 let params = req::PublishDiagnosticsParams { uri, diagnostics: Vec::new() }; 447 let params = req::PublishDiagnosticsParams { uri, diagnostics: Vec::new() };
449 let not = RawNotification::new::<req::PublishDiagnostics>(&params); 448 let not = notification_new::<req::PublishDiagnostics>(params);
450 msg_sender.send(not.into()).unwrap(); 449 msg_sender.send(not.into()).unwrap();
451 return Ok(()); 450 return Ok(());
452 } 451 }
453 Err(not) => not, 452 Err(not) => not,
454 }; 453 };
455 let not = match not.cast::<req::DidChangeConfiguration>() { 454 let not = match notification_cast::<req::DidChangeConfiguration>(not) {
456 Ok(_params) => { 455 Ok(_params) => {
457 return Ok(()); 456 return Ok(());
458 } 457 }
@@ -463,11 +462,11 @@ fn on_notification(
463} 462}
464 463
465struct PoolDispatcher<'a> { 464struct PoolDispatcher<'a> {
466 req: Option<RawRequest>, 465 req: Option<Request>,
467 pool: &'a ThreadPool, 466 pool: &'a ThreadPool,
468 world: &'a mut WorldState, 467 world: &'a mut WorldState,
469 pending_requests: &'a mut PendingRequests, 468 pending_requests: &'a mut PendingRequests,
470 msg_sender: &'a Sender<RawMessage>, 469 msg_sender: &'a Sender<Message>,
471 sender: &'a Sender<Task>, 470 sender: &'a Sender<Task>,
472 request_received: Instant, 471 request_received: Instant,
473} 472}
@@ -522,13 +521,13 @@ impl<'a> PoolDispatcher<'a> {
522 Ok(self) 521 Ok(self)
523 } 522 }
524 523
525 fn parse<R>(&mut self) -> Option<(u64, R::Params)> 524 fn parse<R>(&mut self) -> Option<(RequestId, R::Params)>
526 where 525 where
527 R: req::Request + 'static, 526 R: req::Request + 'static,
528 R::Params: DeserializeOwned + Send + 'static, 527 R::Params: DeserializeOwned + Send + 'static,
529 { 528 {
530 let req = self.req.take()?; 529 let req = self.req.take()?;
531 let (id, params) = match req.cast::<R>() { 530 let (id, params) = match req.extract::<R::Params>(R::METHOD) {
532 Ok(it) => it, 531 Ok(it) => it,
533 Err(req) => { 532 Err(req) => {
534 self.req = Some(req); 533 self.req = Some(req);
@@ -536,7 +535,7 @@ impl<'a> PoolDispatcher<'a> {
536 } 535 }
537 }; 536 };
538 self.pending_requests.start(PendingRequest { 537 self.pending_requests.start(PendingRequest {
539 id, 538 id: id.clone(),
540 method: R::METHOD.to_string(), 539 method: R::METHOD.to_string(),
541 received: self.request_received, 540 received: self.request_received,
542 }); 541 });
@@ -548,7 +547,7 @@ impl<'a> PoolDispatcher<'a> {
548 None => (), 547 None => (),
549 Some(req) => { 548 Some(req) => {
550 log::error!("unknown request: {:?}", req); 549 log::error!("unknown request: {:?}", req);
551 let resp = RawResponse::err( 550 let resp = Response::new_err(
552 req.id, 551 req.id,
553 ErrorCode::MethodNotFound as i32, 552 ErrorCode::MethodNotFound as i32,
554 "unknown request".to_string(), 553 "unknown request".to_string(),
@@ -559,34 +558,30 @@ impl<'a> PoolDispatcher<'a> {
559 } 558 }
560} 559}
561 560
562fn result_to_task<R>(id: u64, result: Result<R::Result>) -> Task 561fn result_to_task<R>(id: RequestId, result: Result<R::Result>) -> Task
563where 562where
564 R: req::Request + 'static, 563 R: req::Request + 'static,
565 R::Params: DeserializeOwned + Send + 'static, 564 R::Params: DeserializeOwned + Send + 'static,
566 R::Result: Serialize + 'static, 565 R::Result: Serialize + 'static,
567{ 566{
568 let response = match result { 567 let response = match result {
569 Ok(resp) => RawResponse::ok::<R>(id, &resp), 568 Ok(resp) => Response::new_ok(id, &resp),
570 Err(e) => match e.downcast::<LspError>() { 569 Err(e) => match e.downcast::<LspError>() {
571 Ok(lsp_error) => RawResponse::err(id, lsp_error.code, lsp_error.message), 570 Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message),
572 Err(e) => { 571 Err(e) => {
573 if is_canceled(&e) { 572 if is_canceled(&e) {
574 // FIXME: When https://github.com/Microsoft/vscode-languageserver-node/issues/457 573 // FIXME: When https://github.com/Microsoft/vscode-languageserver-node/issues/457
575 // gets fixed, we can return the proper response. 574 // gets fixed, we can return the proper response.
576 // This works around the issue where "content modified" error would continuously 575 // This works around the issue where "content modified" error would continuously
577 // show an message pop-up in VsCode 576 // show an message pop-up in VsCode
578 // RawResponse::err( 577 // Response::err(
579 // id, 578 // id,
580 // ErrorCode::ContentModified as i32, 579 // ErrorCode::ContentModified as i32,
581 // "content modified".to_string(), 580 // "content modified".to_string(),
582 // ) 581 // )
583 RawResponse { 582 Response::new_ok(id, ())
584 id,
585 result: Some(serde_json::to_value(&()).unwrap()),
586 error: None,
587 }
588 } else { 583 } else {
589 RawResponse::err(id, ErrorCode::InternalError as i32, e.to_string()) 584 Response::new_err(id, ErrorCode::InternalError as i32, e.to_string())
590 } 585 }
591 } 586 }
592 }, 587 },
@@ -613,7 +608,7 @@ fn update_file_notifications_on_threadpool(
613 } 608 }
614 } 609 }
615 Ok(params) => { 610 Ok(params) => {
616 let not = RawNotification::new::<req::PublishDiagnostics>(&params); 611 let not = notification_new::<req::PublishDiagnostics>(params);
617 sender.send(Task::Notify(not)).unwrap(); 612 sender.send(Task::Notify(not)).unwrap();
618 } 613 }
619 } 614 }
@@ -626,7 +621,7 @@ fn update_file_notifications_on_threadpool(
626 } 621 }
627 } 622 }
628 Ok(params) => { 623 Ok(params) => {
629 let not = RawNotification::new::<req::PublishDecorations>(&params); 624 let not = notification_new::<req::PublishDecorations>(params);
630 sender.send(Task::Notify(not)).unwrap(); 625 sender.send(Task::Notify(not)).unwrap();
631 } 626 }
632 } 627 }
@@ -635,17 +630,33 @@ fn update_file_notifications_on_threadpool(
635 }); 630 });
636} 631}
637 632
638pub fn show_message( 633pub fn show_message(typ: req::MessageType, message: impl Into<String>, sender: &Sender<Message>) {
639 typ: req::MessageType,
640 message: impl Into<String>,
641 sender: &Sender<RawMessage>,
642) {
643 let message = message.into(); 634 let message = message.into();
644 let params = req::ShowMessageParams { typ, message }; 635 let params = req::ShowMessageParams { typ, message };
645 let not = RawNotification::new::<req::ShowMessage>(&params); 636 let not = notification_new::<req::ShowMessage>(params);
646 sender.send(not.into()).unwrap(); 637 sender.send(not.into()).unwrap();
647} 638}
648 639
649fn is_canceled(e: &Box<dyn std::error::Error + Send + Sync>) -> bool { 640fn is_canceled(e: &Box<dyn std::error::Error + Send + Sync>) -> bool {
650 e.downcast_ref::<Canceled>().is_some() 641 e.downcast_ref::<Canceled>().is_some()
651} 642}
643
644fn notification_is<N: lsp_types::notification::Notification>(notification: &Notification) -> bool {
645 notification.method == N::METHOD
646}
647
648fn notification_cast<N>(notification: Notification) -> std::result::Result<N::Params, Notification>
649where
650 N: lsp_types::notification::Notification,
651 N::Params: DeserializeOwned,
652{
653 notification.extract(N::METHOD)
654}
655
656fn notification_new<N>(params: N::Params) -> Notification
657where
658 N: lsp_types::notification::Notification,
659 N::Params: Serialize,
660{
661 Notification::new(N::METHOD.to_string(), params)
662}
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 @@
1use std::{fmt::Write as _, io::Write as _}; 1use std::{fmt::Write as _, io::Write as _};
2 2
3use gen_lsp_server::ErrorCode; 3use lsp_server::ErrorCode;
4use lsp_types::{ 4use 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 @@
1use std::time::{Duration, Instant}; 1use std::time::{Duration, Instant};
2 2
3use lsp_server::RequestId;
3use rustc_hash::FxHashMap; 4use rustc_hash::FxHashMap;
4 5
5#[derive(Debug)] 6#[derive(Debug)]
6pub struct CompletedRequest { 7pub 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)]
13pub(crate) struct PendingRequest { 14pub(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)]
30pub(crate) struct PendingRequests { 31pub(crate) struct PendingRequests {
31 map: FxHashMap<u64, PendingRequest>, 32 map: FxHashMap<RequestId, PendingRequest>,
32} 33}
33 34
34impl PendingRequests { 35impl 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
6use crossbeam_channel::{unbounded, Receiver}; 6use crossbeam_channel::{unbounded, Receiver};
7use gen_lsp_server::ErrorCode; 7use lsp_server::ErrorCode;
8use lsp_types::Url; 8use lsp_types::Url;
9use parking_lot::RwLock; 9use parking_lot::RwLock;
10use ra_ide_api::{ 10use 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
9use crossbeam_channel::{after, select, Receiver}; 9use crossbeam_channel::{after, select, Receiver};
10use flexi_logger::Logger; 10use flexi_logger::Logger;
11use gen_lsp_server::{RawMessage, RawNotification, RawRequest}; 11use lsp_server::{Message, Notification, Request};
12use lsp_types::{ 12use 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};
19use serde::Serialize; 16use serde::Serialize;
20use serde_json::{to_string_pretty, Value}; 17use serde_json::{to_string_pretty, Value};
@@ -84,9 +81,9 @@ pub fn project(fixture: &str) -> Server {
84 81
85pub struct Server { 82pub 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
92impl Server { 89impl 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>(&params); 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, &params); 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
249fn recv_timeout(receiver: &Receiver<RawMessage>) -> Option<RawMessage> { 248fn 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(),