diff --git a/crates/gen_lsp_server/Cargo.toml b/crates/gen_lsp_server/Cargo.toml index 6580ba6e43..cf5c34a887 100644 --- a/crates/gen_lsp_server/Cargo.toml +++ b/crates/gen_lsp_server/Cargo.toml @@ -2,11 +2,13 @@ name = "gen_lsp_server" version = "0.1.0" authors = ["Aleksey Kladov "] +repository = "https://github.com/rust-analyzer/rust-analyzer" +license = "MIT OR Apache-2.0" +description = "Generic LSP server scaffold." [dependencies] languageserver-types = "0.51.0" log = "0.4.3" - failure = "0.1.2" serde_json = "1.0.24" serde = "1.0.71" diff --git a/crates/gen_lsp_server/src/lib.rs b/crates/gen_lsp_server/src/lib.rs index baca921dfc..645728a573 100644 --- a/crates/gen_lsp_server/src/lib.rs +++ b/crates/gen_lsp_server/src/lib.rs @@ -1,3 +1,65 @@ +//! A language server scaffold, exposing synchroneous crossbeam-channel based API. +//! This crate handles protocol handshaking and parsing messages, while you +//! control the message dispatch loop yourself. +//! +//! Run with `RUST_LOG=sync_lsp_server=debug` to see all the messages. +//! +//! ```no_run +//! extern crate gen_lsp_server; +//! extern crate languageserver_types; +//! extern crate failure; +//! extern crate crossbeam_channel; +//! +//! use crossbeam_channel::{Sender, Receiver}; +//! use languageserver_types::{ServerCapabilities, InitializeParams, request::{GotoDefinition, GotoDefinitionResponse}}; +//! use gen_lsp_server::{run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse}; +//! +//! fn main() -> Result<(), failure::Error> { +//! let (receiver, sender, io_threads) = stdio_transport(); +//! gen_lsp_server::run_server( +//! ServerCapabilities::default(), +//! receiver, +//! sender, +//! main_loop, +//! )?; +//! io_threads.join()?; +//! Ok(()) +//! } +//! +//! fn main_loop( +//! _params: InitializeParams, +//! receiver: &Receiver, +//! sender: &Sender, +//! ) -> Result<(), failure::Error> { +//! for msg in receiver { +//! match msg { +//! RawMessage::Request(req) => { +//! let req = match handle_shutdown(req, sender) { +//! None => return Ok(()), +//! Some(req) => req, +//! }; +//! let req = match req.cast::() { +//! Ok((id, _params)) => { +//! let resp = RawResponse::ok::( +//! id, +//! &Some(GotoDefinitionResponse::Array(Vec::new())), +//! ); +//! sender.send(RawMessage::Response(resp)); +//! continue; +//! }, +//! Err(req) => req, +//! }; +//! // ... +//! } +//! RawMessage::Response(_resp) => (), +//! RawMessage::Notification(_not) => (), +//! } +//! } +//! Ok(()) +//! } +//! ``` + + #[macro_use] extern crate failure; #[macro_use] @@ -25,20 +87,26 @@ pub use { stdio::{stdio_transport, Threads}, }; +/// Main entry point: runs the server from initialization to shutdown. +/// To attach server to standard input/output streams, use `stdio_transport` +/// function to create corresponding `sender` and `receiver` pair. +/// +///`server` should use `handle_shutdown` function to handle the `Shutdown` +/// request. pub fn run_server( caps: ServerCapabilities, + receiver: Receiver, + sender: Sender, server: impl FnOnce( InitializeParams, - &mut Receiver, - &mut Sender, + &Receiver, + &Sender, ) -> Result<()>, - mut receiver: Receiver, - mut sender: Sender, ) -> Result<()> { info!("lsp server initializes"); - let params = initialize(&mut receiver, &mut sender, caps)?; + let params = initialize(&receiver, &sender, caps)?; info!("lsp server initialized, serving requests"); - server(params, &mut receiver, &mut sender)?; + server(params, &receiver, &sender)?; info!("lsp server waiting for exit notification"); match receiver.recv() { Some(RawMessage::Notification(n)) => { @@ -52,6 +120,7 @@ pub fn run_server( Ok(()) } +/// if `req` is `Shutdown`, respond to it and return `None`, otherwise return `Some(req)` pub fn handle_shutdown(req: RawRequest, sender: &Sender) -> Option { match req.cast::() { Ok((id, ())) => { @@ -64,8 +133,8 @@ pub fn handle_shutdown(req: RawRequest, sender: &Sender) -> Option, - sender: &mut Sender, + receiver: &Receiver, + sender: &Sender, caps: ServerCapabilities, ) -> Result { let (id, params) = match receiver.recv() { diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index c547764f66..e5d1792b7e 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs @@ -35,14 +35,14 @@ fn main_inner() -> Result<()> { let cwd = ::std::env::current_dir()?; run_server( ra_lsp_server::server_capabilities(), + receiver, + sender, |params, r, s| { let root = params.root_uri .and_then(|it| it.to_file_path().ok()) .unwrap_or(cwd); ra_lsp_server::main_loop(false, root, r, s) }, - receiver, - sender, )?; info!("shutting down IO..."); threads.join()?; diff --git a/crates/ra_lsp_server/src/main_loop/mod.rs b/crates/ra_lsp_server/src/main_loop/mod.rs index abc58b70ec..47a9b202ef 100644 --- a/crates/ra_lsp_server/src/main_loop/mod.rs +++ b/crates/ra_lsp_server/src/main_loop/mod.rs @@ -35,8 +35,8 @@ enum Task { pub fn main_loop( internal_mode: bool, root: PathBuf, - msg_receriver: &mut Receiver, - msg_sender: &mut Sender, + msg_receriver: &Receiver, + msg_sender: &Sender, ) -> Result<()> { let pool = rayon::ThreadPoolBuilder::new() .num_threads(4) @@ -88,8 +88,8 @@ fn main_loop_inner( internal_mode: bool, ws_root: PathBuf, pool: &ThreadPool, - msg_sender: &mut Sender, - msg_receiver: &mut Receiver, + msg_sender: &Sender, + msg_receiver: &Receiver, task_sender: Sender, task_receiver: Receiver, fs_worker: Worker)>, @@ -212,7 +212,7 @@ fn main_loop_inner( fn on_task( task: Task, - msg_sender: &mut Sender, + msg_sender: &Sender, pending_requests: &mut HashMap, ) { match task { @@ -266,7 +266,7 @@ fn on_request( } fn on_notification( - msg_sender: &mut Sender, + msg_sender: &Sender, state: &mut ServerWorldState, pending_requests: &mut HashMap, subs: &mut Subscriptions,