diff options
Diffstat (limited to 'crates/gen_lsp_server/src')
-rw-r--r-- | crates/gen_lsp_server/src/lib.rs | 136 | ||||
-rw-r--r-- | crates/gen_lsp_server/src/msg.rs | 205 | ||||
-rw-r--r-- | crates/gen_lsp_server/src/stdio.rs | 57 |
3 files changed, 0 insertions, 398 deletions
diff --git a/crates/gen_lsp_server/src/lib.rs b/crates/gen_lsp_server/src/lib.rs deleted file mode 100644 index 0984e3e25..000000000 --- a/crates/gen_lsp_server/src/lib.rs +++ /dev/null | |||
@@ -1,136 +0,0 @@ | |||
1 | //! A language server scaffold, exposing a synchronous crossbeam-channel based API. | ||
2 | //! This crate handles protocol handshaking and parsing messages, while you | ||
3 | //! control the message dispatch loop yourself. | ||
4 | //! | ||
5 | //! Run with `RUST_LOG=gen_lsp_server=debug` to see all the messages. | ||
6 | //! | ||
7 | //! ```no_run | ||
8 | //! use std::error::Error; | ||
9 | //! use crossbeam_channel::{Sender, Receiver}; | ||
10 | //! use lsp_types::{ServerCapabilities, InitializeParams, request::{GotoDefinition, GotoDefinitionResponse}}; | ||
11 | //! use gen_lsp_server::{run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse}; | ||
12 | //! | ||
13 | //! fn main() -> Result<(), Box<dyn Error + Send + Sync>> { | ||
14 | //! let (receiver, sender, io_threads) = stdio_transport(); | ||
15 | //! run_server( | ||
16 | //! ServerCapabilities::default(), | ||
17 | //! receiver, | ||
18 | //! sender, | ||
19 | //! main_loop, | ||
20 | //! )?; | ||
21 | //! io_threads.join()?; | ||
22 | //! Ok(()) | ||
23 | //! } | ||
24 | //! | ||
25 | //! fn main_loop( | ||
26 | //! _params: InitializeParams, | ||
27 | //! receiver: &Receiver<RawMessage>, | ||
28 | //! sender: &Sender<RawMessage>, | ||
29 | //! ) -> Result<(), Box<dyn Error + Send + Sync>> { | ||
30 | //! for msg in receiver { | ||
31 | //! match msg { | ||
32 | //! RawMessage::Request(req) => { | ||
33 | //! let req = match handle_shutdown(req, sender) { | ||
34 | //! None => return Ok(()), | ||
35 | //! Some(req) => req, | ||
36 | //! }; | ||
37 | //! match req.cast::<GotoDefinition>() { | ||
38 | //! Ok((id, _params)) => { | ||
39 | //! let resp = RawResponse::ok::<GotoDefinition>( | ||
40 | //! id, | ||
41 | //! &Some(GotoDefinitionResponse::Array(Vec::new())), | ||
42 | //! ); | ||
43 | //! sender.send(RawMessage::Response(resp))?; | ||
44 | //! continue; | ||
45 | //! }, | ||
46 | //! Err(req) => req, | ||
47 | //! }; | ||
48 | //! // ... | ||
49 | //! } | ||
50 | //! RawMessage::Response(_resp) => (), | ||
51 | //! RawMessage::Notification(_not) => (), | ||
52 | //! } | ||
53 | //! } | ||
54 | //! Ok(()) | ||
55 | //! } | ||
56 | //! ``` | ||
57 | |||
58 | use std::error::Error; | ||
59 | |||
60 | mod msg; | ||
61 | mod stdio; | ||
62 | |||
63 | use crossbeam_channel::{Receiver, Sender}; | ||
64 | use lsp_types::{ | ||
65 | notification::{Exit, Initialized}, | ||
66 | request::{Initialize, Shutdown}, | ||
67 | InitializeParams, InitializeResult, ServerCapabilities, | ||
68 | }; | ||
69 | |||
70 | pub type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>; | ||
71 | pub use crate::{ | ||
72 | msg::{ErrorCode, RawMessage, RawNotification, RawRequest, RawResponse, RawResponseError}, | ||
73 | stdio::{stdio_transport, Threads}, | ||
74 | }; | ||
75 | |||
76 | /// Main entry point: runs the server from initialization to shutdown. | ||
77 | /// To attach server to standard input/output streams, use the `stdio_transport` | ||
78 | /// function to create corresponding `sender` and `receiver` pair. | ||
79 | /// | ||
80 | /// `server` should use the `handle_shutdown` function to handle the `Shutdown` | ||
81 | /// request. | ||
82 | pub fn run_server( | ||
83 | caps: ServerCapabilities, | ||
84 | receiver: Receiver<RawMessage>, | ||
85 | sender: Sender<RawMessage>, | ||
86 | server: impl FnOnce(InitializeParams, &Receiver<RawMessage>, &Sender<RawMessage>) -> Result<()>, | ||
87 | ) -> Result<()> { | ||
88 | log::info!("lsp server initializes"); | ||
89 | let params = initialize(&receiver, &sender, caps)?; | ||
90 | log::info!("lsp server initialized, serving requests"); | ||
91 | server(params, &receiver, &sender)?; | ||
92 | log::info!("lsp server waiting for exit notification"); | ||
93 | match receiver.recv() { | ||
94 | Ok(RawMessage::Notification(n)) => n | ||
95 | .cast::<Exit>() | ||
96 | .map_err(|n| format!("unexpected notification during shutdown: {:?}", n))?, | ||
97 | m => Err(format!("unexpected message during shutdown: {:?}", m))?, | ||
98 | } | ||
99 | log::info!("lsp server shutdown complete"); | ||
100 | Ok(()) | ||
101 | } | ||
102 | |||
103 | /// If `req` is `Shutdown`, respond to it and return `None`, otherwise return `Some(req)` | ||
104 | pub fn handle_shutdown(req: RawRequest, sender: &Sender<RawMessage>) -> Option<RawRequest> { | ||
105 | match req.cast::<Shutdown>() { | ||
106 | Ok((id, ())) => { | ||
107 | let resp = RawResponse::ok::<Shutdown>(id, &()); | ||
108 | let _ = sender.send(RawMessage::Response(resp)); | ||
109 | None | ||
110 | } | ||
111 | Err(req) => Some(req), | ||
112 | } | ||
113 | } | ||
114 | |||
115 | fn initialize( | ||
116 | receiver: &Receiver<RawMessage>, | ||
117 | sender: &Sender<RawMessage>, | ||
118 | caps: ServerCapabilities, | ||
119 | ) -> Result<InitializeParams> { | ||
120 | let (id, params) = match receiver.recv() { | ||
121 | Ok(RawMessage::Request(req)) => match req.cast::<Initialize>() { | ||
122 | Err(req) => Err(format!("expected initialize request, got {:?}", req))?, | ||
123 | Ok(req) => req, | ||
124 | }, | ||
125 | msg => Err(format!("expected initialize request, got {:?}", msg))?, | ||
126 | }; | ||
127 | let resp = RawResponse::ok::<Initialize>(id, &InitializeResult { capabilities: caps }); | ||
128 | sender.send(RawMessage::Response(resp)).unwrap(); | ||
129 | match receiver.recv() { | ||
130 | Ok(RawMessage::Notification(n)) => { | ||
131 | n.cast::<Initialized>().map_err(|_| "expected initialized notification")?; | ||
132 | } | ||
133 | _ => Err("expected initialized notification".to_string())?, | ||
134 | } | ||
135 | Ok(params) | ||
136 | } | ||
diff --git a/crates/gen_lsp_server/src/msg.rs b/crates/gen_lsp_server/src/msg.rs deleted file mode 100644 index 2928e4f8b..000000000 --- a/crates/gen_lsp_server/src/msg.rs +++ /dev/null | |||
@@ -1,205 +0,0 @@ | |||
1 | use std::io::{BufRead, Write}; | ||
2 | |||
3 | use lsp_types::{notification::Notification, request::Request}; | ||
4 | use serde::{Deserialize, Serialize}; | ||
5 | use serde_json::{from_str, from_value, to_string, to_value, Value}; | ||
6 | |||
7 | use crate::Result; | ||
8 | |||
9 | #[derive(Serialize, Deserialize, Debug, Clone)] | ||
10 | #[serde(untagged)] | ||
11 | pub enum RawMessage { | ||
12 | Request(RawRequest), | ||
13 | Notification(RawNotification), | ||
14 | Response(RawResponse), | ||
15 | } | ||
16 | |||
17 | impl From<RawRequest> for RawMessage { | ||
18 | fn from(raw: RawRequest) -> RawMessage { | ||
19 | RawMessage::Request(raw) | ||
20 | } | ||
21 | } | ||
22 | |||
23 | impl From<RawNotification> for RawMessage { | ||
24 | fn from(raw: RawNotification) -> RawMessage { | ||
25 | RawMessage::Notification(raw) | ||
26 | } | ||
27 | } | ||
28 | |||
29 | impl From<RawResponse> for RawMessage { | ||
30 | fn from(raw: RawResponse) -> RawMessage { | ||
31 | RawMessage::Response(raw) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
36 | pub struct RawRequest { | ||
37 | pub id: u64, | ||
38 | pub method: String, | ||
39 | pub params: Value, | ||
40 | } | ||
41 | |||
42 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
43 | pub struct RawResponse { | ||
44 | // JSON RPC allows this to be null if it was impossible | ||
45 | // to decode the request's id. Ignore this special case | ||
46 | // and just die horribly. | ||
47 | pub id: u64, | ||
48 | #[serde(skip_serializing_if = "Option::is_none")] | ||
49 | pub result: Option<Value>, | ||
50 | #[serde(skip_serializing_if = "Option::is_none")] | ||
51 | pub error: Option<RawResponseError>, | ||
52 | } | ||
53 | |||
54 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
55 | pub struct RawResponseError { | ||
56 | pub code: i32, | ||
57 | pub message: String, | ||
58 | #[serde(skip_serializing_if = "Option::is_none")] | ||
59 | pub data: Option<Value>, | ||
60 | } | ||
61 | |||
62 | #[derive(Clone, Copy, Debug)] | ||
63 | #[allow(unused)] | ||
64 | pub enum ErrorCode { | ||
65 | ParseError = -32700, | ||
66 | InvalidRequest = -32600, | ||
67 | MethodNotFound = -32601, | ||
68 | InvalidParams = -32602, | ||
69 | InternalError = -32603, | ||
70 | ServerErrorStart = -32099, | ||
71 | ServerErrorEnd = -32000, | ||
72 | ServerNotInitialized = -32002, | ||
73 | UnknownErrorCode = -32001, | ||
74 | RequestCanceled = -32800, | ||
75 | ContentModified = -32801, | ||
76 | } | ||
77 | |||
78 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
79 | pub struct RawNotification { | ||
80 | pub method: String, | ||
81 | pub params: Value, | ||
82 | } | ||
83 | |||
84 | impl RawMessage { | ||
85 | pub fn read(r: &mut impl BufRead) -> Result<Option<RawMessage>> { | ||
86 | let text = match read_msg_text(r)? { | ||
87 | None => return Ok(None), | ||
88 | Some(text) => text, | ||
89 | }; | ||
90 | let msg = from_str(&text)?; | ||
91 | Ok(Some(msg)) | ||
92 | } | ||
93 | pub fn write(self, w: &mut impl Write) -> Result<()> { | ||
94 | #[derive(Serialize)] | ||
95 | struct JsonRpc { | ||
96 | jsonrpc: &'static str, | ||
97 | #[serde(flatten)] | ||
98 | msg: RawMessage, | ||
99 | } | ||
100 | let text = to_string(&JsonRpc { jsonrpc: "2.0", msg: self })?; | ||
101 | write_msg_text(w, &text)?; | ||
102 | Ok(()) | ||
103 | } | ||
104 | } | ||
105 | |||
106 | impl RawRequest { | ||
107 | pub fn new<R>(id: u64, params: &R::Params) -> RawRequest | ||
108 | where | ||
109 | R: Request, | ||
110 | R::Params: serde::Serialize, | ||
111 | { | ||
112 | RawRequest { id, method: R::METHOD.to_string(), params: to_value(params).unwrap() } | ||
113 | } | ||
114 | pub fn cast<R>(self) -> ::std::result::Result<(u64, R::Params), RawRequest> | ||
115 | where | ||
116 | R: Request, | ||
117 | R::Params: serde::de::DeserializeOwned, | ||
118 | { | ||
119 | if self.method != R::METHOD { | ||
120 | return Err(self); | ||
121 | } | ||
122 | let id = self.id; | ||
123 | let params: R::Params = from_value(self.params).unwrap(); | ||
124 | Ok((id, params)) | ||
125 | } | ||
126 | } | ||
127 | |||
128 | impl RawResponse { | ||
129 | pub fn ok<R>(id: u64, result: &R::Result) -> RawResponse | ||
130 | where | ||
131 | R: Request, | ||
132 | R::Result: serde::Serialize, | ||
133 | { | ||
134 | RawResponse { id, result: Some(to_value(&result).unwrap()), error: None } | ||
135 | } | ||
136 | pub fn err(id: u64, code: i32, message: String) -> RawResponse { | ||
137 | let error = RawResponseError { code, message, data: None }; | ||
138 | RawResponse { id, result: None, error: Some(error) } | ||
139 | } | ||
140 | } | ||
141 | |||
142 | impl RawNotification { | ||
143 | pub fn new<N>(params: &N::Params) -> RawNotification | ||
144 | where | ||
145 | N: Notification, | ||
146 | N::Params: serde::Serialize, | ||
147 | { | ||
148 | RawNotification { method: N::METHOD.to_string(), params: to_value(params).unwrap() } | ||
149 | } | ||
150 | pub fn is<N>(&self) -> bool | ||
151 | where | ||
152 | N: Notification, | ||
153 | { | ||
154 | self.method == N::METHOD | ||
155 | } | ||
156 | pub fn cast<N>(self) -> ::std::result::Result<N::Params, RawNotification> | ||
157 | where | ||
158 | N: Notification, | ||
159 | N::Params: serde::de::DeserializeOwned, | ||
160 | { | ||
161 | if !self.is::<N>() { | ||
162 | return Err(self); | ||
163 | } | ||
164 | Ok(from_value(self.params).unwrap()) | ||
165 | } | ||
166 | } | ||
167 | |||
168 | fn read_msg_text(inp: &mut impl BufRead) -> Result<Option<String>> { | ||
169 | let mut size = None; | ||
170 | let mut buf = String::new(); | ||
171 | loop { | ||
172 | buf.clear(); | ||
173 | if inp.read_line(&mut buf)? == 0 { | ||
174 | return Ok(None); | ||
175 | } | ||
176 | if !buf.ends_with("\r\n") { | ||
177 | Err(format!("malformed header: {:?}", buf))?; | ||
178 | } | ||
179 | let buf = &buf[..buf.len() - 2]; | ||
180 | if buf.is_empty() { | ||
181 | break; | ||
182 | } | ||
183 | let mut parts = buf.splitn(2, ": "); | ||
184 | let header_name = parts.next().unwrap(); | ||
185 | let header_value = parts.next().ok_or_else(|| format!("malformed header: {:?}", buf))?; | ||
186 | if header_name == "Content-Length" { | ||
187 | size = Some(header_value.parse::<usize>()?); | ||
188 | } | ||
189 | } | ||
190 | let size = size.ok_or("no Content-Length")?; | ||
191 | let mut buf = buf.into_bytes(); | ||
192 | buf.resize(size, 0); | ||
193 | inp.read_exact(&mut buf)?; | ||
194 | let buf = String::from_utf8(buf)?; | ||
195 | log::debug!("< {}", buf); | ||
196 | Ok(Some(buf)) | ||
197 | } | ||
198 | |||
199 | fn write_msg_text(out: &mut impl Write, msg: &str) -> Result<()> { | ||
200 | log::debug!("> {}", msg); | ||
201 | write!(out, "Content-Length: {}\r\n\r\n", msg.len())?; | ||
202 | out.write_all(msg.as_bytes())?; | ||
203 | out.flush()?; | ||
204 | Ok(()) | ||
205 | } | ||
diff --git a/crates/gen_lsp_server/src/stdio.rs b/crates/gen_lsp_server/src/stdio.rs deleted file mode 100644 index f8931f2dc..000000000 --- a/crates/gen_lsp_server/src/stdio.rs +++ /dev/null | |||
@@ -1,57 +0,0 @@ | |||
1 | use std::{ | ||
2 | io::{stdin, stdout}, | ||
3 | thread, | ||
4 | }; | ||
5 | |||
6 | use crossbeam_channel::{bounded, Receiver, Sender}; | ||
7 | use lsp_types::notification::Exit; | ||
8 | |||
9 | use crate::{RawMessage, Result}; | ||
10 | |||
11 | pub fn stdio_transport() -> (Receiver<RawMessage>, Sender<RawMessage>, Threads) { | ||
12 | let (writer_sender, writer_receiver) = bounded::<RawMessage>(16); | ||
13 | let writer = thread::spawn(move || { | ||
14 | let stdout = stdout(); | ||
15 | let mut stdout = stdout.lock(); | ||
16 | writer_receiver.into_iter().try_for_each(|it| it.write(&mut stdout))?; | ||
17 | Ok(()) | ||
18 | }); | ||
19 | let (reader_sender, reader_receiver) = bounded::<RawMessage>(16); | ||
20 | let reader = thread::spawn(move || { | ||
21 | let stdin = stdin(); | ||
22 | let mut stdin = stdin.lock(); | ||
23 | while let Some(msg) = RawMessage::read(&mut stdin)? { | ||
24 | let is_exit = match &msg { | ||
25 | RawMessage::Notification(n) => n.is::<Exit>(), | ||
26 | _ => false, | ||
27 | }; | ||
28 | |||
29 | reader_sender.send(msg).unwrap(); | ||
30 | |||
31 | if is_exit { | ||
32 | break; | ||
33 | } | ||
34 | } | ||
35 | Ok(()) | ||
36 | }); | ||
37 | let threads = Threads { reader, writer }; | ||
38 | (reader_receiver, writer_sender, threads) | ||
39 | } | ||
40 | |||
41 | pub struct Threads { | ||
42 | reader: thread::JoinHandle<Result<()>>, | ||
43 | writer: thread::JoinHandle<Result<()>>, | ||
44 | } | ||
45 | |||
46 | impl Threads { | ||
47 | pub fn join(self) -> Result<()> { | ||
48 | match self.reader.join() { | ||
49 | Ok(r) => r?, | ||
50 | Err(_) => Err("reader panicked")?, | ||
51 | } | ||
52 | match self.writer.join() { | ||
53 | Ok(r) => r, | ||
54 | Err(_) => Err("writer panicked")?, | ||
55 | } | ||
56 | } | ||
57 | } | ||