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
This commit is contained in:
Dylan Moonfire 2019-05-22 14:45:47 -05:00 committed by Dylan R. E. Moonfire
parent a25e103e45
commit cbd131acbe
4 changed files with 170 additions and 9 deletions

View file

@ -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"

View file

@ -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<RawMessage>,
sender: &Sender<RawMessage>,
) -> 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::<GotoDefinition>() {
Ok((id, _params)) => {
let resp = RawResponse::ok::<GotoDefinition>(
id,
&Some(GotoDefinitionResponse::Array(Vec::new())),
);
sender.send(RawMessage::Response(resp))?;
continue;
}
Err(req) => req,
};
// ...
}
RawMessage::Response(_resp) => (),
RawMessage::Notification(_not) => (),
}
}
Ok(())
}

View file

@ -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<RawMessage>,
sender: &Sender<RawMessage>,
) -> 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::<GotoDefinition>() {
Ok((id, params)) => {
info!("got gotoDefinition request #{}: {:?}", id, params);
let resp = RawResponse::ok::<GotoDefinition>(
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<RawMessage>) -> Option<RawRequest> {
info!("handle_shutdown: {:?}", req);
handle_shutdown(req, sender)
}

View file

@ -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::<GotoDefinition>() {
//! match req.cast::<GotoDefinition>() {
//! Ok((id, _params)) => {
//! let resp = RawResponse::ok::<GotoDefinition>(
//! id,
//! &Some(GotoDefinitionResponse::Array(Vec::new())),
//! );
//! sender.send(RawMessage::Response(resp));
//! sender.send(RawMessage::Response(resp))?;
//! continue;
//! },
//! Err(req) => req,