aboutsummaryrefslogtreecommitdiff
path: root/crates/gen_lsp_server
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-08-30 15:24:11 +0100
committerAleksey Kladov <[email protected]>2019-08-30 15:24:11 +0100
commit72a3722470e5297c72dcaccaf2f113e7b758606d (patch)
tree3f0e4056ba2e4b3799b72d71d709783aa6dffc49 /crates/gen_lsp_server
parent7d72ca80003b7915ed7fc64907b5b6dc5c88dacd (diff)
move lsp-server to a separate repository
Diffstat (limited to 'crates/gen_lsp_server')
-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
6 files changed, 0 insertions, 583 deletions
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}