diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-05-23 10:23:36 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-05-23 10:23:36 +0100 |
commit | 078c0e26c3ff4d9652bcb4845b5f4665c2e0989d (patch) | |
tree | 24719bb7205e95c1b2130238c6927dbfaa8db3e5 | |
parent | a25e103e4542637047fe388a926aebddca07b3b7 (diff) | |
parent | cbd131acbe497795028844a592a438b70a30ba32 (diff) |
Merge #1313
1313: Update `gen_lsp_server` examples r=matklad a=dmoonfire
- updated the documentation with an example that has no errors with current compiler
- added two example code to test compilation and show in use
- one example is the bare bones version in documentation
- the other example is the same but with logging statements to show flow
Co-authored-by: Dylan Moonfire <[email protected]>
-rw-r--r-- | crates/gen_lsp_server/Cargo.toml | 3 | ||||
-rw-r--r-- | crates/gen_lsp_server/examples/01_gen_lsp_server.rs | 45 | ||||
-rw-r--r-- | crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs | 118 | ||||
-rw-r--r-- | crates/gen_lsp_server/src/lib.rs | 13 |
4 files changed, 170 insertions, 9 deletions
diff --git a/crates/gen_lsp_server/Cargo.toml b/crates/gen_lsp_server/Cargo.toml index ba8bfdbd3..fa2fefea5 100644 --- a/crates/gen_lsp_server/Cargo.toml +++ b/crates/gen_lsp_server/Cargo.toml | |||
@@ -14,3 +14,6 @@ failure = "0.1.4" | |||
14 | serde_json = "1.0.34" | 14 | serde_json = "1.0.34" |
15 | serde = { version = "1.0.83", features = ["derive"] } | 15 | serde = { version = "1.0.83", features = ["derive"] } |
16 | crossbeam-channel = "0.3.5" | 16 | crossbeam-channel = "0.3.5" |
17 | |||
18 | [dev-dependencies] | ||
19 | flexi_logger = "0.11.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 new file mode 100644 index 000000000..60c581075 --- /dev/null +++ b/crates/gen_lsp_server/examples/01_gen_lsp_server.rs | |||
@@ -0,0 +1,45 @@ | |||
1 | use crossbeam_channel::{Sender, Receiver}; | ||
2 | use lsp_types::{ | ||
3 | ServerCapabilities, InitializeParams, | ||
4 | request::{GotoDefinition, GotoDefinitionResponse}, | ||
5 | }; | ||
6 | use gen_lsp_server::{run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse}; | ||
7 | |||
8 | fn main() -> Result<(), failure::Error> { | ||
9 | let (receiver, sender, io_threads) = stdio_transport(); | ||
10 | run_server(ServerCapabilities::default(), receiver, sender, main_loop)?; | ||
11 | io_threads.join()?; | ||
12 | Ok(()) | ||
13 | } | ||
14 | |||
15 | fn main_loop( | ||
16 | _params: InitializeParams, | ||
17 | receiver: &Receiver<RawMessage>, | ||
18 | sender: &Sender<RawMessage>, | ||
19 | ) -> Result<(), failure::Error> { | ||
20 | for msg in receiver { | ||
21 | match msg { | ||
22 | RawMessage::Request(req) => { | ||
23 | let req = match handle_shutdown(req, sender) { | ||
24 | None => return Ok(()), | ||
25 | Some(req) => req, | ||
26 | }; | ||
27 | match req.cast::<GotoDefinition>() { | ||
28 | Ok((id, _params)) => { | ||
29 | let resp = RawResponse::ok::<GotoDefinition>( | ||
30 | id, | ||
31 | &Some(GotoDefinitionResponse::Array(Vec::new())), | ||
32 | ); | ||
33 | sender.send(RawMessage::Response(resp))?; | ||
34 | continue; | ||
35 | } | ||
36 | Err(req) => req, | ||
37 | }; | ||
38 | // ... | ||
39 | } | ||
40 | RawMessage::Response(_resp) => (), | ||
41 | RawMessage::Notification(_not) => (), | ||
42 | } | ||
43 | } | ||
44 | Ok(()) | ||
45 | } | ||
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 new file mode 100644 index 000000000..27e4f1cbc --- /dev/null +++ b/crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs | |||
@@ -0,0 +1,118 @@ | |||
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 crossbeam_channel::{Sender, Receiver}; | ||
46 | use lsp_types::{ | ||
47 | ServerCapabilities, InitializeParams, | ||
48 | request::{GotoDefinition, GotoDefinitionResponse}, | ||
49 | }; | ||
50 | use log::info; | ||
51 | use gen_lsp_server::{ | ||
52 | run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse, RawRequest, | ||
53 | }; | ||
54 | |||
55 | fn main() -> Result<(), failure::Error> { | ||
56 | // Set up logging. Because `stdio_transport` gets a lock on stdout and stdin, we must have | ||
57 | // our logging only write out to stderr. | ||
58 | flexi_logger::Logger::with_str("info").start().unwrap(); | ||
59 | info!("starting generic LSP server"); | ||
60 | |||
61 | // Create the transport. Includes the stdio (stdin and stdout) versions but this could | ||
62 | // also be implemented to use sockets or HTTP. | ||
63 | let (receiver, sender, io_threads) = stdio_transport(); | ||
64 | |||
65 | // Run the server and wait for the two threads to end (typically by trigger LSP Exit event). | ||
66 | run_server(ServerCapabilities::default(), receiver, sender, main_loop)?; | ||
67 | io_threads.join()?; | ||
68 | |||
69 | // Shut down gracefully. | ||
70 | info!("shutting down server"); | ||
71 | Ok(()) | ||
72 | } | ||
73 | |||
74 | fn main_loop( | ||
75 | _params: InitializeParams, | ||
76 | receiver: &Receiver<RawMessage>, | ||
77 | sender: &Sender<RawMessage>, | ||
78 | ) -> Result<(), failure::Error> { | ||
79 | info!("starting example main loop"); | ||
80 | for msg in receiver { | ||
81 | info!("got msg: {:?}", msg); | ||
82 | match msg { | ||
83 | RawMessage::Request(req) => { | ||
84 | let req = match log_handle_shutdown(req, sender) { | ||
85 | None => return Ok(()), | ||
86 | Some(req) => req, | ||
87 | }; | ||
88 | info!("got request: {:?}", req); | ||
89 | match req.cast::<GotoDefinition>() { | ||
90 | Ok((id, params)) => { | ||
91 | info!("got gotoDefinition request #{}: {:?}", id, params); | ||
92 | let resp = RawResponse::ok::<GotoDefinition>( | ||
93 | id, | ||
94 | &Some(GotoDefinitionResponse::Array(Vec::new())), | ||
95 | ); | ||
96 | info!("sending gotoDefinition response: {:?}", resp); | ||
97 | sender.send(RawMessage::Response(resp))?; | ||
98 | continue; | ||
99 | } | ||
100 | Err(req) => req, | ||
101 | }; | ||
102 | // ... | ||
103 | } | ||
104 | RawMessage::Response(resp) => { | ||
105 | info!("got response: {:?}", resp); | ||
106 | } | ||
107 | RawMessage::Notification(not) => { | ||
108 | info!("got notification: {:?}", not); | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | Ok(()) | ||
113 | } | ||
114 | |||
115 | pub fn log_handle_shutdown(req: RawRequest, sender: &Sender<RawMessage>) -> Option<RawRequest> { | ||
116 | info!("handle_shutdown: {:?}", req); | ||
117 | handle_shutdown(req, sender) | ||
118 | } | ||
diff --git a/crates/gen_lsp_server/src/lib.rs b/crates/gen_lsp_server/src/lib.rs index edbdda6c8..1cd5a3a7c 100644 --- a/crates/gen_lsp_server/src/lib.rs +++ b/crates/gen_lsp_server/src/lib.rs | |||
@@ -2,21 +2,16 @@ | |||
2 | //! This crate handles protocol handshaking and parsing messages, while you | 2 | //! This crate handles protocol handshaking and parsing messages, while you |
3 | //! control the message dispatch loop yourself. | 3 | //! control the message dispatch loop yourself. |
4 | //! | 4 | //! |
5 | //! Run with `RUST_LOG=sync_lsp_server=debug` to see all the messages. | 5 | //! Run with `RUST_LOG=gen_lsp_server=debug` to see all the messages. |
6 | //! | 6 | //! |
7 | //! ```no_run | 7 | //! ```no_run |
8 | //! extern crate gen_lsp_server; | ||
9 | //! extern crate lsp_types; | ||
10 | //! extern crate failure; | ||
11 | //! extern crate crossbeam_channel; | ||
12 | //! | ||
13 | //! use crossbeam_channel::{Sender, Receiver}; | 8 | //! use crossbeam_channel::{Sender, Receiver}; |
14 | //! use lsp_types::{ServerCapabilities, InitializeParams, request::{GotoDefinition, GotoDefinitionResponse}}; | 9 | //! use lsp_types::{ServerCapabilities, InitializeParams, request::{GotoDefinition, GotoDefinitionResponse}}; |
15 | //! use gen_lsp_server::{run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse}; | 10 | //! use gen_lsp_server::{run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse}; |
16 | //! | 11 | //! |
17 | //! fn main() -> Result<(), failure::Error> { | 12 | //! fn main() -> Result<(), failure::Error> { |
18 | //! let (receiver, sender, io_threads) = stdio_transport(); | 13 | //! let (receiver, sender, io_threads) = stdio_transport(); |
19 | //! gen_lsp_server::run_server( | 14 | //! run_server( |
20 | //! ServerCapabilities::default(), | 15 | //! ServerCapabilities::default(), |
21 | //! receiver, | 16 | //! receiver, |
22 | //! sender, | 17 | //! sender, |
@@ -38,13 +33,13 @@ | |||
38 | //! None => return Ok(()), | 33 | //! None => return Ok(()), |
39 | //! Some(req) => req, | 34 | //! Some(req) => req, |
40 | //! }; | 35 | //! }; |
41 | //! let req = match req.cast::<GotoDefinition>() { | 36 | //! match req.cast::<GotoDefinition>() { |
42 | //! Ok((id, _params)) => { | 37 | //! Ok((id, _params)) => { |
43 | //! let resp = RawResponse::ok::<GotoDefinition>( | 38 | //! let resp = RawResponse::ok::<GotoDefinition>( |
44 | //! id, | 39 | //! id, |
45 | //! &Some(GotoDefinitionResponse::Array(Vec::new())), | 40 | //! &Some(GotoDefinitionResponse::Array(Vec::new())), |
46 | //! ); | 41 | //! ); |
47 | //! sender.send(RawMessage::Response(resp)); | 42 | //! sender.send(RawMessage::Response(resp))?; |
48 | //! continue; | 43 | //! continue; |
49 | //! }, | 44 | //! }, |
50 | //! Err(req) => req, | 45 | //! Err(req) => req, |