aboutsummaryrefslogtreecommitdiff
path: root/crates/gen_lsp_server
diff options
context:
space:
mode:
Diffstat (limited to 'crates/gen_lsp_server')
-rw-r--r--crates/gen_lsp_server/Cargo.toml3
-rw-r--r--crates/gen_lsp_server/examples/01_gen_lsp_server.rs45
-rw-r--r--crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs118
-rw-r--r--crates/gen_lsp_server/src/lib.rs13
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"
14serde_json = "1.0.34" 14serde_json = "1.0.34"
15serde = { version = "1.0.83", features = ["derive"] } 15serde = { version = "1.0.83", features = ["derive"] }
16crossbeam-channel = "0.3.5" 16crossbeam-channel = "0.3.5"
17
18[dev-dependencies]
19flexi_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 @@
1use crossbeam_channel::{Sender, Receiver};
2use lsp_types::{
3 ServerCapabilities, InitializeParams,
4 request::{GotoDefinition, GotoDefinitionResponse},
5};
6use gen_lsp_server::{run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse};
7
8fn 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
15fn 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
45use crossbeam_channel::{Sender, Receiver};
46use lsp_types::{
47 ServerCapabilities, InitializeParams,
48 request::{GotoDefinition, GotoDefinitionResponse},
49};
50use log::info;
51use gen_lsp_server::{
52 run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse, RawRequest,
53};
54
55fn 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
74fn 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
115pub 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,