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