Skip to content

Commit

Permalink
langauge server channel macro docs
Browse files Browse the repository at this point in the history
  • Loading branch information
micahscopes committed Mar 28, 2024
1 parent 061a538 commit c8688c1
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 10 deletions.
98 changes: 93 additions & 5 deletions crates/language-server-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<syn::Ident>);
let channel_senders_struct_name = format_ident!(
"MessageSenders",
// attr.clone().map_or("MessageSenders".to_string(), |attr| attr.to_string())
Expand Down Expand Up @@ -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
}
Expand All @@ -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 `<method_name>_tx`.
///
/// For each implemented LSP notification method, a channel of type `tokio::sync::mpsc::UnboundedSender<Params>` 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<Result>)` is generated, where `Params` is the method's parameter type and `Result` is the method's return type.
///
/// The macro also generates corresponding `send_<method_name>` 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<InitializeResult> {
/// 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 `<method_name>_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<Result>)>`,
/// 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<Params>`.
///
/// 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
(
Expand Down
6 changes: 1 addition & 5 deletions crates/language-server/src/language_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<InitializeResult> {
// 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");
Expand Down Expand Up @@ -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);
}

Expand All @@ -85,7 +83,6 @@ impl LanguageServer for Server {
}

async fn hover(&self, params: lsp_types::HoverParams) -> Result<Option<lsp_types::Hover>> {
// info!("sending hover to channel of capacity {}", self.messaging.hover_tx.capacity());
let rx = self.messaging.send_hover(params);
rx.await.expect("hover response")
}
Expand All @@ -94,7 +91,6 @@ impl LanguageServer for Server {
&self,
params: lsp_types::GotoDefinitionParams,
) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
// let messaging = self.messaging.read().await;
let rx = self.messaging.send_goto_definition(params);
rx.await.expect("goto definition response")
}
Expand Down

0 comments on commit c8688c1

Please sign in to comment.