//! 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 response. 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} //! ``` #![allow(clippy::print_stderr)] use std::error::Error; use lsp_types::OneOf; use lsp_types::{ request::GotoDefinition, GotoDefinitionResponse, InitializeParams, ServerCapabilities, }; use lsp_server::{Connection, ExtractError, Message, Request, RequestId, Response}; fn main() -> Result<(), Box> { // Note that we must have our logging only write out to stderr. eprintln!("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 (connection, io_threads) = Connection::stdio(); // Run the server and wait for the two threads to end (typically by trigger LSP Exit event). let server_capabilities = serde_json::to_value(&ServerCapabilities { definition_provider: Some(OneOf::Left(true)), ..Default::default() }) .unwrap(); let initialization_params = match connection.initialize(server_capabilities) { Ok(it) => it, Err(e) => { if e.channel_is_disconnected() { io_threads.join()?; } return Err(e.into()); } }; main_loop(connection, initialization_params)?; io_threads.join()?; // Shut down gracefully. eprintln!("shutting down server"); Ok(()) } fn main_loop( connection: Connection, params: serde_json::Value, ) -> Result<(), Box> { let _params: InitializeParams = serde_json::from_value(params).unwrap(); eprintln!("starting example main loop"); for msg in &connection.receiver { eprintln!("got msg: {msg:?}"); match msg { Message::Request(req) => { if connection.handle_shutdown(&req)? { return Ok(()); } eprintln!("got request: {req:?}"); match cast::(req) { Ok((id, params)) => { eprintln!("got gotoDefinition request #{id}: {params:?}"); let result = Some(GotoDefinitionResponse::Array(Vec::new())); let result = serde_json::to_value(&result).unwrap(); let resp = Response { id, result: Some(result), error: None }; connection.sender.send(Message::Response(resp))?; continue; } Err(err @ ExtractError::JsonError { .. }) => panic!("{err:?}"), Err(ExtractError::MethodMismatch(req)) => req, }; // ... } Message::Response(resp) => { eprintln!("got response: {resp:?}"); } Message::Notification(not) => { eprintln!("got notification: {not:?}"); } } } Ok(()) } fn cast(req: Request) -> Result<(RequestId, R::Params), ExtractError> where R: lsp_types::request::Request, R::Params: serde::de::DeserializeOwned, { req.extract(R::METHOD) }