tower_lsp_server/service/
client.rs

1//! Types for sending data to and from the language client.
2
3pub use self::socket::{ClientSocket, RequestStream, ResponseSink};
4
5use std::{
6    fmt::{self, Debug, Display, Formatter},
7    sync::{
8        Arc,
9        atomic::{AtomicU32, Ordering},
10    },
11    task::{Context, Poll},
12};
13
14use futures::{
15    channel::mpsc::{self, Sender},
16    future::BoxFuture,
17    sink::SinkExt,
18};
19use ls_types::{notification, request, *};
20use serde::Serialize;
21use tower::Service;
22use tracing::{error, trace};
23
24use self::pending::Pending;
25use self::progress::Progress;
26use super::ExitedError;
27use super::state::{ServerState, State};
28use crate::jsonrpc::{self, Error, ErrorCode, Id, Request, Response};
29
30pub mod progress;
31
32mod pending;
33mod socket;
34
35struct ClientInner {
36    tx: Sender<Request>,
37    request_id: AtomicU32,
38    pending: Arc<Pending>,
39    state: Arc<ServerState>,
40}
41
42/// Handle for communicating with the language client.
43///
44/// This type provides a very cheap implementation of [`Clone`] so API consumers can cheaply clone
45/// and pass it around as needed.
46///
47/// It also implements [`tower::Service`] in order to remain independent from the underlying
48/// transport and to facilitate further abstraction with middleware.
49#[derive(Clone)]
50pub struct Client {
51    inner: Arc<ClientInner>,
52}
53
54impl Client {
55    pub(super) fn new(state: Arc<ServerState>) -> (Self, ClientSocket) {
56        let (tx, rx) = mpsc::channel(1);
57        let pending = Arc::new(Pending::new());
58
59        let client = Self {
60            inner: Arc::new(ClientInner {
61                tx,
62                request_id: AtomicU32::new(0),
63                pending: pending.clone(),
64                state: state.clone(),
65            }),
66        };
67
68        (client, ClientSocket { rx, pending, state })
69    }
70
71    /// Disconnects the `Client` from its corresponding `LspService`.
72    ///
73    /// Closing the client is not required, but doing so will ensure that no more messages can be
74    /// produced. The receiver of the messages will be able to consume any in-flight messages and
75    /// then will observe the end of the stream.
76    ///
77    /// If the client is never closed and never dropped, the receiver of the messages will never
78    /// observe the end of the stream.
79    pub(crate) fn close(&self) {
80        self.inner.tx.clone().close_channel();
81    }
82}
83
84impl Client {
85    // Lifecycle Messages
86
87    /// Registers a new capability with the client.
88    ///
89    /// This corresponds to the [`client/registerCapability`] request.
90    ///
91    /// [`client/registerCapability`]: https://microsoft.github.io/language-server-protocol/specification#client_registerCapability
92    ///
93    /// # Initialization
94    ///
95    /// If the request is sent to the client before the server has been initialized, this will
96    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
97    ///
98    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
99    ///
100    /// # Errors
101    ///
102    /// - The request to the client fails
103    pub async fn register_capability(
104        &self,
105        registrations: Vec<Registration>,
106    ) -> jsonrpc::Result<()> {
107        self.send_request::<request::RegisterCapability>(RegistrationParams { registrations })
108            .await
109    }
110
111    /// Unregisters a capability with the client.
112    ///
113    /// This corresponds to the [`client/unregisterCapability`] request.
114    ///
115    /// [`client/unregisterCapability`]: https://microsoft.github.io/language-server-protocol/specification#client_unregisterCapability
116    ///
117    /// # Initialization
118    ///
119    /// If the request is sent to the client before the server has been initialized, this will
120    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
121    ///
122    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
123    ///
124    /// # Errors
125    ///
126    /// - The request to the client fails
127    pub async fn unregister_capability(
128        &self,
129        unregisterations: Vec<Unregistration>,
130    ) -> jsonrpc::Result<()> {
131        self.send_request::<request::UnregisterCapability>(UnregistrationParams {
132            unregisterations,
133        })
134        .await
135    }
136
137    // Window Features
138
139    /// Notifies the client to display a particular message in the user interface.
140    ///
141    /// This corresponds to the [`window/showMessage`] notification.
142    ///
143    /// [`window/showMessage`]: https://microsoft.github.io/language-server-protocol/specification#window_showMessage
144    pub async fn show_message<M: Display>(&self, typ: MessageType, message: M) {
145        self.send_notification_unchecked::<notification::ShowMessage>(ShowMessageParams {
146            typ,
147            message: message.to_string(),
148        })
149        .await;
150    }
151
152    /// Requests the client to display a particular message in the user interface.
153    ///
154    /// Unlike the `show_message` notification, this request can also pass a list of actions and
155    /// wait for an answer from the client.
156    ///
157    /// This corresponds to the [`window/showMessageRequest`] request.
158    ///
159    /// [`window/showMessageRequest`]: https://microsoft.github.io/language-server-protocol/specification#window_showMessageRequest
160    ///
161    /// # Errors
162    ///
163    /// - The request to the client fails
164    pub async fn show_message_request<M: Display>(
165        &self,
166        typ: MessageType,
167        message: M,
168        actions: Option<Vec<MessageActionItem>>,
169    ) -> jsonrpc::Result<Option<MessageActionItem>> {
170        self.send_request_unchecked::<request::ShowMessageRequest>(ShowMessageRequestParams {
171            typ,
172            message: message.to_string(),
173            actions,
174        })
175        .await
176    }
177
178    /// Notifies the client to log a particular message.
179    ///
180    /// This corresponds to the [`window/logMessage`] notification.
181    ///
182    /// [`window/logMessage`]: https://microsoft.github.io/language-server-protocol/specification#window_logMessage
183    pub async fn log_message<M: Display>(&self, typ: MessageType, message: M) {
184        self.send_notification_unchecked::<notification::LogMessage>(LogMessageParams {
185            typ,
186            message: message.to_string(),
187        })
188        .await;
189    }
190
191    /// Asks the client to display a particular resource referenced by a URI in the user interface.
192    ///
193    /// Returns `Ok(true)` if the document was successfully shown, or `Ok(false)` otherwise.
194    ///
195    /// This corresponds to the [`window/showDocument`] request.
196    ///
197    /// [`window/showDocument`]: https://microsoft.github.io/language-server-protocol/specification#window_showDocument
198    ///
199    /// # Initialization
200    ///
201    /// If the request is sent to the client before the server has been initialized, this will
202    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
203    ///
204    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
205    ///
206    /// # Compatibility
207    ///
208    /// This request was introduced in specification version 3.16.0.
209    ///
210    /// # Errors
211    ///
212    /// - The request to the client fails
213    pub async fn show_document(&self, params: ShowDocumentParams) -> jsonrpc::Result<bool> {
214        self.send_request::<request::ShowDocument>(params)
215            .await
216            .map(|res| res.success)
217    }
218
219    // TODO: Add `work_done_progress_create()` here (since 3.15.0) when supported by `tower-lsp`.
220    // https://github.com/ebkalderon/tower-lsp/issues/176
221
222    /// Notifies the client to log a telemetry event.
223    ///
224    /// This corresponds to the [`telemetry/event`] notification.
225    ///
226    /// [`telemetry/event`]: https://microsoft.github.io/language-server-protocol/specification#telemetry_event
227    pub async fn telemetry_event<S: Serialize>(&self, data: S) {
228        match serde_json::to_value(data) {
229            Err(e) => error!("invalid JSON in `telemetry/event` notification: {}", e),
230            Ok(value) => {
231                let value = match value {
232                    LSPAny::Object(value) => OneOf::Left(value),
233                    LSPAny::Array(value) => OneOf::Right(value),
234                    value => OneOf::Right(vec![value]),
235                };
236                self.send_notification_unchecked::<notification::TelemetryEvent>(value)
237                    .await;
238            }
239        }
240    }
241
242    /// Asks the client to refresh the code lenses currently shown in editors. As a result, the
243    /// client should ask the server to recompute the code lenses for these editors.
244    ///
245    /// This is useful if a server detects a configuration change which requires a re-calculation
246    /// of all code lenses.
247    ///
248    /// Note that the client still has the freedom to delay the re-calculation of the code lenses
249    /// if for example an editor is currently not visible.
250    ///
251    /// This corresponds to the [`workspace/codeLens/refresh`] request.
252    ///
253    /// [`workspace/codeLens/refresh`]: https://microsoft.github.io/language-server-protocol/specification#codeLens_refresh
254    ///
255    /// # Initialization
256    ///
257    /// If the request is sent to the client before the server has been initialized, this will
258    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
259    ///
260    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
261    ///
262    /// # Compatibility
263    ///
264    /// This request was introduced in specification version 3.16.0.
265    ///
266    /// # Errors
267    ///
268    /// - The request to the client fails
269    pub async fn code_lens_refresh(&self) -> jsonrpc::Result<()> {
270        self.send_request::<ls_types::request::CodeLensRefresh>(())
271            .await
272    }
273
274    /// Asks the client to refresh the editors for which this server provides semantic tokens. As a
275    /// result, the client should ask the server to recompute the semantic tokens for these
276    /// editors.
277    ///
278    /// This is useful if a server detects a project-wide configuration change which requires a
279    /// re-calculation of all semantic tokens. Note that the client still has the freedom to delay
280    /// the re-calculation of the semantic tokens if for example an editor is currently not visible.
281    ///
282    /// This corresponds to the [`workspace/semanticTokens/refresh`] request.
283    ///
284    /// [`workspace/semanticTokens/refresh`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens
285    ///
286    /// # Initialization
287    ///
288    /// If the request is sent to the client before the server has been initialized, this will
289    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
290    ///
291    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
292    ///
293    /// # Compatibility
294    ///
295    /// This request was introduced in specification version 3.16.0.
296    ///
297    /// # Errors
298    ///
299    /// - The request to the client fails
300    pub async fn semantic_tokens_refresh(&self) -> jsonrpc::Result<()> {
301        self.send_request::<ls_types::request::SemanticTokensRefresh>(())
302            .await
303    }
304
305    /// Asks the client to refresh the inline values currently shown in editors. As a result, the
306    /// client should ask the server to recompute the inline values for these editors.
307    ///
308    /// This is useful if a server detects a configuration change which requires a re-calculation
309    /// of all inline values. Note that the client still has the freedom to delay the
310    /// re-calculation of the inline values if for example an editor is currently not visible.
311    ///
312    /// This corresponds to the [`workspace/inlineValue/refresh`] request.
313    ///
314    /// [`workspace/inlineValue/refresh`]: https://microsoft.github.io/language-server-protocol/specification#workspace_inlineValue_refresh
315    ///
316    /// # Initialization
317    ///
318    /// If the request is sent to the client before the server has been initialized, this will
319    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
320    ///
321    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
322    ///
323    /// # Compatibility
324    ///
325    /// This request was introduced in specification version 3.17.0.
326    ///
327    /// # Errors
328    ///
329    /// - The request to the client fails
330    pub async fn inline_value_refresh(&self) -> jsonrpc::Result<()> {
331        self.send_request::<request::InlineValueRefreshRequest>(())
332            .await
333    }
334
335    /// Asks the client to refresh the inlay hints currently shown in editors. As a result, the
336    /// client should ask the server to recompute the inlay hints for these editors.
337    ///
338    /// This is useful if a server detects a configuration change which requires a re-calculation
339    /// of all inlay hints. Note that the client still has the freedom to delay the re-calculation
340    /// of the inlay hints if for example an editor is currently not visible.
341    ///
342    /// This corresponds to the [`workspace/inlayHint/refresh`] request.
343    ///
344    /// [`workspace/inlayHint/refresh`]: https://microsoft.github.io/language-server-protocol/specification#workspace_inlayHint_refresh
345    ///
346    /// # Initialization
347    ///
348    /// If the request is sent to the client before the server has been initialized, this will
349    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
350    ///
351    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
352    ///
353    /// # Compatibility
354    ///
355    /// This request was introduced in specification version 3.17.0.
356    ///
357    /// # Errors
358    ///
359    /// - The request to the client fails
360    pub async fn inlay_hint_refresh(&self) -> jsonrpc::Result<()> {
361        self.send_request::<request::InlayHintRefreshRequest>(())
362            .await
363    }
364
365    /// Asks the client to refresh all needed document and workspace diagnostics.
366    ///
367    /// This is useful if a server detects a project wide configuration change which requires a
368    /// re-calculation of all diagnostics.
369    ///
370    /// This corresponds to the [`workspace/diagnostic/refresh`] request.
371    ///
372    /// [`workspace/diagnostic/refresh`]: https://microsoft.github.io/language-server-protocol/specification#diagnostic_refresh
373    ///
374    /// # Initialization
375    ///
376    /// If the request is sent to the client before the server has been initialized, this will
377    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
378    ///
379    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
380    ///
381    /// # Compatibility
382    ///
383    /// This request was introduced in specification version 3.17.0.
384    ///
385    /// # Errors
386    ///
387    /// - The request to the client fails
388    pub async fn workspace_diagnostic_refresh(&self) -> jsonrpc::Result<()> {
389        self.send_request::<request::WorkspaceDiagnosticRefresh>(())
390            .await
391    }
392
393    /// Submits validation diagnostics for an open file with the given URI.
394    ///
395    /// This corresponds to the [`textDocument/publishDiagnostics`] notification.
396    ///
397    /// [`textDocument/publishDiagnostics`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics
398    ///
399    /// # Initialization
400    ///
401    /// This notification will only be sent if the server is initialized.
402    pub async fn publish_diagnostics(
403        &self,
404        uri: Uri,
405        diags: Vec<Diagnostic>,
406        version: Option<i32>,
407    ) {
408        self.send_notification::<notification::PublishDiagnostics>(PublishDiagnosticsParams::new(
409            uri, diags, version,
410        ))
411        .await;
412    }
413
414    /// Creates a work done progress for the given token. Per the spec, the server must not send
415    /// any progress notifications to the given token if an error occurs in this request.
416    ///
417    /// This corresponds to the [`window/workDoneProgress/create`] request.
418    ///
419    /// [`window/workDoneProgress/create`]: https://microsoft.github.io/language-server-protocol/specification#window_workDoneProgress_create
420    ///
421    /// # Initialization
422    ///
423    /// If the request is sent to the client before the server has been initialized, this will
424    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
425    ///
426    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
427    ///
428    /// # Compatibility
429    ///
430    /// This request was introduced in specification version 3.15.0.
431    ///
432    /// # Errors
433    ///
434    /// - The request to the client fails
435    pub async fn create_work_done_progress(&self, token: ProgressToken) -> jsonrpc::Result<()> {
436        self.send_request::<request::WorkDoneProgressCreate>(WorkDoneProgressCreateParams { token })
437            .await
438    }
439
440    // Workspace Features
441
442    /// Fetches configuration settings from the client.
443    ///
444    /// The request can fetch several configuration settings in one roundtrip. The order of the
445    /// returned configuration settings correspond to the order of the passed
446    /// [`ConfigurationItem`]s (e.g. the first item in the response is the result for the first
447    /// configuration item in the params).
448    ///
449    /// This corresponds to the [`workspace/configuration`] request.
450    ///
451    /// [`workspace/configuration`]: https://microsoft.github.io/language-server-protocol/specification#workspace_configuration
452    ///
453    /// # Initialization
454    ///
455    /// If the request is sent to the client before the server has been initialized, this will
456    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
457    ///
458    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
459    ///
460    /// # Compatibility
461    ///
462    /// This request was introduced in specification version 3.6.0.
463    ///
464    /// # Errors
465    ///
466    /// - The request to the client fails
467    pub async fn configuration(
468        &self,
469        items: Vec<ConfigurationItem>,
470    ) -> jsonrpc::Result<Vec<LSPAny>> {
471        self.send_request::<request::WorkspaceConfiguration>(ConfigurationParams { items })
472            .await
473    }
474
475    /// Fetches the current open list of workspace folders.
476    ///
477    /// Returns `None` if only a single file is open in the tool. Returns an empty `Vec` if a
478    /// workspace is open but no folders are configured.
479    ///
480    /// This corresponds to the [`workspace/workspaceFolders`] request.
481    ///
482    /// [`workspace/workspaceFolders`]: https://microsoft.github.io/language-server-protocol/specification#workspace_workspaceFolders
483    ///
484    /// # Initialization
485    ///
486    /// If the request is sent to the client before the server has been initialized, this will
487    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
488    ///
489    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
490    ///
491    /// # Compatibility
492    ///
493    /// This request was introduced in specification version 3.6.0.
494    ///
495    /// # Errors
496    ///
497    /// - The request to the client fails
498    pub async fn workspace_folders(&self) -> jsonrpc::Result<Option<Vec<WorkspaceFolder>>> {
499        self.send_request::<request::WorkspaceFoldersRequest>(())
500            .await
501    }
502
503    /// Requests a workspace resource be edited on the client side and returns whether the edit was
504    /// applied.
505    ///
506    /// This corresponds to the [`workspace/applyEdit`] request.
507    ///
508    /// [`workspace/applyEdit`]: https://microsoft.github.io/language-server-protocol/specification#workspace_applyEdit
509    ///
510    /// # Initialization
511    ///
512    /// If the request is sent to the client before the server has been initialized, this will
513    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
514    ///
515    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
516    ///
517    /// # Errors
518    ///
519    /// - The request to the client fails
520    pub async fn apply_edit(
521        &self,
522        edit: WorkspaceEdit,
523    ) -> jsonrpc::Result<ApplyWorkspaceEditResponse> {
524        self.send_request::<request::ApplyWorkspaceEdit>(ApplyWorkspaceEditParams {
525            edit,
526            label: None,
527        })
528        .await
529    }
530
531    /// Starts a stream of `$/progress` notifications for a client-provided [`ProgressToken`].
532    ///
533    /// This method also takes a `title` argument briefly describing the kind of operation being
534    /// performed, e.g. "Indexing" or "Linking Dependencies".
535    ///
536    /// [`ProgressToken`]: https://docs.rs/lsp-types/latest/ls_types/lsp/type.ProgressToken.html
537    ///
538    /// # Initialization
539    ///
540    /// These notifications will only be sent if the server is initialized.
541    ///
542    /// # Examples
543    ///
544    /// ```no_run
545    /// # use tower_lsp_server::{ls_types::*, Client};
546    /// #
547    /// # struct Mock {
548    /// #     client: Client,
549    /// # }
550    /// #
551    /// # impl Mock {
552    /// # async fn completion(&self, params: CompletionParams) {
553    /// # let work_done_token = ProgressToken::Number(1);
554    /// #
555    /// let progress = self
556    ///     .client
557    ///     .progress(work_done_token, "Progress Title")
558    ///     .with_message("Working...")
559    ///     .with_percentage(0)
560    ///     .begin()
561    ///     .await;
562    ///
563    /// for percent in 1..=100 {
564    ///     let msg = format!("Working... [{percent}/100]");
565    ///     progress.report_with_message(msg, percent).await;
566    /// }
567    ///
568    /// progress.finish_with_message("Done!").await;
569    /// # }
570    /// # }
571    /// ```
572    pub fn progress<T>(&self, token: ProgressToken, title: T) -> Progress
573    where
574        T: Into<String>,
575    {
576        Progress::new(self.clone(), token, title.into())
577    }
578
579    /// Sends a custom notification to the client.
580    ///
581    /// # Initialization
582    ///
583    /// This notification will only be sent if the server is initialized.
584    pub async fn send_notification<N>(&self, params: N::Params)
585    where
586        N: notification::Notification,
587    {
588        if let State::Initialized | State::ShutDown = self.inner.state.get() {
589            self.send_notification_unchecked::<N>(params).await;
590        } else {
591            let msg = Request::from_notification::<N>(params);
592            trace!("server not initialized, supressing message: {}", msg);
593        }
594    }
595
596    async fn send_notification_unchecked<N>(&self, params: N::Params)
597    where
598        N: notification::Notification,
599    {
600        let request = Request::from_notification::<N>(params);
601        if self.clone().call(request).await.is_err() {
602            error!("failed to send notification");
603        }
604    }
605
606    /// Sends a custom request to the client.
607    ///
608    /// # Initialization
609    ///
610    /// If the request is sent to the client before the server has been initialized, this will
611    /// immediately return `Err` with JSON-RPC error code `-32002` ([read more]).
612    ///
613    /// [read more]: https://microsoft.github.io/language-server-protocol/specification#initialize
614    ///
615    /// # Errors
616    ///
617    /// - The client is not yet initialized
618    /// - The client returns an error
619    pub async fn send_request<R>(&self, params: R::Params) -> jsonrpc::Result<R::Result>
620    where
621        R: request::Request,
622    {
623        if let State::Initialized | State::ShutDown = self.inner.state.get() {
624            self.send_request_unchecked::<R>(params).await
625        } else {
626            let id = i64::from(self.inner.request_id.load(Ordering::SeqCst)) + 1;
627            let msg = Request::from_request::<R>(id.into(), params);
628            trace!("server not initialized, supressing message: {}", msg);
629            Err(jsonrpc::not_initialized_error())
630        }
631    }
632
633    async fn send_request_unchecked<R>(&self, params: R::Params) -> jsonrpc::Result<R::Result>
634    where
635        R: request::Request,
636    {
637        let id = self.next_request_id();
638        let request = Request::from_request::<R>(id, params);
639
640        let Ok(Some(response)) = self.clone().call(request).await else {
641            return Err(Error::internal_error());
642        };
643
644        let (_, result) = response.into_parts();
645        result.and_then(|v| {
646            serde_json::from_value(v).map_err(|e| Error {
647                code: ErrorCode::ParseError,
648                message: e.to_string().into(),
649                data: None,
650            })
651        })
652    }
653}
654
655impl Client {
656    /// Increments the internal request ID counter and returns the previous value.
657    ///
658    /// This method can be used to build custom [`Request`] objects with numeric IDs that are
659    /// guaranteed to be unique every time.
660    #[must_use]
661    pub fn next_request_id(&self) -> Id {
662        let num = self.inner.request_id.fetch_add(1, Ordering::Relaxed);
663        Id::Number(i64::from(num))
664    }
665}
666
667impl Debug for Client {
668    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
669        f.debug_struct("Client")
670            .field("tx", &self.inner.tx)
671            .field("pending", &self.inner.pending)
672            .field("request_id", &self.inner.request_id)
673            .field("state", &self.inner.state)
674            .finish()
675    }
676}
677
678impl Service<Request> for Client {
679    type Response = Option<Response>;
680    type Error = ExitedError;
681    type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
682
683    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
684        self.inner
685            .tx
686            .clone()
687            .poll_ready(cx)
688            .map_err(|_| ExitedError(()))
689    }
690
691    fn call(&mut self, req: Request) -> Self::Future {
692        let mut tx = self.inner.tx.clone();
693        let response_waiter = req.id().cloned().map(|id| self.inner.pending.wait(id));
694
695        Box::pin(async move {
696            if tx.send(req).await.is_err() {
697                return Err(ExitedError(()));
698            }
699
700            match response_waiter {
701                Some(fut) => Ok(Some(fut.await)),
702                None => Ok(None),
703            }
704        })
705    }
706}
707
708#[cfg(test)]
709mod tests {
710    use std::future::Future;
711
712    use futures::stream::StreamExt;
713    use ls_types::notification::{LogMessage, PublishDiagnostics, ShowMessage, TelemetryEvent};
714    use serde_json::json;
715
716    use super::*;
717
718    async fn assert_client_message<F, Fut>(f: F, expected: Request)
719    where
720        F: FnOnce(Client) -> Fut,
721        Fut: Future,
722    {
723        let state = Arc::new(ServerState::new());
724        state.set(State::Initialized);
725
726        let (client, socket) = Client::new(state);
727        f(client).await;
728
729        let messages: Vec<_> = socket.collect().await;
730        assert_eq!(messages, vec![expected]);
731    }
732
733    #[tokio::test(flavor = "current_thread")]
734    async fn log_message() {
735        let (typ, msg) = (MessageType::LOG, "foo bar".to_owned());
736        let expected = Request::from_notification::<LogMessage>(LogMessageParams {
737            typ,
738            message: msg.clone(),
739        });
740
741        assert_client_message(|p| async move { p.log_message(typ, msg).await }, expected).await;
742    }
743
744    #[tokio::test(flavor = "current_thread")]
745    async fn show_message() {
746        let (typ, msg) = (MessageType::LOG, "foo bar".to_owned());
747        let expected = Request::from_notification::<ShowMessage>(ShowMessageParams {
748            typ,
749            message: msg.clone(),
750        });
751
752        assert_client_message(|p| async move { p.show_message(typ, msg).await }, expected).await;
753    }
754
755    #[tokio::test(flavor = "current_thread")]
756    async fn telemetry_event() {
757        let null = json!(null);
758        let value = OneOf::Right(vec![null.clone()]);
759        let expected = Request::from_notification::<TelemetryEvent>(value);
760        assert_client_message(|p| async move { p.telemetry_event(null).await }, expected).await;
761
762        let array = json!([1, 2, 3]);
763        let value = OneOf::Right(array.as_array().unwrap().to_owned());
764        let expected = Request::from_notification::<TelemetryEvent>(value);
765        assert_client_message(|p| async move { p.telemetry_event(array).await }, expected).await;
766
767        let object = json!({});
768        let value = OneOf::Left(object.as_object().unwrap().to_owned());
769        let expected = Request::from_notification::<TelemetryEvent>(value);
770        assert_client_message(|p| async move { p.telemetry_event(object).await }, expected).await;
771
772        let other = json!("hello");
773        let wrapped = LSPAny::Array(vec![other.clone()]);
774        let value = OneOf::Right(wrapped.as_array().unwrap().to_owned());
775        let expected = Request::from_notification::<TelemetryEvent>(value);
776        assert_client_message(|p| async move { p.telemetry_event(other).await }, expected).await;
777    }
778
779    #[tokio::test(flavor = "current_thread")]
780    async fn publish_diagnostics() {
781        let uri: Uri = "file:///path/to/file".parse().unwrap();
782        let diagnostics = vec![Diagnostic::new_simple(Range::default(), "example".into())];
783
784        let params = PublishDiagnosticsParams::new(uri.clone(), diagnostics.clone(), None);
785        let expected = Request::from_notification::<PublishDiagnostics>(params);
786
787        assert_client_message(
788            |p| async move { p.publish_diagnostics(uri, diagnostics, None).await },
789            expected,
790        )
791        .await;
792    }
793}