aboutsummaryrefslogtreecommitdiff
path: root/crates/gen_lsp_server/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/gen_lsp_server/src/lib.rs')
-rw-r--r--crates/gen_lsp_server/src/lib.rs136
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
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}