diff options
Diffstat (limited to 'crates/gen_lsp_server/src/lib.rs')
-rw-r--r-- | crates/gen_lsp_server/src/lib.rs | 136 |
1 files changed, 0 insertions, 136 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 | } | ||