about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/lib/lsp-server/examples
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2024-04-21 14:40:10 +0200
committerLukas Wirth <lukastw97@gmail.com>2024-04-21 16:26:55 +0200
commit7c3926345004c4d679c835760dbea5438a4f9d7b (patch)
tree600a079cdebc5cdd91b5ed272f796e472401e5af /src/tools/rust-analyzer/lib/lsp-server/examples
parent55d9a533b309119c8acd13061581b43ae8840823 (diff)
downloadrust-7c3926345004c4d679c835760dbea5438a4f9d7b.tar.gz
rust-7c3926345004c4d679c835760dbea5438a4f9d7b.zip
Allow rust files to be used linkedProjects
Diffstat (limited to 'src/tools/rust-analyzer/lib/lsp-server/examples')
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/examples/goto_def.rs132
1 files changed, 132 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/lib/lsp-server/examples/goto_def.rs b/src/tools/rust-analyzer/lib/lsp-server/examples/goto_def.rs
new file mode 100644
index 00000000000..6ad144b85fe
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/lsp-server/examples/goto_def.rs
@@ -0,0 +1,132 @@
+//! 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)
+}