From c8688c1f4fef4e8874894ecd11006c1622d431f5 Mon Sep 17 00:00:00 2001 From: Micah Date: Wed, 27 Mar 2024 20:40:18 -0500 Subject: [PATCH] langauge server channel macro docs --- crates/language-server-macros/src/lib.rs | 98 ++++++++++++++++++- crates/language-server/src/language_server.rs | 6 +- 2 files changed, 94 insertions(+), 10 deletions(-) diff --git a/crates/language-server-macros/src/lib.rs b/crates/language-server-macros/src/lib.rs index 99150f982..3c0e5a316 100644 --- a/crates/language-server-macros/src/lib.rs +++ b/crates/language-server-macros/src/lib.rs @@ -4,14 +4,27 @@ use proc_macro::TokenStream; use quote::{format_ident, quote}; use syn::{parse_macro_input, FnArg, ImplItem, ItemImpl, ReturnType}; -/// Macro for generating tokio channels from [`lsp-types`](https://docs.rs/lsp-types). +/// Generates message channels and dispatch methods for a `tower_lsp::LanguageServer` implementation. /// -/// This procedural macro annotates the `tower_lsp::LanguageServer` trait implementation and generates -/// a struct full of tokio mpsc channels that can be used to signal the server to handle -/// defined requests and notifications. +/// This macro generates two structs: +/// - `MessageSenders`: Contains `tokio::sync::mpsc::UnboundedSender` channels for each method in the `LanguageServer` trait. +/// - `MessageReceivers`: Contains `tokio_stream::wrappers::UnboundedReceiverStream` streams for each method in the `LanguageServer` trait. +/// +/// It also generates a `setup_message_channels` function that initializes the channels and returns instances of the `MessageSenders` and `MessageReceivers` structs. +/// +/// # Example +/// +/// ```rust,ignore +/// use tower_lsp::LanguageServer; +/// +/// #[language_server_macros::message_channels] +/// #[tower_lsp::async_trait] +/// impl LanguageServer for Server { +/// // ... +/// } +/// ``` #[proc_macro_attribute] pub fn message_channels(_attr: TokenStream, item: TokenStream) -> TokenStream { - // let attr = parse_macro_input!(attr as Option); let channel_senders_struct_name = format_ident!( "MessageSenders", // attr.clone().map_or("MessageSenders".to_string(), |attr| attr.to_string()) @@ -213,11 +226,15 @@ fn gen_channel_structs( let dispatcher_fn = match params { Some(params) => quote! { + /// Forward the LSP request parameters to the designated channel. + /// + /// An oneshot receiver is returned which can optionally be used to get a response back from the channel listener. pub fn #sender_fn_name(&self, params: #params) -> #sender_fn_result { #send_payload } }, None => quote! { + /// Forward the LSP notification parameters to the designated channel. pub fn #sender_fn_name(&self) -> #sender_fn_result { #send_payload } @@ -231,14 +248,85 @@ fn gen_channel_structs( .collect(); quote! { + /// Struct containing `tokio::sync::mpsc::UnboundedSender` channels for each method in the `LanguageServer` trait. + /// + /// This struct is generated by the `#[message_channels]` macro. For each method in the `LanguageServer` trait, + /// it generates a corresponding field with a name in the format `_tx`. + /// + /// For each implemented LSP notification method, a channel of type `tokio::sync::mpsc::UnboundedSender` is generated, where `Params` is the method's parameter type. + /// For each implemented LSP request methods, a channel of type `tokio::sync::mpsc::UnboundedSender<(Params, tokio::sync::oneshot::Sender)` is generated, where `Params` is the method's parameter type and `Result` is the method's return type. + /// + /// The macro also generates corresponding `send_` helper methods for each implemented LSP method to allow sending + /// requests or notifications through the respective channels. + /// + /// # Example + /// + /// ```rust,ignore + /// use tower_lsp::{LanguageServer, Client, jsonrpc::Result}; + /// use lsp_types::{InitializeParams, InitializeResult}; + /// + /// struct Backend { + /// messaging: MessageSenders, + /// client: Client, + /// } + /// + /// #[tower_lsp::async_trait] + /// impl LanguageServer for Backend { + /// async fn initialize(&self, params: InitializeParams) -> Result { + /// let rx = self.messaging.send_initialize(params); + /// + /// match rx.await { + /// Ok(result) => { + /// self.client.log_message(lsp_types::MessageType::INFO, "Server initialized!").await; + /// Ok(result) + /// } + /// Err(e) => { + /// self.client.log_message(lsp_types::MessageType::ERROR, format!("Failed to initialize: {:?}", e)).await; + /// Err(jsonrpc::Error::internal_error()) + /// } + /// } + /// } + /// + /// // Other LanguageServer methods... + /// } + /// ``` pub struct #channel_receivers_struct_name { #channel_receivers_declarations } + /// Struct containing `tokio_stream::wrappers::UnboundedReceiverStream` streams for each implemented `LanguageServer` trait method. + /// + /// This struct is generated by the `#[message_channels]` macro. For each implemented method of the `LanguageServer` trait, + /// it generates a corresponding field with a name in the format `_stream`. + /// + /// The type of each field depends on the signature of the corresponding `LanguageServer` method: + /// - If the method has a return type, the field type is `tokio_stream::wrappers::UnboundedReceiverStream<(Params, tokio::sync::oneshot::Sender)>`, + /// where `Params` is the type of the method's parameter and `Result` is the return type. + /// - If the method doesn't have a return type, the field type is `tokio_stream::wrappers::UnboundedReceiverStream`. + /// + /// These streams can be used to handle incoming requests or notifications for each `LanguageServer` method. + /// + /// # Example + /// + /// ```rust,ignore + /// let (senders, receivers) = setup_message_channels(); + /// let mut initialized_stream = receivers.initialize_stream.fuse(); + /// loop { + /// select! { + /// Some((params, responder)) = initialized_stream.next() => { + /// // Handle initialization request + /// let result = lsp_types::InitializeResult { ... }; + /// let _ = responder.send(Ok(result)); + /// } + /// // ... + /// } + /// } + /// ``` pub struct #channel_senders_struct_name { #channel_senders_declarations } + /// Initializes the message channels and returns instances of the `MessageSenders` and `MessageReceivers` structs. pub fn setup_message_channels() -> (#channel_senders_struct_name, #channel_receivers_struct_name) { #channel_instantiations ( diff --git a/crates/language-server/src/language_server.rs b/crates/language-server/src/language_server.rs index 300e23140..87b68f677 100644 --- a/crates/language-server/src/language_server.rs +++ b/crates/language-server/src/language_server.rs @@ -35,12 +35,11 @@ impl Server { } } -#[language_server_macros::message_channels(MessageChannels)] +#[language_server_macros::message_channels] #[tower_lsp::async_trait] impl LanguageServer for Server { async fn initialize(&self, initialize_params: InitializeParams) -> Result { // forward the initialize request to the messaging system - // let messaging = self.messaging.read().await; let rx = self.messaging.send_initialize(initialize_params); info!("awaiting initialization result"); @@ -72,7 +71,6 @@ impl LanguageServer for Server { } async fn did_change(&self, params: lsp_types::DidChangeTextDocumentParams) { - // info!("sending did change to channel of capacity {}", self.messaging.did_change_tx.capacity()); self.messaging.send_did_change(params); } @@ -85,7 +83,6 @@ impl LanguageServer for Server { } async fn hover(&self, params: lsp_types::HoverParams) -> Result> { - // info!("sending hover to channel of capacity {}", self.messaging.hover_tx.capacity()); let rx = self.messaging.send_hover(params); rx.await.expect("hover response") } @@ -94,7 +91,6 @@ impl LanguageServer for Server { &self, params: lsp_types::GotoDefinitionParams, ) -> Result> { - // let messaging = self.messaging.read().await; let rx = self.messaging.send_goto_definition(params); rx.await.expect("goto definition response") }