Skip to main content

lsp_server_tokio/
lib.rs

1#![warn(clippy::all, clippy::pedantic)]
2
3//! # lsp-server-tokio
4//!
5//! An async-first Rust crate for building LSP (Language Server Protocol) servers using Tokio.
6//!
7//! This crate provides transport-agnostic async LSP server infrastructure that handles
8//! protocol concerns so developers can focus on language-specific logic.
9//!
10//! ## Quick Start
11//!
12//! ```
13//! use lsp_server_tokio::{duplex_transport, Message, Request, Response};
14//! use futures::{SinkExt, StreamExt};
15//!
16//! # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
17//! // Create in-memory transport pair for testing
18//! let (mut client, mut server) = duplex_transport(4096);
19//!
20//! // Send a request from client
21//! let request = Message::Request(Request::new(1, "textDocument/hover", None));
22//! client.send(request).await.unwrap();
23//!
24//! // Receive on server
25//! if let Some(Ok(Message::Request(req))) = server.next().await {
26//!     println!("Received: {}", req.method);
27//!
28//!     // Send response back
29//!     let response = Message::Response(Response::ok(1, serde_json::json!({"contents": "Hello"})));
30//!     server.send(response).await.unwrap();
31//! }
32//!
33//! // Receive response on client
34//! if let Some(Ok(Message::Response(resp))) = client.next().await {
35//!     println!("Got response for id: {:?}", resp.id);
36//! }
37//! # });
38//! ```
39//!
40//! ## Core Types
41//!
42//! - [`RequestId`] - Identifies requests/responses (supports both integer and string IDs)
43//! - [`ErrorCode`] - LSP specification error codes
44//! - [`ResponseError`] - Error responses with code, message, and optional data
45//! - [`Message`] - Discriminated union of Request, Response, and Notification
46//! - [`Request`] - JSON-RPC request with id and method
47//! - [`Response`] - JSON-RPC response with result or error
48//! - [`Notification`] - JSON-RPC notification (no id, no response)
49//!
50//! ## Transport Layer
51//!
52//! - [`Transport`] - Type alias for `Framed<T, LspCodec>` providing Stream + Sink
53//! - [`transport()`] - Factory function wrapping any `AsyncRead` + `AsyncWrite`
54//! - [`duplex_transport()`] - Creates connected in-memory transports for testing
55//! - [`LspCodec`] - Encoder/Decoder for Content-Length message framing
56//!
57//! ## Request Routing
58//!
59//! The [`IncomingMessage`] enum classifies messages received from [`Connection::route()`]:
60//! - [`IncomingMessage::Request`] - A request with automatic [`CancellationToken`] for cooperative cancellation
61//! - [`IncomingMessage::Notification`] - A notification (no response expected)
62//! - [`IncomingMessage::ResponseRouted`] - A response delivered to an awaiting receiver
63//! - [`IncomingMessage::ResponseUnknown`] - A response for an unknown request ID
64
65pub mod codec;
66pub mod connection;
67pub mod error;
68pub mod lifecycle;
69pub mod message;
70pub mod request_id;
71pub mod request_queue;
72pub mod routing;
73pub mod transport;
74
75pub use codec::LspCodec;
76pub use connection::{Connection, Receiver, Sender, StdioConnection};
77pub use error::{ErrorCode, ResponseError};
78pub use lifecycle::{ExitCode, LifecycleState, ProtocolError};
79pub use message::{Message, Notification, Request, Response};
80pub use request_id::RequestId;
81pub use request_queue::{
82    parse_cancel_params, IncomingRequests, OutgoingRequests, RequestQueue, CANCEL_REQUEST_METHOD,
83};
84pub use routing::{cancelled_response, method_not_found_response, IncomingMessage};
85pub use transport::{duplex_transport, transport, Transport};
86
87// Re-export CancellationToken for ergonomic use with IncomingMessage::Request
88pub use tokio_util::sync::CancellationToken;