lsp.rs

  1pub use lsp_types::*;
  2
  3use anyhow::{anyhow, Context, Result};
  4use collections::HashMap;
  5use futures::{channel::oneshot, io::BufWriter, AsyncRead, AsyncWrite};
  6use gpui::{executor, AsyncAppContext, Task};
  7use parking_lot::Mutex;
  8use postage::{barrier, prelude::Stream};
  9use serde::{de::DeserializeOwned, Deserialize, Serialize};
 10use serde_json::{json, value::RawValue, Value};
 11use smol::{
 12    channel,
 13    io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader},
 14    process::{self, Child},
 15};
 16use std::{
 17    future::Future,
 18    io::Write,
 19    path::PathBuf,
 20    str::FromStr,
 21    sync::{
 22        atomic::{AtomicUsize, Ordering::SeqCst},
 23        Arc,
 24    },
 25};
 26use std::{path::Path, process::Stdio};
 27use util::{ResultExt, TryFutureExt};
 28
 29const JSON_RPC_VERSION: &'static str = "2.0";
 30const CONTENT_LEN_HEADER: &'static str = "Content-Length: ";
 31
 32type NotificationHandler = Box<dyn Send + FnMut(Option<usize>, &str, AsyncAppContext)>;
 33type ResponseHandler = Box<dyn Send + FnOnce(Result<&str, Error>)>;
 34
 35pub struct LanguageServer {
 36    server_id: usize,
 37    next_id: AtomicUsize,
 38    outbound_tx: channel::Sender<Vec<u8>>,
 39    name: String,
 40    capabilities: ServerCapabilities,
 41    notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
 42    response_handlers: Arc<Mutex<HashMap<usize, ResponseHandler>>>,
 43    executor: Arc<executor::Background>,
 44    io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,
 45    output_done_rx: Mutex<Option<barrier::Receiver>>,
 46    root_path: PathBuf,
 47    _server: Option<Child>,
 48}
 49
 50pub struct Subscription {
 51    method: &'static str,
 52    notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
 53}
 54
 55#[derive(Serialize, Deserialize)]
 56struct Request<'a, T> {
 57    jsonrpc: &'a str,
 58    id: usize,
 59    method: &'a str,
 60    params: T,
 61}
 62
 63#[derive(Serialize, Deserialize)]
 64struct AnyResponse<'a> {
 65    id: usize,
 66    #[serde(default)]
 67    error: Option<Error>,
 68    #[serde(borrow)]
 69    result: Option<&'a RawValue>,
 70}
 71
 72#[derive(Serialize)]
 73struct Response<T> {
 74    id: usize,
 75    result: Option<T>,
 76    error: Option<Error>,
 77}
 78
 79#[derive(Serialize, Deserialize)]
 80struct Notification<'a, T> {
 81    #[serde(borrow)]
 82    jsonrpc: &'a str,
 83    #[serde(borrow)]
 84    method: &'a str,
 85    params: T,
 86}
 87
 88#[derive(Deserialize)]
 89struct AnyNotification<'a> {
 90    #[serde(default)]
 91    id: Option<usize>,
 92    #[serde(borrow)]
 93    method: &'a str,
 94    #[serde(borrow)]
 95    params: &'a RawValue,
 96}
 97
 98#[derive(Debug, Serialize, Deserialize)]
 99struct Error {
100    message: String,
101}
102
103impl LanguageServer {
104    pub fn new(
105        server_id: usize,
106        binary_path: &Path,
107        args: &[&str],
108        root_path: &Path,
109        cx: AsyncAppContext,
110    ) -> Result<Self> {
111        let working_dir = if root_path.is_dir() {
112            root_path
113        } else {
114            root_path.parent().unwrap_or(Path::new("/"))
115        };
116        let mut server = process::Command::new(binary_path)
117            .current_dir(working_dir)
118            .args(args)
119            .stdin(Stdio::piped())
120            .stdout(Stdio::piped())
121            .stderr(Stdio::inherit())
122            .kill_on_drop(true)
123            .spawn()?;
124
125        let stdin = server.stdin.take().unwrap();
126        let stout = server.stdout.take().unwrap();
127
128        let mut server = Self::new_internal(
129            server_id,
130            stdin,
131            stout,
132            Some(server),
133            root_path,
134            cx,
135            |notification| {
136                log::info!(
137                    "unhandled notification {}:\n{}",
138                    notification.method,
139                    serde_json::to_string_pretty(
140                        &Value::from_str(notification.params.get()).unwrap()
141                    )
142                    .unwrap()
143                );
144            },
145        );
146        if let Some(name) = binary_path.file_name() {
147            server.name = name.to_string_lossy().to_string();
148        }
149        Ok(server)
150    }
151
152    fn new_internal<Stdin, Stdout, F>(
153        server_id: usize,
154        stdin: Stdin,
155        stdout: Stdout,
156        server: Option<Child>,
157        root_path: &Path,
158        cx: AsyncAppContext,
159        mut on_unhandled_notification: F,
160    ) -> Self
161    where
162        Stdin: AsyncWrite + Unpin + Send + 'static,
163        Stdout: AsyncRead + Unpin + Send + 'static,
164        F: FnMut(AnyNotification) + 'static + Send,
165    {
166        let mut stdin = BufWriter::new(stdin);
167        let mut stdout = BufReader::new(stdout);
168        let (outbound_tx, outbound_rx) = channel::unbounded::<Vec<u8>>();
169        let notification_handlers =
170            Arc::new(Mutex::new(HashMap::<_, NotificationHandler>::default()));
171        let response_handlers = Arc::new(Mutex::new(HashMap::<_, ResponseHandler>::default()));
172        let input_task = cx.spawn(|cx| {
173            let notification_handlers = notification_handlers.clone();
174            let response_handlers = response_handlers.clone();
175            async move {
176                let _clear_response_handlers = ClearResponseHandlers(response_handlers.clone());
177                let mut buffer = Vec::new();
178                loop {
179                    buffer.clear();
180                    stdout.read_until(b'\n', &mut buffer).await?;
181                    stdout.read_until(b'\n', &mut buffer).await?;
182                    let message_len: usize = std::str::from_utf8(&buffer)?
183                        .strip_prefix(CONTENT_LEN_HEADER)
184                        .ok_or_else(|| anyhow!("invalid header"))?
185                        .trim_end()
186                        .parse()?;
187
188                    buffer.resize(message_len, 0);
189                    stdout.read_exact(&mut buffer).await?;
190                    log::trace!("incoming message:{}", String::from_utf8_lossy(&buffer));
191
192                    if let Ok(msg) = serde_json::from_slice::<AnyNotification>(&buffer) {
193                        if let Some(handler) = notification_handlers.lock().get_mut(msg.method) {
194                            handler(msg.id, msg.params.get(), cx.clone());
195                        } else {
196                            on_unhandled_notification(msg);
197                        }
198                    } else if let Ok(AnyResponse { id, error, result }) =
199                        serde_json::from_slice(&buffer)
200                    {
201                        if let Some(handler) = response_handlers.lock().remove(&id) {
202                            if let Some(error) = error {
203                                handler(Err(error));
204                            } else if let Some(result) = result {
205                                handler(Ok(result.get()));
206                            } else {
207                                handler(Ok("null"));
208                            }
209                        }
210                    } else {
211                        return Err(anyhow!(
212                            "failed to deserialize message:\n{}",
213                            std::str::from_utf8(&buffer)?
214                        ));
215                    }
216
217                    // Don't starve the main thread when receiving lots of messages at once.
218                    smol::future::yield_now().await;
219                }
220            }
221            .log_err()
222        });
223        let (output_done_tx, output_done_rx) = barrier::channel();
224        let output_task = cx.background().spawn({
225            let response_handlers = response_handlers.clone();
226            async move {
227                let _clear_response_handlers = ClearResponseHandlers(response_handlers);
228                let mut content_len_buffer = Vec::new();
229                while let Ok(message) = outbound_rx.recv().await {
230                    log::trace!("outgoing message:{}", String::from_utf8_lossy(&message));
231                    content_len_buffer.clear();
232                    write!(content_len_buffer, "{}", message.len()).unwrap();
233                    stdin.write_all(CONTENT_LEN_HEADER.as_bytes()).await?;
234                    stdin.write_all(&content_len_buffer).await?;
235                    stdin.write_all("\r\n\r\n".as_bytes()).await?;
236                    stdin.write_all(&message).await?;
237                    stdin.flush().await?;
238                }
239                drop(output_done_tx);
240                Ok(())
241            }
242            .log_err()
243        });
244
245        Self {
246            server_id,
247            notification_handlers,
248            response_handlers,
249            name: Default::default(),
250            capabilities: Default::default(),
251            next_id: Default::default(),
252            outbound_tx,
253            executor: cx.background().clone(),
254            io_tasks: Mutex::new(Some((input_task, output_task))),
255            output_done_rx: Mutex::new(Some(output_done_rx)),
256            root_path: root_path.to_path_buf(),
257            _server: server,
258        }
259    }
260
261    pub async fn initialize(mut self, options: Option<Value>) -> Result<Arc<Self>> {
262        let root_uri = Url::from_file_path(&self.root_path).unwrap();
263        #[allow(deprecated)]
264        let params = InitializeParams {
265            process_id: Default::default(),
266            root_path: Default::default(),
267            root_uri: Some(root_uri.clone()),
268            initialization_options: options,
269            capabilities: ClientCapabilities {
270                workspace: Some(WorkspaceClientCapabilities {
271                    configuration: Some(true),
272                    did_change_configuration: Some(DynamicRegistrationClientCapabilities {
273                        dynamic_registration: Some(true),
274                    }),
275                    ..Default::default()
276                }),
277                text_document: Some(TextDocumentClientCapabilities {
278                    definition: Some(GotoCapability {
279                        link_support: Some(true),
280                        ..Default::default()
281                    }),
282                    code_action: Some(CodeActionClientCapabilities {
283                        code_action_literal_support: Some(CodeActionLiteralSupport {
284                            code_action_kind: CodeActionKindLiteralSupport {
285                                value_set: vec![
286                                    CodeActionKind::REFACTOR.as_str().into(),
287                                    CodeActionKind::QUICKFIX.as_str().into(),
288                                    CodeActionKind::SOURCE.as_str().into(),
289                                ],
290                            },
291                        }),
292                        data_support: Some(true),
293                        resolve_support: Some(CodeActionCapabilityResolveSupport {
294                            properties: vec!["edit".to_string(), "command".to_string()],
295                        }),
296                        ..Default::default()
297                    }),
298                    completion: Some(CompletionClientCapabilities {
299                        completion_item: Some(CompletionItemCapability {
300                            snippet_support: Some(true),
301                            resolve_support: Some(CompletionItemCapabilityResolveSupport {
302                                properties: vec!["additionalTextEdits".to_string()],
303                            }),
304                            ..Default::default()
305                        }),
306                        ..Default::default()
307                    }),
308                    rename: Some(RenameClientCapabilities {
309                        prepare_support: Some(true),
310                        ..Default::default()
311                    }),
312                    hover: Some(HoverClientCapabilities {
313                        content_format: Some(vec![MarkupKind::Markdown]),
314                        ..Default::default()
315                    }),
316                    ..Default::default()
317                }),
318                experimental: Some(json!({
319                    "serverStatusNotification": true,
320                })),
321                window: Some(WindowClientCapabilities {
322                    work_done_progress: Some(true),
323                    ..Default::default()
324                }),
325                ..Default::default()
326            },
327            trace: Default::default(),
328            workspace_folders: Some(vec![WorkspaceFolder {
329                uri: root_uri,
330                name: Default::default(),
331            }]),
332            client_info: Default::default(),
333            locale: Default::default(),
334        };
335
336        let response = self.request::<request::Initialize>(params).await?;
337        if let Some(info) = response.server_info {
338            self.name = info.name;
339        }
340        self.capabilities = response.capabilities;
341
342        self.notify::<notification::Initialized>(InitializedParams {})?;
343        Ok(Arc::new(self))
344    }
345
346    pub fn shutdown(&self) -> Option<impl 'static + Send + Future<Output = Option<()>>> {
347        if let Some(tasks) = self.io_tasks.lock().take() {
348            let response_handlers = self.response_handlers.clone();
349            let next_id = AtomicUsize::new(self.next_id.load(SeqCst));
350            let outbound_tx = self.outbound_tx.clone();
351            let mut output_done = self.output_done_rx.lock().take().unwrap();
352            let shutdown_request = Self::request_internal::<request::Shutdown>(
353                &next_id,
354                &response_handlers,
355                &outbound_tx,
356                (),
357            );
358            let exit = Self::notify_internal::<notification::Exit>(&outbound_tx, ());
359            outbound_tx.close();
360            Some(
361                async move {
362                    log::debug!("language server shutdown started");
363                    shutdown_request.await?;
364                    response_handlers.lock().clear();
365                    exit?;
366                    output_done.recv().await;
367                    log::debug!("language server shutdown finished");
368                    drop(tasks);
369                    Ok(())
370                }
371                .log_err(),
372            )
373        } else {
374            None
375        }
376    }
377
378    #[must_use]
379    pub fn on_notification<T, F>(&self, f: F) -> Subscription
380    where
381        T: notification::Notification,
382        F: 'static + Send + FnMut(T::Params, AsyncAppContext),
383    {
384        self.on_custom_notification(T::METHOD, f)
385    }
386
387    #[must_use]
388    pub fn on_request<T, F, Fut>(&self, f: F) -> Subscription
389    where
390        T: request::Request,
391        T::Params: 'static + Send,
392        F: 'static + Send + FnMut(T::Params, AsyncAppContext) -> Fut,
393        Fut: 'static + Future<Output = Result<T::Result>>,
394    {
395        self.on_custom_request(T::METHOD, f)
396    }
397
398    pub fn remove_request_handler<T: request::Request>(&self) {
399        self.notification_handlers.lock().remove(T::METHOD);
400    }
401
402    #[must_use]
403    pub fn on_custom_notification<Params, F>(&self, method: &'static str, mut f: F) -> Subscription
404    where
405        F: 'static + Send + FnMut(Params, AsyncAppContext),
406        Params: DeserializeOwned,
407    {
408        let prev_handler = self.notification_handlers.lock().insert(
409            method,
410            Box::new(move |_, params, cx| {
411                if let Some(params) = serde_json::from_str(params).log_err() {
412                    f(params, cx);
413                }
414            }),
415        );
416        assert!(
417            prev_handler.is_none(),
418            "registered multiple handlers for the same LSP method"
419        );
420        Subscription {
421            method,
422            notification_handlers: self.notification_handlers.clone(),
423        }
424    }
425
426    #[must_use]
427    pub fn on_custom_request<Params, Res, Fut, F>(
428        &self,
429        method: &'static str,
430        mut f: F,
431    ) -> Subscription
432    where
433        F: 'static + Send + FnMut(Params, AsyncAppContext) -> Fut,
434        Fut: 'static + Future<Output = Result<Res>>,
435        Params: DeserializeOwned + Send + 'static,
436        Res: Serialize,
437    {
438        let outbound_tx = self.outbound_tx.clone();
439        let prev_handler = self.notification_handlers.lock().insert(
440            method,
441            Box::new(move |id, params, cx| {
442                if let Some(id) = id {
443                    if let Some(params) = serde_json::from_str(params).log_err() {
444                        let response = f(params, cx.clone());
445                        cx.foreground()
446                            .spawn({
447                                let outbound_tx = outbound_tx.clone();
448                                async move {
449                                    let response = match response.await {
450                                        Ok(result) => Response {
451                                            id,
452                                            result: Some(result),
453                                            error: None,
454                                        },
455                                        Err(error) => Response {
456                                            id,
457                                            result: None,
458                                            error: Some(Error {
459                                                message: error.to_string(),
460                                            }),
461                                        },
462                                    };
463                                    if let Some(response) = serde_json::to_vec(&response).log_err()
464                                    {
465                                        outbound_tx.try_send(response).ok();
466                                    }
467                                }
468                            })
469                            .detach();
470                    }
471                }
472            }),
473        );
474        assert!(
475            prev_handler.is_none(),
476            "registered multiple handlers for the same LSP method"
477        );
478        Subscription {
479            method,
480            notification_handlers: self.notification_handlers.clone(),
481        }
482    }
483
484    pub fn name<'a>(self: &'a Arc<Self>) -> &'a str {
485        &self.name
486    }
487
488    pub fn capabilities<'a>(self: &'a Arc<Self>) -> &'a ServerCapabilities {
489        &self.capabilities
490    }
491
492    pub fn server_id(&self) -> usize {
493        self.server_id
494    }
495
496    pub fn root_path(&self) -> &PathBuf {
497        &self.root_path
498    }
499
500    pub fn request<T: request::Request>(
501        &self,
502        params: T::Params,
503    ) -> impl Future<Output = Result<T::Result>>
504    where
505        T::Result: 'static + Send,
506    {
507        Self::request_internal::<T>(
508            &self.next_id,
509            &self.response_handlers,
510            &self.outbound_tx,
511            params,
512        )
513    }
514
515    fn request_internal<T: request::Request>(
516        next_id: &AtomicUsize,
517        response_handlers: &Mutex<HashMap<usize, ResponseHandler>>,
518        outbound_tx: &channel::Sender<Vec<u8>>,
519        params: T::Params,
520    ) -> impl 'static + Future<Output = Result<T::Result>>
521    where
522        T::Result: 'static + Send,
523    {
524        let id = next_id.fetch_add(1, SeqCst);
525        let message = serde_json::to_vec(&Request {
526            jsonrpc: JSON_RPC_VERSION,
527            id,
528            method: T::METHOD,
529            params,
530        })
531        .unwrap();
532
533        let send = outbound_tx
534            .try_send(message)
535            .context("failed to write to language server's stdin");
536
537        let (tx, rx) = oneshot::channel();
538        response_handlers.lock().insert(
539            id,
540            Box::new(move |result| {
541                let response = match result {
542                    Ok(response) => {
543                        serde_json::from_str(response).context("failed to deserialize response")
544                    }
545                    Err(error) => Err(anyhow!("{}", error.message)),
546                };
547                let _ = tx.send(response);
548            }),
549        );
550
551        async move {
552            send?;
553            rx.await?
554        }
555    }
556
557    pub fn notify<T: notification::Notification>(&self, params: T::Params) -> Result<()> {
558        Self::notify_internal::<T>(&self.outbound_tx, params)
559    }
560
561    fn notify_internal<T: notification::Notification>(
562        outbound_tx: &channel::Sender<Vec<u8>>,
563        params: T::Params,
564    ) -> Result<()> {
565        let message = serde_json::to_vec(&Notification {
566            jsonrpc: JSON_RPC_VERSION,
567            method: T::METHOD,
568            params,
569        })
570        .unwrap();
571        outbound_tx.try_send(message)?;
572        Ok(())
573    }
574}
575
576impl Drop for LanguageServer {
577    fn drop(&mut self) {
578        if let Some(shutdown) = self.shutdown() {
579            self.executor.spawn(shutdown).detach();
580        }
581    }
582}
583
584impl Subscription {
585    pub fn detach(mut self) {
586        self.method = "";
587    }
588}
589
590impl Drop for Subscription {
591    fn drop(&mut self) {
592        self.notification_handlers.lock().remove(self.method);
593    }
594}
595
596#[cfg(any(test, feature = "test-support"))]
597#[derive(Clone)]
598pub struct FakeLanguageServer {
599    pub server: Arc<LanguageServer>,
600    notifications_rx: channel::Receiver<(String, String)>,
601}
602
603#[cfg(any(test, feature = "test-support"))]
604impl LanguageServer {
605    pub fn full_capabilities() -> ServerCapabilities {
606        ServerCapabilities {
607            document_highlight_provider: Some(OneOf::Left(true)),
608            code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
609            document_formatting_provider: Some(OneOf::Left(true)),
610            document_range_formatting_provider: Some(OneOf::Left(true)),
611            ..Default::default()
612        }
613    }
614
615    pub fn fake(
616        name: String,
617        capabilities: ServerCapabilities,
618        cx: AsyncAppContext,
619    ) -> (Self, FakeLanguageServer) {
620        let (stdin_writer, stdin_reader) = async_pipe::pipe();
621        let (stdout_writer, stdout_reader) = async_pipe::pipe();
622        let (notifications_tx, notifications_rx) = channel::unbounded();
623
624        let server = Self::new_internal(
625            0,
626            stdin_writer,
627            stdout_reader,
628            None,
629            Path::new("/"),
630            cx.clone(),
631            |_| {},
632        );
633        let fake = FakeLanguageServer {
634            server: Arc::new(Self::new_internal(
635                0,
636                stdout_writer,
637                stdin_reader,
638                None,
639                Path::new("/"),
640                cx.clone(),
641                move |msg| {
642                    notifications_tx
643                        .try_send((msg.method.to_string(), msg.params.get().to_string()))
644                        .ok();
645                },
646            )),
647            notifications_rx,
648        };
649        fake.handle_request::<request::Initialize, _, _>({
650            let capabilities = capabilities.clone();
651            move |_, _| {
652                let capabilities = capabilities.clone();
653                let name = name.clone();
654                async move {
655                    Ok(InitializeResult {
656                        capabilities,
657                        server_info: Some(ServerInfo {
658                            name,
659                            ..Default::default()
660                        }),
661                        ..Default::default()
662                    })
663                }
664            }
665        });
666
667        (server, fake)
668    }
669}
670
671#[cfg(any(test, feature = "test-support"))]
672impl FakeLanguageServer {
673    pub fn notify<T: notification::Notification>(&self, params: T::Params) {
674        self.server.notify::<T>(params).ok();
675    }
676
677    pub async fn request<T>(&self, params: T::Params) -> Result<T::Result>
678    where
679        T: request::Request,
680        T::Result: 'static + Send,
681    {
682        self.server.request::<T>(params).await
683    }
684
685    pub async fn receive_notification<T: notification::Notification>(&mut self) -> T::Params {
686        self.try_receive_notification::<T>().await.unwrap()
687    }
688
689    pub async fn try_receive_notification<T: notification::Notification>(
690        &mut self,
691    ) -> Option<T::Params> {
692        use futures::StreamExt as _;
693
694        loop {
695            let (method, params) = self.notifications_rx.next().await?;
696            if &method == T::METHOD {
697                return Some(serde_json::from_str::<T::Params>(&params).unwrap());
698            } else {
699                log::info!("skipping message in fake language server {:?}", params);
700            }
701        }
702    }
703
704    pub fn handle_request<T, F, Fut>(
705        &self,
706        mut handler: F,
707    ) -> futures::channel::mpsc::UnboundedReceiver<()>
708    where
709        T: 'static + request::Request,
710        T::Params: 'static + Send,
711        F: 'static + Send + FnMut(T::Params, gpui::AsyncAppContext) -> Fut,
712        Fut: 'static + Send + Future<Output = Result<T::Result>>,
713    {
714        let (responded_tx, responded_rx) = futures::channel::mpsc::unbounded();
715        self.server.remove_request_handler::<T>();
716        self.server
717            .on_request::<T, _, _>(move |params, cx| {
718                let result = handler(params, cx.clone());
719                let responded_tx = responded_tx.clone();
720                async move {
721                    cx.background().simulate_random_delay().await;
722                    let result = result.await;
723                    responded_tx.unbounded_send(()).ok();
724                    result
725                }
726            })
727            .detach();
728        responded_rx
729    }
730
731    pub fn remove_request_handler<T>(&mut self)
732    where
733        T: 'static + request::Request,
734    {
735        self.server.remove_request_handler::<T>();
736    }
737
738    pub async fn start_progress(&self, token: impl Into<String>) {
739        let token = token.into();
740        self.request::<request::WorkDoneProgressCreate>(WorkDoneProgressCreateParams {
741            token: NumberOrString::String(token.clone()),
742        })
743        .await
744        .unwrap();
745        self.notify::<notification::Progress>(ProgressParams {
746            token: NumberOrString::String(token),
747            value: ProgressParamsValue::WorkDone(WorkDoneProgress::Begin(Default::default())),
748        });
749    }
750
751    pub fn end_progress(&self, token: impl Into<String>) {
752        self.notify::<notification::Progress>(ProgressParams {
753            token: NumberOrString::String(token.into()),
754            value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(Default::default())),
755        });
756    }
757}
758
759struct ClearResponseHandlers(Arc<Mutex<HashMap<usize, ResponseHandler>>>);
760
761impl Drop for ClearResponseHandlers {
762    fn drop(&mut self) {
763        self.0.lock().clear();
764    }
765}
766
767#[cfg(test)]
768mod tests {
769    use super::*;
770    use gpui::TestAppContext;
771
772    #[ctor::ctor]
773    fn init_logger() {
774        if std::env::var("RUST_LOG").is_ok() {
775            env_logger::init();
776        }
777    }
778
779    #[gpui::test]
780    async fn test_fake(cx: &mut TestAppContext) {
781        let (server, mut fake) =
782            LanguageServer::fake("the-lsp".to_string(), Default::default(), cx.to_async());
783
784        let (message_tx, message_rx) = channel::unbounded();
785        let (diagnostics_tx, diagnostics_rx) = channel::unbounded();
786        server
787            .on_notification::<notification::ShowMessage, _>(move |params, _| {
788                message_tx.try_send(params).unwrap()
789            })
790            .detach();
791        server
792            .on_notification::<notification::PublishDiagnostics, _>(move |params, _| {
793                diagnostics_tx.try_send(params).unwrap()
794            })
795            .detach();
796
797        let server = server.initialize(None).await.unwrap();
798        server
799            .notify::<notification::DidOpenTextDocument>(DidOpenTextDocumentParams {
800                text_document: TextDocumentItem::new(
801                    Url::from_str("file://a/b").unwrap(),
802                    "rust".to_string(),
803                    0,
804                    "".to_string(),
805                ),
806            })
807            .unwrap();
808        assert_eq!(
809            fake.receive_notification::<notification::DidOpenTextDocument>()
810                .await
811                .text_document
812                .uri
813                .as_str(),
814            "file://a/b"
815        );
816
817        fake.notify::<notification::ShowMessage>(ShowMessageParams {
818            typ: MessageType::ERROR,
819            message: "ok".to_string(),
820        });
821        fake.notify::<notification::PublishDiagnostics>(PublishDiagnosticsParams {
822            uri: Url::from_str("file://b/c").unwrap(),
823            version: Some(5),
824            diagnostics: vec![],
825        });
826        assert_eq!(message_rx.recv().await.unwrap().message, "ok");
827        assert_eq!(
828            diagnostics_rx.recv().await.unwrap().uri.as_str(),
829            "file://b/c"
830        );
831
832        fake.handle_request::<request::Shutdown, _, _>(|_, _| async move { Ok(()) });
833
834        drop(server);
835        fake.receive_notification::<notification::Exit>().await;
836    }
837}