mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 03:54:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			132 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! 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<dyn Error + Sync + Send>> {
 | |
|     // 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<dyn Error + Sync + Send>> {
 | |
|     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::<GotoDefinition>(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<R>(req: Request) -> Result<(RequestId, R::Params), ExtractError<Request>>
 | |
| where
 | |
|     R: lsp_types::request::Request,
 | |
|     R::Params: serde::de::DeserializeOwned,
 | |
| {
 | |
|     req.extract(R::METHOD)
 | |
| }
 | 
