Skip to content

Commit

Permalink
Implement support for client-initiated $/progress
Browse files Browse the repository at this point in the history
Adapters for `Iterator` and `Stream` are not currently included due to
previously unforeseen complexity stemming from the type state pattern.
  • Loading branch information
ebkalderon committed Jan 5, 2024
1 parent 7b58012 commit 49e1ce5
Show file tree
Hide file tree
Showing 4 changed files with 442 additions and 3 deletions.
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ pub extern crate lsp_types;
/// A re-export of [`async-trait`](https://docs.rs/async-trait) for convenience.
pub use async_trait::async_trait;

pub use self::service::progress::{
Bounded, Cancellable, NotCancellable, OngoingProgress, Progress, Unbounded,
};
pub use self::service::{Client, ClientSocket, ExitedError, LspService, LspServiceBuilder};
pub use self::transport::{Loopback, Server};

Expand Down
2 changes: 1 addition & 1 deletion src/service.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Service abstraction for language servers.
pub use self::client::{Client, ClientSocket, RequestStream, ResponseSink};
pub use self::client::{progress, Client, ClientSocket, RequestStream, ResponseSink};

pub(crate) use self::pending::Pending;
pub(crate) use self::state::{ServerState, State};
Expand Down
69 changes: 67 additions & 2 deletions src/service/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ use std::task::{Context, Poll};
use futures::channel::mpsc::{self, Sender};
use futures::future::BoxFuture;
use futures::sink::SinkExt;
use lsp_types::notification::*;
use lsp_types::request::*;
use lsp_types::*;
use serde::Serialize;
use serde_json::Value;
use tower::Service;
use tracing::{error, trace};

use self::pending::Pending;
use self::progress::Progress;
use super::state::{ServerState, State};
use super::ExitedError;
use crate::jsonrpc::{self, Error, ErrorCode, Id, Request, Response};

pub mod progress;

mod pending;
mod socket;

Expand Down Expand Up @@ -94,6 +95,7 @@ impl Client {
&self,
registrations: Vec<Registration>,
) -> jsonrpc::Result<()> {
use lsp_types::request::RegisterCapability;
self.send_request::<RegisterCapability>(RegistrationParams { registrations })
.await
}
Expand All @@ -114,6 +116,7 @@ impl Client {
&self,
unregisterations: Vec<Unregistration>,
) -> jsonrpc::Result<()> {
use lsp_types::request::UnregisterCapability;
self.send_request::<UnregisterCapability>(UnregistrationParams { unregisterations })
.await
}
Expand All @@ -126,6 +129,7 @@ impl Client {
///
/// [`window/showMessage`]: https://microsoft.github.io/language-server-protocol/specification#window_showMessage
pub async fn show_message<M: Display>(&self, typ: MessageType, message: M) {
use lsp_types::notification::ShowMessage;
self.send_notification_unchecked::<ShowMessage>(ShowMessageParams {
typ,
message: message.to_string(),
Expand All @@ -147,6 +151,7 @@ impl Client {
message: M,
actions: Option<Vec<MessageActionItem>>,
) -> jsonrpc::Result<Option<MessageActionItem>> {
use lsp_types::request::ShowMessageRequest;
self.send_request_unchecked::<ShowMessageRequest>(ShowMessageRequestParams {
typ,
message: message.to_string(),
Expand All @@ -161,6 +166,7 @@ impl Client {
///
/// [`window/logMessage`]: https://microsoft.github.io/language-server-protocol/specification#window_logMessage
pub async fn log_message<M: Display>(&self, typ: MessageType, message: M) {
use lsp_types::notification::LogMessage;
self.send_notification_unchecked::<LogMessage>(LogMessageParams {
typ,
message: message.to_string(),
Expand All @@ -187,6 +193,7 @@ impl Client {
///
/// This request was introduced in specification version 3.16.0.
pub async fn show_document(&self, params: ShowDocumentParams) -> jsonrpc::Result<bool> {
use lsp_types::request::ShowDocument;
let response = self.send_request::<ShowDocument>(params).await?;
Ok(response.success)
}
Expand All @@ -200,6 +207,7 @@ impl Client {
///
/// [`telemetry/event`]: https://microsoft.github.io/language-server-protocol/specification#telemetry_event
pub async fn telemetry_event<S: Serialize>(&self, data: S) {
use lsp_types::notification::TelemetryEvent;
match serde_json::to_value(data) {
Err(e) => error!("invalid JSON in `telemetry/event` notification: {}", e),
Ok(mut value) => {
Expand Down Expand Up @@ -236,6 +244,7 @@ impl Client {
///
/// This request was introduced in specification version 3.16.0.
pub async fn code_lens_refresh(&self) -> jsonrpc::Result<()> {
use lsp_types::request::CodeLensRefresh;
self.send_request::<CodeLensRefresh>(()).await
}

Expand All @@ -262,6 +271,7 @@ impl Client {
///
/// This request was introduced in specification version 3.16.0.
pub async fn semantic_tokens_refresh(&self) -> jsonrpc::Result<()> {
use lsp_types::request::SemanticTokensRefresh;
self.send_request::<SemanticTokensRefresh>(()).await
}

Expand All @@ -287,6 +297,7 @@ impl Client {
///
/// This request was introduced in specification version 3.17.0.
pub async fn inline_value_refresh(&self) -> jsonrpc::Result<()> {
use lsp_types::request::InlineValueRefreshRequest;
self.send_request::<InlineValueRefreshRequest>(()).await
}

Expand All @@ -312,6 +323,7 @@ impl Client {
///
/// This request was introduced in specification version 3.17.0.
pub async fn inlay_hint_refresh(&self) -> jsonrpc::Result<()> {
use lsp_types::request::InlayHintRefreshRequest;
self.send_request::<InlayHintRefreshRequest>(()).await
}

Expand Down Expand Up @@ -353,6 +365,7 @@ impl Client {
diags: Vec<Diagnostic>,
version: Option<i32>,
) {
use lsp_types::notification::PublishDiagnostics;
self.send_notification::<PublishDiagnostics>(PublishDiagnosticsParams::new(
uri, diags, version,
))
Expand Down Expand Up @@ -386,6 +399,7 @@ impl Client {
&self,
items: Vec<ConfigurationItem>,
) -> jsonrpc::Result<Vec<Value>> {
use lsp_types::request::WorkspaceConfiguration;
self.send_request::<WorkspaceConfiguration>(ConfigurationParams { items })
.await
}
Expand All @@ -410,6 +424,7 @@ impl Client {
///
/// This request was introduced in specification version 3.6.0.
pub async fn workspace_folders(&self) -> jsonrpc::Result<Option<Vec<WorkspaceFolder>>> {
use lsp_types::request::WorkspaceFoldersRequest;
self.send_request::<WorkspaceFoldersRequest>(()).await
}

Expand All @@ -430,10 +445,59 @@ impl Client {
&self,
edit: WorkspaceEdit,
) -> jsonrpc::Result<ApplyWorkspaceEditResponse> {
use lsp_types::request::ApplyWorkspaceEdit;
self.send_request::<ApplyWorkspaceEdit>(ApplyWorkspaceEditParams { edit, label: None })
.await
}

/// Starts a stream of `$/progress` notifications for a client-provided [`ProgressToken`].
///
/// This method also takes a `title` argument briefly describing the kind of operation being
/// performed, e.g. "Indexing" or "Linking Dependencies".
///
/// [`ProgressToken`]: https://docs.rs/lsp-types/latest/lsp_types/type.ProgressToken.html
///
/// # Initialization
///
/// These notifications will only be sent if the server is initialized.
///
/// # Examples
///
/// ```no_run
/// # use tower_lsp::{lsp_types::*, Client};
/// #
/// # struct Mock {
/// # client: Client,
/// # }
/// #
/// # impl Mock {
/// # async fn completion(&self, params: CompletionParams) {
/// # let work_done_token = ProgressToken::Number(1);
/// #
/// let progress = self
/// .client
/// .progress(work_done_token, "Progress Title")
/// .with_message("Working...")
/// .with_percentage(0)
/// .begin()
/// .await;
///
/// for percent in 1..=100 {
/// let msg = format!("Working... [{percent}/100]");
/// progress.report_with_message(msg, percent).await;
/// }
///
/// progress.finish_with_message("Done!").await;
/// # }
/// # }
/// ```
pub fn progress<T>(&self, token: ProgressToken, title: T) -> Progress
where
T: Into<String>,
{
Progress::new(self.clone(), token, title.into())
}

/// Sends a custom notification to the client.
///
/// # Initialization
Expand Down Expand Up @@ -563,6 +627,7 @@ mod tests {
use std::future::Future;

use futures::stream::StreamExt;
use lsp_types::notification::{LogMessage, PublishDiagnostics, ShowMessage, TelemetryEvent};
use serde_json::json;

use super::*;
Expand Down
Loading

0 comments on commit 49e1ce5

Please sign in to comment.