From cbd131acbe497795028844a592a438b70a30ba32 Mon Sep 17 00:00:00 2001 From: Dylan Moonfire Date: Wed, 22 May 2019 14:45:47 -0500 Subject: updated gen_lsp_server examples - updated the documentation with an example that has no errors with current compiler - added two example code to test compilation and show in use --- crates/gen_lsp_server/Cargo.toml | 3 + .../gen_lsp_server/examples/01_gen_lsp_server.rs | 45 ++++++++ .../examples/02_gen_lsp_server_with_logging.rs | 118 +++++++++++++++++++++ crates/gen_lsp_server/src/lib.rs | 13 +-- 4 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 crates/gen_lsp_server/examples/01_gen_lsp_server.rs create mode 100644 crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs diff --git a/crates/gen_lsp_server/Cargo.toml b/crates/gen_lsp_server/Cargo.toml index ba8bfdbd3..fa2fefea5 100644 --- a/crates/gen_lsp_server/Cargo.toml +++ b/crates/gen_lsp_server/Cargo.toml @@ -14,3 +14,6 @@ failure = "0.1.4" serde_json = "1.0.34" serde = { version = "1.0.83", features = ["derive"] } crossbeam-channel = "0.3.5" + +[dev-dependencies] +flexi_logger = "0.11.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 new file mode 100644 index 000000000..60c581075 --- /dev/null +++ b/crates/gen_lsp_server/examples/01_gen_lsp_server.rs @@ -0,0 +1,45 @@ +use crossbeam_channel::{Sender, Receiver}; +use lsp_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(); + 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, + }; + 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(()) +} 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 new file mode 100644 index 000000000..27e4f1cbc --- /dev/null +++ b/crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs @@ -0,0 +1,118 @@ +//! A minimal example LSP server that can only respond to the `gotoDefinition` request. To use +//! this example, execute it and then send an `initialize` request. +//! +//! ```no_run +//! Content-Length: 85 +//! +//! {"jsonrpc": "2.0", "method": "initialize", "id": 1, "params": {"capabilities": {}}} +//! ``` +//! +//! This will respond with a server respose. Then send it a `initialized` notification which will +//! have no response. +//! +//! ```no_run +//! Content-Length: 59 +//! +//! {"jsonrpc": "2.0", "method": "initialized", "params": {}} +//! ``` +//! +//! Once these two are sent, then we enter the main loop of the server. The only request this +//! example can handle is `gotoDefinition`: +//! +//! ```no_run +//! Content-Length: 159 +//! +//! {"jsonrpc": "2.0", "method": "textDocument/definition", "id": 2, "params": {"textDocument": {"uri": "file://temp"}, "position": {"line": 1, "character": 1}}} +//! ``` +//! +//! To finish up without errors, send a shutdown request: +//! +//! ```no_run +//! Content-Length: 67 +//! +//! {"jsonrpc": "2.0", "method": "shutdown", "id": 3, "params": null} +//! ``` +//! +//! The server will exit the main loop and finally we send a `shutdown` notification to stop +//! the server. +//! +//! ``` +//! Content-Length: 54 +//! +//! {"jsonrpc": "2.0", "method": "exit", "params": null} +//! ``` + +use crossbeam_channel::{Sender, Receiver}; +use lsp_types::{ + ServerCapabilities, InitializeParams, + request::{GotoDefinition, GotoDefinitionResponse}, +}; +use log::info; +use gen_lsp_server::{ + run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse, RawRequest, +}; + +fn main() -> Result<(), failure::Error> { + // Set up logging. Because `stdio_transport` gets a lock on stdout and stdin, we must have + // our logging only write out to stderr. + flexi_logger::Logger::with_str("info").start().unwrap(); + info!("starting generic LSP server"); + + // Create the transport. Includes the stdio (stdin and stdout) versions but this could + // also be implemented to use sockets or HTTP. + let (receiver, sender, io_threads) = stdio_transport(); + + // Run the server and wait for the two threads to end (typically by trigger LSP Exit event). + run_server(ServerCapabilities::default(), receiver, sender, main_loop)?; + io_threads.join()?; + + // Shut down gracefully. + info!("shutting down server"); + Ok(()) +} + +fn main_loop( + _params: InitializeParams, + receiver: &Receiver, + sender: &Sender, +) -> Result<(), failure::Error> { + info!("starting example main loop"); + for msg in receiver { + info!("got msg: {:?}", msg); + match msg { + RawMessage::Request(req) => { + let req = match log_handle_shutdown(req, sender) { + None => return Ok(()), + Some(req) => req, + }; + info!("got request: {:?}", req); + match req.cast::() { + Ok((id, params)) => { + info!("got gotoDefinition request #{}: {:?}", id, params); + let resp = RawResponse::ok::( + id, + &Some(GotoDefinitionResponse::Array(Vec::new())), + ); + info!("sending gotoDefinition response: {:?}", resp); + sender.send(RawMessage::Response(resp))?; + continue; + } + Err(req) => req, + }; + // ... + } + RawMessage::Response(resp) => { + info!("got response: {:?}", resp); + } + RawMessage::Notification(not) => { + info!("got notification: {:?}", not); + } + } + } + Ok(()) +} + +pub fn log_handle_shutdown(req: RawRequest, sender: &Sender) -> Option { + info!("handle_shutdown: {:?}", req); + handle_shutdown(req, sender) +} diff --git a/crates/gen_lsp_server/src/lib.rs b/crates/gen_lsp_server/src/lib.rs index edbdda6c8..1cd5a3a7c 100644 --- a/crates/gen_lsp_server/src/lib.rs +++ b/crates/gen_lsp_server/src/lib.rs @@ -2,21 +2,16 @@ //! 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. +//! Run with `RUST_LOG=gen_lsp_server=debug` to see all the messages. //! //! ```no_run -//! extern crate gen_lsp_server; -//! extern crate lsp_types; -//! extern crate failure; -//! extern crate crossbeam_channel; -//! //! use crossbeam_channel::{Sender, Receiver}; //! use lsp_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( +//! run_server( //! ServerCapabilities::default(), //! receiver, //! sender, @@ -38,13 +33,13 @@ //! None => return Ok(()), //! Some(req) => req, //! }; -//! let req = match req.cast::() { +//! match req.cast::() { //! Ok((id, _params)) => { //! let resp = RawResponse::ok::( //! id, //! &Some(GotoDefinitionResponse::Array(Vec::new())), //! ); -//! sender.send(RawMessage::Response(resp)); +//! sender.send(RawMessage::Response(resp))?; //! continue; //! }, //! Err(req) => req, -- cgit v1.2.3