]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! A minimal example LSP server that can only respond to the `gotoDefinition` request. To use |
2 | //! this example, execute it and then send an `initialize` request. | |
3 | //! | |
4 | //! ```no_run | |
5 | //! Content-Length: 85 | |
6 | //! | |
7 | //! {"jsonrpc": "2.0", "method": "initialize", "id": 1, "params": {"capabilities": {}}} | |
8 | //! ``` | |
9 | //! | |
10 | //! This will respond with a server response. Then send it a `initialized` notification which will | |
11 | //! have no response. | |
12 | //! | |
13 | //! ```no_run | |
14 | //! Content-Length: 59 | |
15 | //! | |
16 | //! {"jsonrpc": "2.0", "method": "initialized", "params": {}} | |
17 | //! ``` | |
18 | //! | |
19 | //! Once these two are sent, then we enter the main loop of the server. The only request this | |
20 | //! example can handle is `gotoDefinition`: | |
21 | //! | |
22 | //! ```no_run | |
23 | //! Content-Length: 159 | |
24 | //! | |
25 | //! {"jsonrpc": "2.0", "method": "textDocument/definition", "id": 2, "params": {"textDocument": {"uri": "file://temp"}, "position": {"line": 1, "character": 1}}} | |
26 | //! ``` | |
27 | //! | |
28 | //! To finish up without errors, send a shutdown request: | |
29 | //! | |
30 | //! ```no_run | |
31 | //! Content-Length: 67 | |
32 | //! | |
33 | //! {"jsonrpc": "2.0", "method": "shutdown", "id": 3, "params": null} | |
34 | //! ``` | |
35 | //! | |
36 | //! The server will exit the main loop and finally we send a `shutdown` notification to stop | |
37 | //! the server. | |
38 | //! | |
39 | //! ``` | |
40 | //! Content-Length: 54 | |
41 | //! | |
42 | //! {"jsonrpc": "2.0", "method": "exit", "params": null} | |
43 | //! ``` | |
44 | use std::error::Error; | |
45 | ||
46 | use lsp_types::OneOf; | |
47 | use lsp_types::{ | |
48 | request::GotoDefinition, GotoDefinitionResponse, InitializeParams, ServerCapabilities, | |
49 | }; | |
50 | ||
51 | use lsp_server::{Connection, ExtractError, Message, Request, RequestId, Response}; | |
52 | ||
53 | fn main() -> Result<(), Box<dyn Error + Sync + Send>> { | |
54 | // Note that we must have our logging only write out to stderr. | |
55 | eprintln!("starting generic LSP server"); | |
56 | ||
57 | // Create the transport. Includes the stdio (stdin and stdout) versions but this could | |
58 | // also be implemented to use sockets or HTTP. | |
59 | let (connection, io_threads) = Connection::stdio(); | |
60 | ||
61 | // Run the server and wait for the two threads to end (typically by trigger LSP Exit event). | |
62 | let server_capabilities = serde_json::to_value(&ServerCapabilities { | |
63 | definition_provider: Some(OneOf::Left(true)), | |
64 | ..Default::default() | |
65 | }) | |
66 | .unwrap(); | |
67 | let initialization_params = connection.initialize(server_capabilities)?; | |
68 | main_loop(connection, initialization_params)?; | |
69 | io_threads.join()?; | |
70 | ||
71 | // Shut down gracefully. | |
72 | eprintln!("shutting down server"); | |
73 | Ok(()) | |
74 | } | |
75 | ||
76 | fn main_loop( | |
77 | connection: Connection, | |
78 | params: serde_json::Value, | |
79 | ) -> Result<(), Box<dyn Error + Sync + Send>> { | |
80 | let _params: InitializeParams = serde_json::from_value(params).unwrap(); | |
81 | eprintln!("starting example main loop"); | |
82 | for msg in &connection.receiver { | |
6522a427 | 83 | eprintln!("got msg: {msg:?}"); |
064997fb FG |
84 | match msg { |
85 | Message::Request(req) => { | |
86 | if connection.handle_shutdown(&req)? { | |
87 | return Ok(()); | |
88 | } | |
6522a427 | 89 | eprintln!("got request: {req:?}"); |
064997fb FG |
90 | match cast::<GotoDefinition>(req) { |
91 | Ok((id, params)) => { | |
6522a427 | 92 | eprintln!("got gotoDefinition request #{id}: {params:?}"); |
064997fb FG |
93 | let result = Some(GotoDefinitionResponse::Array(Vec::new())); |
94 | let result = serde_json::to_value(&result).unwrap(); | |
95 | let resp = Response { id, result: Some(result), error: None }; | |
96 | connection.sender.send(Message::Response(resp))?; | |
97 | continue; | |
98 | } | |
6522a427 | 99 | Err(err @ ExtractError::JsonError { .. }) => panic!("{err:?}"), |
064997fb FG |
100 | Err(ExtractError::MethodMismatch(req)) => req, |
101 | }; | |
102 | // ... | |
103 | } | |
104 | Message::Response(resp) => { | |
6522a427 | 105 | eprintln!("got response: {resp:?}"); |
064997fb FG |
106 | } |
107 | Message::Notification(not) => { | |
6522a427 | 108 | eprintln!("got notification: {not:?}"); |
064997fb FG |
109 | } |
110 | } | |
111 | } | |
112 | Ok(()) | |
113 | } | |
114 | ||
115 | fn cast<R>(req: Request) -> Result<(RequestId, R::Params), ExtractError<Request>> | |
116 | where | |
117 | R: lsp_types::request::Request, | |
118 | R::Params: serde::de::DeserializeOwned, | |
119 | { | |
120 | req.extract(R::METHOD) | |
121 | } |