1mod input_handler;
2
3pub use lsp_types::request::*;
4pub use lsp_types::*;
5
6use anyhow::{anyhow, Context, Result};
7use collections::HashMap;
8use futures::{channel::oneshot, io::BufWriter, select, AsyncRead, AsyncWrite, Future, FutureExt};
9use gpui::{AppContext, AsyncAppContext, BackgroundExecutor, Task};
10use parking_lot::{Mutex, RwLock};
11use postage::{barrier, prelude::Stream};
12use serde::{de::DeserializeOwned, Deserialize, Serialize};
13use serde_json::{json, value::RawValue, Value};
14use smol::{
15 channel,
16 io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
17 process::{self, Child},
18};
19
20#[cfg(target_os = "windows")]
21use smol::process::windows::CommandExt;
22
23use std::{
24 ffi::OsString,
25 fmt,
26 io::Write,
27 ops::DerefMut,
28 path::PathBuf,
29 pin::Pin,
30 sync::{
31 atomic::{AtomicI32, Ordering::SeqCst},
32 Arc, Weak,
33 },
34 task::Poll,
35 time::{Duration, Instant},
36};
37use std::{path::Path, process::Stdio};
38use util::{ResultExt, TryFutureExt};
39
40const JSON_RPC_VERSION: &str = "2.0";
41const CONTENT_LEN_HEADER: &str = "Content-Length: ";
42
43const LSP_REQUEST_TIMEOUT: Duration = Duration::from_secs(60 * 2);
44const SERVER_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);
45
46type NotificationHandler = Box<dyn Send + FnMut(Option<RequestId>, Value, AsyncAppContext)>;
47type ResponseHandler = Box<dyn Send + FnOnce(Result<String, Error>)>;
48type IoHandler = Box<dyn Send + FnMut(IoKind, &str)>;
49
50/// Kind of language server stdio given to an IO handler.
51#[derive(Debug, Clone, Copy)]
52pub enum IoKind {
53 StdOut,
54 StdIn,
55 StdErr,
56}
57
58/// Represents a launchable language server. This can either be a standalone binary or the path
59/// to a runtime with arguments to instruct it to launch the actual language server file.
60#[derive(Debug, Clone, Deserialize)]
61pub struct LanguageServerBinary {
62 pub path: PathBuf,
63 pub arguments: Vec<OsString>,
64 pub env: Option<HashMap<String, String>>,
65}
66
67/// Configures the search (and installation) of language servers.
68#[derive(Debug, Clone, Deserialize)]
69pub struct LanguageServerBinaryOptions {
70 /// Whether the adapter should look at the users system
71 pub allow_path_lookup: bool,
72 /// Whether the adapter should download its own version
73 pub allow_binary_download: bool,
74}
75
76/// A running language server process.
77pub struct LanguageServer {
78 server_id: LanguageServerId,
79 next_id: AtomicI32,
80 outbound_tx: channel::Sender<String>,
81 name: Arc<str>,
82 capabilities: RwLock<ServerCapabilities>,
83 code_action_kinds: Option<Vec<CodeActionKind>>,
84 notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
85 response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
86 io_handlers: Arc<Mutex<HashMap<i32, IoHandler>>>,
87 executor: BackgroundExecutor,
88 #[allow(clippy::type_complexity)]
89 io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,
90 output_done_rx: Mutex<Option<barrier::Receiver>>,
91 root_path: PathBuf,
92 working_dir: PathBuf,
93 server: Arc<Mutex<Option<Child>>>,
94}
95
96/// Identifies a running language server.
97#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
98#[repr(transparent)]
99pub struct LanguageServerId(pub usize);
100
101impl LanguageServerId {
102 pub fn from_proto(id: u64) -> Self {
103 Self(id as usize)
104 }
105
106 pub fn to_proto(self) -> u64 {
107 self.0 as u64
108 }
109}
110
111/// Handle to a language server RPC activity subscription.
112pub enum Subscription {
113 Notification {
114 method: &'static str,
115 notification_handlers: Option<Arc<Mutex<HashMap<&'static str, NotificationHandler>>>>,
116 },
117 Io {
118 id: i32,
119 io_handlers: Option<Weak<Mutex<HashMap<i32, IoHandler>>>>,
120 },
121}
122
123/// Language server protocol RPC request message ID.
124///
125/// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage)
126#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
127#[serde(untagged)]
128pub enum RequestId {
129 Int(i32),
130 Str(String),
131}
132
133/// Language server protocol RPC request message.
134///
135/// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage)
136#[derive(Serialize, Deserialize)]
137pub struct Request<'a, T> {
138 jsonrpc: &'static str,
139 id: RequestId,
140 method: &'a str,
141 params: T,
142}
143
144/// Language server protocol RPC request response message before it is deserialized into a concrete type.
145#[derive(Serialize, Deserialize)]
146struct AnyResponse<'a> {
147 jsonrpc: &'a str,
148 id: RequestId,
149 #[serde(default)]
150 error: Option<Error>,
151 #[serde(borrow)]
152 result: Option<&'a RawValue>,
153}
154
155/// Language server protocol RPC request response message.
156///
157/// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#responseMessage)
158#[derive(Serialize)]
159struct Response<T> {
160 jsonrpc: &'static str,
161 id: RequestId,
162 #[serde(flatten)]
163 value: LspResult<T>,
164}
165
166#[derive(Serialize)]
167#[serde(rename_all = "snake_case")]
168enum LspResult<T> {
169 #[serde(rename = "result")]
170 Ok(Option<T>),
171 Error(Option<Error>),
172}
173
174/// Language server protocol RPC notification message.
175///
176/// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#notificationMessage)
177#[derive(Serialize, Deserialize)]
178struct Notification<'a, T> {
179 jsonrpc: &'static str,
180 #[serde(borrow)]
181 method: &'a str,
182 params: T,
183}
184
185/// Language server RPC notification message before it is deserialized into a concrete type.
186#[derive(Debug, Clone, Deserialize)]
187struct AnyNotification {
188 #[serde(default)]
189 id: Option<RequestId>,
190 method: String,
191 #[serde(default)]
192 params: Option<Value>,
193}
194
195#[derive(Debug, Serialize, Deserialize)]
196struct Error {
197 message: String,
198}
199
200pub trait LspRequestFuture<O>: Future<Output = O> {
201 fn id(&self) -> i32;
202}
203
204struct LspRequest<F> {
205 id: i32,
206 request: F,
207}
208
209impl<F> LspRequest<F> {
210 pub fn new(id: i32, request: F) -> Self {
211 Self { id, request }
212 }
213}
214
215impl<F: Future> Future for LspRequest<F> {
216 type Output = F::Output;
217
218 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
219 // SAFETY: This is standard pin projection, we're pinned so our fields must be pinned.
220 let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().request) };
221 inner.poll(cx)
222 }
223}
224
225impl<F: Future> LspRequestFuture<F::Output> for LspRequest<F> {
226 fn id(&self) -> i32 {
227 self.id
228 }
229}
230
231/// Combined capabilities of the server and the adapter.
232pub struct AdapterServerCapabilities {
233 // Reported capabilities by the server
234 pub server_capabilities: ServerCapabilities,
235 // List of code actions supported by the LspAdapter matching the server
236 pub code_action_kinds: Option<Vec<CodeActionKind>>,
237}
238
239/// Experimental: Informs the end user about the state of the server
240///
241/// [Rust Analyzer Specification](https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/lsp-extensions.md#server-status)
242#[derive(Debug)]
243pub enum ServerStatus {}
244
245/// Other(String) variant to handle unknown values due to this still being experimental
246#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
247#[serde(rename_all = "camelCase")]
248pub enum ServerHealthStatus {
249 Ok,
250 Warning,
251 Error,
252 Other(String),
253}
254
255#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
256#[serde(rename_all = "camelCase")]
257pub struct ServerStatusParams {
258 pub health: ServerHealthStatus,
259 pub message: Option<String>,
260}
261
262impl lsp_types::notification::Notification for ServerStatus {
263 type Params = ServerStatusParams;
264 const METHOD: &'static str = "experimental/serverStatus";
265}
266
267impl LanguageServer {
268 /// Starts a language server process.
269 pub fn new(
270 stderr_capture: Arc<Mutex<Option<String>>>,
271 server_id: LanguageServerId,
272 binary: LanguageServerBinary,
273 root_path: &Path,
274 code_action_kinds: Option<Vec<CodeActionKind>>,
275 cx: AsyncAppContext,
276 ) -> Result<Self> {
277 let working_dir = if root_path.is_dir() {
278 root_path
279 } else {
280 root_path.parent().unwrap_or_else(|| Path::new("/"))
281 };
282
283 log::info!(
284 "starting language server process. binary path: {:?}, working directory: {:?}, args: {:?}",
285 binary.path,
286 working_dir,
287 &binary.arguments
288 );
289
290 let mut command = process::Command::new(&binary.path);
291 command
292 .current_dir(working_dir)
293 .args(&binary.arguments)
294 .envs(binary.env.unwrap_or_default())
295 .stdin(Stdio::piped())
296 .stdout(Stdio::piped())
297 .stderr(Stdio::piped())
298 .kill_on_drop(true);
299 #[cfg(windows)]
300 command.creation_flags(windows::Win32::System::Threading::CREATE_NO_WINDOW.0);
301 let mut server = command.spawn().with_context(|| {
302 format!(
303 "failed to spawn command. path: {:?}, working directory: {:?}, args: {:?}",
304 binary.path, working_dir, &binary.arguments
305 )
306 })?;
307
308 let stdin = server.stdin.take().unwrap();
309 let stdout = server.stdout.take().unwrap();
310 let stderr = server.stderr.take().unwrap();
311 let mut server = Self::new_internal(
312 server_id,
313 stdin,
314 stdout,
315 Some(stderr),
316 stderr_capture,
317 Some(server),
318 root_path,
319 working_dir,
320 code_action_kinds,
321 cx,
322 move |notification| {
323 log::info!(
324 "Language server with id {} sent unhandled notification {}:\n{}",
325 server_id,
326 notification.method,
327 serde_json::to_string_pretty(¬ification.params).unwrap(),
328 );
329 },
330 );
331
332 if let Some(name) = binary.path.file_name() {
333 server.name = name.to_string_lossy().into();
334 }
335
336 Ok(server)
337 }
338
339 #[allow(clippy::too_many_arguments)]
340 fn new_internal<Stdin, Stdout, Stderr, F>(
341 server_id: LanguageServerId,
342 stdin: Stdin,
343 stdout: Stdout,
344 stderr: Option<Stderr>,
345 stderr_capture: Arc<Mutex<Option<String>>>,
346 server: Option<Child>,
347 root_path: &Path,
348 working_dir: &Path,
349 code_action_kinds: Option<Vec<CodeActionKind>>,
350 cx: AsyncAppContext,
351 on_unhandled_notification: F,
352 ) -> Self
353 where
354 Stdin: AsyncWrite + Unpin + Send + 'static,
355 Stdout: AsyncRead + Unpin + Send + 'static,
356 Stderr: AsyncRead + Unpin + Send + 'static,
357 F: FnMut(AnyNotification) + 'static + Send + Sync + Clone,
358 {
359 let (outbound_tx, outbound_rx) = channel::unbounded::<String>();
360 let (output_done_tx, output_done_rx) = barrier::channel();
361 let notification_handlers =
362 Arc::new(Mutex::new(HashMap::<_, NotificationHandler>::default()));
363 let response_handlers =
364 Arc::new(Mutex::new(Some(HashMap::<_, ResponseHandler>::default())));
365 let io_handlers = Arc::new(Mutex::new(HashMap::default()));
366
367 let stdout_input_task = cx.spawn({
368 let on_unhandled_notification = on_unhandled_notification.clone();
369 let notification_handlers = notification_handlers.clone();
370 let response_handlers = response_handlers.clone();
371 let io_handlers = io_handlers.clone();
372 move |cx| {
373 Self::handle_input(
374 stdout,
375 on_unhandled_notification,
376 notification_handlers,
377 response_handlers,
378 io_handlers,
379 cx,
380 )
381 .log_err()
382 }
383 });
384 let stderr_input_task = stderr
385 .map(|stderr| {
386 let io_handlers = io_handlers.clone();
387 let stderr_captures = stderr_capture.clone();
388 cx.spawn(|_| Self::handle_stderr(stderr, io_handlers, stderr_captures).log_err())
389 })
390 .unwrap_or_else(|| Task::Ready(Some(None)));
391 let input_task = cx.spawn(|_| async move {
392 let (stdout, stderr) = futures::join!(stdout_input_task, stderr_input_task);
393 stdout.or(stderr)
394 });
395 let output_task = cx.background_executor().spawn({
396 Self::handle_output(
397 stdin,
398 outbound_rx,
399 output_done_tx,
400 response_handlers.clone(),
401 io_handlers.clone(),
402 )
403 .log_err()
404 });
405
406 Self {
407 server_id,
408 notification_handlers,
409 response_handlers,
410 io_handlers,
411 name: Arc::default(),
412 capabilities: Default::default(),
413 code_action_kinds,
414 next_id: Default::default(),
415 outbound_tx,
416 executor: cx.background_executor().clone(),
417 io_tasks: Mutex::new(Some((input_task, output_task))),
418 output_done_rx: Mutex::new(Some(output_done_rx)),
419 root_path: root_path.to_path_buf(),
420 working_dir: working_dir.to_path_buf(),
421 server: Arc::new(Mutex::new(server)),
422 }
423 }
424
425 /// List of code action kinds this language server reports being able to emit.
426 pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
427 self.code_action_kinds.clone()
428 }
429
430 async fn handle_input<Stdout, F>(
431 stdout: Stdout,
432 mut on_unhandled_notification: F,
433 notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
434 response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
435 io_handlers: Arc<Mutex<HashMap<i32, IoHandler>>>,
436 cx: AsyncAppContext,
437 ) -> anyhow::Result<()>
438 where
439 Stdout: AsyncRead + Unpin + Send + 'static,
440 F: FnMut(AnyNotification) + 'static + Send,
441 {
442 use smol::stream::StreamExt;
443 let stdout = BufReader::new(stdout);
444 let _clear_response_handlers = util::defer({
445 let response_handlers = response_handlers.clone();
446 move || {
447 response_handlers.lock().take();
448 }
449 });
450 let mut input_handler = input_handler::LspStdoutHandler::new(
451 stdout,
452 response_handlers,
453 io_handlers,
454 cx.background_executor().clone(),
455 );
456
457 while let Some(msg) = input_handler.notifications_channel.next().await {
458 {
459 let mut notification_handlers = notification_handlers.lock();
460 if let Some(handler) = notification_handlers.get_mut(msg.method.as_str()) {
461 handler(msg.id, msg.params.unwrap_or(Value::Null), cx.clone());
462 } else {
463 drop(notification_handlers);
464 on_unhandled_notification(msg);
465 }
466 }
467
468 // Don't starve the main thread when receiving lots of notifications at once.
469 smol::future::yield_now().await;
470 }
471 input_handler.loop_handle.await
472 }
473
474 async fn handle_stderr<Stderr>(
475 stderr: Stderr,
476 io_handlers: Arc<Mutex<HashMap<i32, IoHandler>>>,
477 stderr_capture: Arc<Mutex<Option<String>>>,
478 ) -> anyhow::Result<()>
479 where
480 Stderr: AsyncRead + Unpin + Send + 'static,
481 {
482 let mut stderr = BufReader::new(stderr);
483 let mut buffer = Vec::new();
484
485 loop {
486 buffer.clear();
487
488 let bytes_read = stderr.read_until(b'\n', &mut buffer).await?;
489 if bytes_read == 0 {
490 return Ok(());
491 }
492
493 if let Ok(message) = std::str::from_utf8(&buffer) {
494 log::trace!("incoming stderr message:{message}");
495 for handler in io_handlers.lock().values_mut() {
496 handler(IoKind::StdErr, message);
497 }
498
499 if let Some(stderr) = stderr_capture.lock().as_mut() {
500 stderr.push_str(message);
501 }
502 }
503
504 // Don't starve the main thread when receiving lots of messages at once.
505 smol::future::yield_now().await;
506 }
507 }
508
509 async fn handle_output<Stdin>(
510 stdin: Stdin,
511 outbound_rx: channel::Receiver<String>,
512 output_done_tx: barrier::Sender,
513 response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
514 io_handlers: Arc<Mutex<HashMap<i32, IoHandler>>>,
515 ) -> anyhow::Result<()>
516 where
517 Stdin: AsyncWrite + Unpin + Send + 'static,
518 {
519 let mut stdin = BufWriter::new(stdin);
520 let _clear_response_handlers = util::defer({
521 let response_handlers = response_handlers.clone();
522 move || {
523 response_handlers.lock().take();
524 }
525 });
526 let mut content_len_buffer = Vec::new();
527 while let Ok(message) = outbound_rx.recv().await {
528 log::trace!("outgoing message:{}", message);
529 for handler in io_handlers.lock().values_mut() {
530 handler(IoKind::StdIn, &message);
531 }
532
533 content_len_buffer.clear();
534 write!(content_len_buffer, "{}", message.len()).unwrap();
535 stdin.write_all(CONTENT_LEN_HEADER.as_bytes()).await?;
536 stdin.write_all(&content_len_buffer).await?;
537 stdin.write_all("\r\n\r\n".as_bytes()).await?;
538 stdin.write_all(message.as_bytes()).await?;
539 stdin.flush().await?;
540 }
541 drop(output_done_tx);
542 Ok(())
543 }
544
545 /// Initializes a language server by sending the `Initialize` request.
546 /// Note that `options` is used directly to construct [`InitializeParams`], which is why it is owned.
547 ///
548 /// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize)
549 pub fn initialize(
550 mut self,
551 options: Option<Value>,
552 cx: &AppContext,
553 ) -> Task<Result<Arc<Self>>> {
554 let root_uri = Url::from_file_path(&self.working_dir).unwrap();
555 #[allow(deprecated)]
556 let params = InitializeParams {
557 process_id: None,
558 root_path: None,
559 root_uri: Some(root_uri.clone()),
560 initialization_options: options,
561 capabilities: ClientCapabilities {
562 workspace: Some(WorkspaceClientCapabilities {
563 configuration: Some(true),
564 did_change_watched_files: Some(DidChangeWatchedFilesClientCapabilities {
565 dynamic_registration: Some(true),
566 relative_pattern_support: Some(true),
567 }),
568 did_change_configuration: Some(DynamicRegistrationClientCapabilities {
569 dynamic_registration: Some(true),
570 }),
571 workspace_folders: Some(true),
572 symbol: Some(WorkspaceSymbolClientCapabilities {
573 resolve_support: None,
574 ..WorkspaceSymbolClientCapabilities::default()
575 }),
576 inlay_hint: Some(InlayHintWorkspaceClientCapabilities {
577 refresh_support: Some(true),
578 }),
579 diagnostic: Some(DiagnosticWorkspaceClientCapabilities {
580 refresh_support: None,
581 }),
582 workspace_edit: Some(WorkspaceEditClientCapabilities {
583 resource_operations: Some(vec![
584 ResourceOperationKind::Create,
585 ResourceOperationKind::Rename,
586 ResourceOperationKind::Delete,
587 ]),
588 document_changes: Some(true),
589 snippet_edit_support: Some(true),
590 ..WorkspaceEditClientCapabilities::default()
591 }),
592 ..Default::default()
593 }),
594 text_document: Some(TextDocumentClientCapabilities {
595 definition: Some(GotoCapability {
596 link_support: Some(true),
597 dynamic_registration: None,
598 }),
599 code_action: Some(CodeActionClientCapabilities {
600 code_action_literal_support: Some(CodeActionLiteralSupport {
601 code_action_kind: CodeActionKindLiteralSupport {
602 value_set: vec![
603 CodeActionKind::REFACTOR.as_str().into(),
604 CodeActionKind::QUICKFIX.as_str().into(),
605 CodeActionKind::SOURCE.as_str().into(),
606 ],
607 },
608 }),
609 data_support: Some(true),
610 resolve_support: Some(CodeActionCapabilityResolveSupport {
611 properties: vec![
612 "kind".to_string(),
613 "diagnostics".to_string(),
614 "isPreferred".to_string(),
615 "disabled".to_string(),
616 "edit".to_string(),
617 "command".to_string(),
618 ],
619 }),
620 ..Default::default()
621 }),
622 completion: Some(CompletionClientCapabilities {
623 completion_item: Some(CompletionItemCapability {
624 snippet_support: Some(true),
625 resolve_support: Some(CompletionItemCapabilityResolveSupport {
626 properties: vec![
627 "additionalTextEdits".to_string(),
628 "command".to_string(),
629 "detail".to_string(),
630 "documentation".to_string(),
631 "filterText".to_string(),
632 "labelDetails".to_string(),
633 "tags".to_string(),
634 // NB: Do not have this resolved, otherwise Zed becomes slow to complete things
635 // "textEdit".to_string(),
636 ],
637 }),
638 insert_replace_support: Some(true),
639 label_details_support: Some(true),
640 ..Default::default()
641 }),
642 completion_list: Some(CompletionListCapability {
643 item_defaults: Some(vec![
644 "commitCharacters".to_owned(),
645 "editRange".to_owned(),
646 "insertTextMode".to_owned(),
647 "data".to_owned(),
648 ]),
649 }),
650 context_support: Some(true),
651 ..Default::default()
652 }),
653 rename: Some(RenameClientCapabilities {
654 prepare_support: Some(true),
655 ..Default::default()
656 }),
657 hover: Some(HoverClientCapabilities {
658 content_format: Some(vec![MarkupKind::Markdown]),
659 dynamic_registration: None,
660 }),
661 inlay_hint: Some(InlayHintClientCapabilities {
662 resolve_support: Some(InlayHintResolveClientCapabilities {
663 properties: vec![
664 "textEdits".to_string(),
665 "tooltip".to_string(),
666 "label.tooltip".to_string(),
667 "label.location".to_string(),
668 "label.command".to_string(),
669 ],
670 }),
671 dynamic_registration: Some(false),
672 }),
673 publish_diagnostics: Some(PublishDiagnosticsClientCapabilities {
674 related_information: Some(true),
675 ..Default::default()
676 }),
677 formatting: Some(DynamicRegistrationClientCapabilities {
678 dynamic_registration: Some(true),
679 }),
680 range_formatting: Some(DynamicRegistrationClientCapabilities {
681 dynamic_registration: Some(true),
682 }),
683 on_type_formatting: Some(DynamicRegistrationClientCapabilities {
684 dynamic_registration: Some(true),
685 }),
686 signature_help: Some(SignatureHelpClientCapabilities {
687 signature_information: Some(SignatureInformationSettings {
688 documentation_format: Some(vec![
689 MarkupKind::Markdown,
690 MarkupKind::PlainText,
691 ]),
692 parameter_information: Some(ParameterInformationSettings {
693 label_offset_support: Some(true),
694 }),
695 active_parameter_support: Some(true),
696 }),
697 ..SignatureHelpClientCapabilities::default()
698 }),
699 synchronization: Some(TextDocumentSyncClientCapabilities {
700 did_save: Some(true),
701 ..TextDocumentSyncClientCapabilities::default()
702 }),
703 ..TextDocumentClientCapabilities::default()
704 }),
705 experimental: Some(json!({
706 "serverStatusNotification": true,
707 })),
708 window: Some(WindowClientCapabilities {
709 work_done_progress: Some(true),
710 ..Default::default()
711 }),
712 general: None,
713 },
714 trace: None,
715 workspace_folders: Some(vec![WorkspaceFolder {
716 uri: root_uri,
717 name: Default::default(),
718 }]),
719 client_info: release_channel::ReleaseChannel::try_global(cx).map(|release_channel| {
720 ClientInfo {
721 name: release_channel.display_name().to_string(),
722 version: Some(release_channel::AppVersion::global(cx).to_string()),
723 }
724 }),
725 locale: None,
726 ..Default::default()
727 };
728
729 cx.spawn(|_| async move {
730 let response = self.request::<request::Initialize>(params).await?;
731 if let Some(info) = response.server_info {
732 self.name = info.name.into();
733 }
734 self.capabilities = RwLock::new(response.capabilities);
735
736 self.notify::<notification::Initialized>(InitializedParams {})?;
737 Ok(Arc::new(self))
738 })
739 }
740
741 /// Sends a shutdown request to the language server process and prepares the [`LanguageServer`] to be dropped.
742 pub fn shutdown(&self) -> Option<impl 'static + Send + Future<Output = Option<()>>> {
743 if let Some(tasks) = self.io_tasks.lock().take() {
744 let response_handlers = self.response_handlers.clone();
745 let next_id = AtomicI32::new(self.next_id.load(SeqCst));
746 let outbound_tx = self.outbound_tx.clone();
747 let executor = self.executor.clone();
748 let mut output_done = self.output_done_rx.lock().take().unwrap();
749 let shutdown_request = Self::request_internal::<request::Shutdown>(
750 &next_id,
751 &response_handlers,
752 &outbound_tx,
753 &executor,
754 (),
755 );
756 let exit = Self::notify_internal::<notification::Exit>(&outbound_tx, ());
757 outbound_tx.close();
758
759 let server = self.server.clone();
760 let name = self.name.clone();
761 let mut timer = self.executor.timer(SERVER_SHUTDOWN_TIMEOUT).fuse();
762 Some(
763 async move {
764 log::debug!("language server shutdown started");
765
766 select! {
767 request_result = shutdown_request.fuse() => {
768 request_result?;
769 }
770
771 _ = timer => {
772 log::info!("timeout waiting for language server {name} to shutdown");
773 },
774 }
775
776 response_handlers.lock().take();
777 exit?;
778 output_done.recv().await;
779 server.lock().take().map(|mut child| child.kill());
780 log::debug!("language server shutdown finished");
781
782 drop(tasks);
783 anyhow::Ok(())
784 }
785 .log_err(),
786 )
787 } else {
788 None
789 }
790 }
791
792 /// Register a handler to handle incoming LSP notifications.
793 ///
794 /// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#notificationMessage)
795 #[must_use]
796 pub fn on_notification<T, F>(&self, f: F) -> Subscription
797 where
798 T: notification::Notification,
799 F: 'static + Send + FnMut(T::Params, AsyncAppContext),
800 {
801 self.on_custom_notification(T::METHOD, f)
802 }
803
804 /// Register a handler to handle incoming LSP requests.
805 ///
806 /// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage)
807 #[must_use]
808 pub fn on_request<T, F, Fut>(&self, f: F) -> Subscription
809 where
810 T: request::Request,
811 T::Params: 'static + Send,
812 F: 'static + FnMut(T::Params, AsyncAppContext) -> Fut + Send,
813 Fut: 'static + Future<Output = Result<T::Result>>,
814 {
815 self.on_custom_request(T::METHOD, f)
816 }
817
818 /// Registers a handler to inspect all language server process stdio.
819 #[must_use]
820 pub fn on_io<F>(&self, f: F) -> Subscription
821 where
822 F: 'static + Send + FnMut(IoKind, &str),
823 {
824 let id = self.next_id.fetch_add(1, SeqCst);
825 self.io_handlers.lock().insert(id, Box::new(f));
826 Subscription::Io {
827 id,
828 io_handlers: Some(Arc::downgrade(&self.io_handlers)),
829 }
830 }
831
832 /// Removes a request handler registers via [`Self::on_request`].
833 pub fn remove_request_handler<T: request::Request>(&self) {
834 self.notification_handlers.lock().remove(T::METHOD);
835 }
836
837 /// Removes a notification handler registers via [`Self::on_notification`].
838 pub fn remove_notification_handler<T: notification::Notification>(&self) {
839 self.notification_handlers.lock().remove(T::METHOD);
840 }
841
842 /// Checks if a notification handler has been registered via [`Self::on_notification`].
843 pub fn has_notification_handler<T: notification::Notification>(&self) -> bool {
844 self.notification_handlers.lock().contains_key(T::METHOD)
845 }
846
847 #[must_use]
848 fn on_custom_notification<Params, F>(&self, method: &'static str, mut f: F) -> Subscription
849 where
850 F: 'static + FnMut(Params, AsyncAppContext) + Send,
851 Params: DeserializeOwned,
852 {
853 let prev_handler = self.notification_handlers.lock().insert(
854 method,
855 Box::new(move |_, params, cx| {
856 if let Some(params) = serde_json::from_value(params).log_err() {
857 f(params, cx);
858 }
859 }),
860 );
861 assert!(
862 prev_handler.is_none(),
863 "registered multiple handlers for the same LSP method"
864 );
865 Subscription::Notification {
866 method,
867 notification_handlers: Some(self.notification_handlers.clone()),
868 }
869 }
870
871 #[must_use]
872 fn on_custom_request<Params, Res, Fut, F>(&self, method: &'static str, mut f: F) -> Subscription
873 where
874 F: 'static + FnMut(Params, AsyncAppContext) -> Fut + Send,
875 Fut: 'static + Future<Output = Result<Res>>,
876 Params: DeserializeOwned + Send + 'static,
877 Res: Serialize,
878 {
879 let outbound_tx = self.outbound_tx.clone();
880 let prev_handler = self.notification_handlers.lock().insert(
881 method,
882 Box::new(move |id, params, cx| {
883 if let Some(id) = id {
884 match serde_json::from_value(params) {
885 Ok(params) => {
886 let response = f(params, cx.clone());
887 cx.foreground_executor()
888 .spawn({
889 let outbound_tx = outbound_tx.clone();
890 async move {
891 let response = match response.await {
892 Ok(result) => Response {
893 jsonrpc: JSON_RPC_VERSION,
894 id,
895 value: LspResult::Ok(Some(result)),
896 },
897 Err(error) => Response {
898 jsonrpc: JSON_RPC_VERSION,
899 id,
900 value: LspResult::Error(Some(Error {
901 message: error.to_string(),
902 })),
903 },
904 };
905 if let Some(response) =
906 serde_json::to_string(&response).log_err()
907 {
908 outbound_tx.try_send(response).ok();
909 }
910 }
911 })
912 .detach();
913 }
914
915 Err(error) => {
916 log::error!("error deserializing {} request: {:?}", method, error);
917 let response = AnyResponse {
918 jsonrpc: JSON_RPC_VERSION,
919 id,
920 result: None,
921 error: Some(Error {
922 message: error.to_string(),
923 }),
924 };
925 if let Some(response) = serde_json::to_string(&response).log_err() {
926 outbound_tx.try_send(response).ok();
927 }
928 }
929 }
930 }
931 }),
932 );
933 assert!(
934 prev_handler.is_none(),
935 "registered multiple handlers for the same LSP method"
936 );
937 Subscription::Notification {
938 method,
939 notification_handlers: Some(self.notification_handlers.clone()),
940 }
941 }
942
943 /// Get the name of the running language server.
944 pub fn name(&self) -> &str {
945 &self.name
946 }
947
948 /// Get the reported capabilities of the running language server.
949 pub fn capabilities(&self) -> ServerCapabilities {
950 self.capabilities.read().clone()
951 }
952
953 /// Get the reported capabilities of the running language server and
954 /// what we know on the client/adapter-side of its capabilities.
955 pub fn adapter_server_capabilities(&self) -> AdapterServerCapabilities {
956 AdapterServerCapabilities {
957 server_capabilities: self.capabilities(),
958 code_action_kinds: self.code_action_kinds(),
959 }
960 }
961
962 pub fn update_capabilities(&self, update: impl FnOnce(&mut ServerCapabilities)) {
963 update(self.capabilities.write().deref_mut());
964 }
965
966 /// Get the id of the running language server.
967 pub fn server_id(&self) -> LanguageServerId {
968 self.server_id
969 }
970
971 /// Get the root path of the project the language server is running against.
972 pub fn root_path(&self) -> &PathBuf {
973 &self.root_path
974 }
975
976 /// Sends a RPC request to the language server.
977 ///
978 /// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage)
979 pub fn request<T: request::Request>(
980 &self,
981 params: T::Params,
982 ) -> impl LspRequestFuture<Result<T::Result>>
983 where
984 T::Result: 'static + Send,
985 {
986 Self::request_internal::<T>(
987 &self.next_id,
988 &self.response_handlers,
989 &self.outbound_tx,
990 &self.executor,
991 params,
992 )
993 }
994
995 fn request_internal<T: request::Request>(
996 next_id: &AtomicI32,
997 response_handlers: &Mutex<Option<HashMap<RequestId, ResponseHandler>>>,
998 outbound_tx: &channel::Sender<String>,
999 executor: &BackgroundExecutor,
1000 params: T::Params,
1001 ) -> impl LspRequestFuture<Result<T::Result>>
1002 where
1003 T::Result: 'static + Send,
1004 {
1005 let id = next_id.fetch_add(1, SeqCst);
1006 let message = serde_json::to_string(&Request {
1007 jsonrpc: JSON_RPC_VERSION,
1008 id: RequestId::Int(id),
1009 method: T::METHOD,
1010 params,
1011 })
1012 .unwrap();
1013
1014 let (tx, rx) = oneshot::channel();
1015 let handle_response = response_handlers
1016 .lock()
1017 .as_mut()
1018 .ok_or_else(|| anyhow!("server shut down"))
1019 .map(|handlers| {
1020 let executor = executor.clone();
1021 handlers.insert(
1022 RequestId::Int(id),
1023 Box::new(move |result| {
1024 executor
1025 .spawn(async move {
1026 let response = match result {
1027 Ok(response) => match serde_json::from_str(&response) {
1028 Ok(deserialized) => Ok(deserialized),
1029 Err(error) => {
1030 log::error!("failed to deserialize response from language server: {}. response from language server: {:?}", error, response);
1031 Err(error).context("failed to deserialize response")
1032 }
1033 }
1034 Err(error) => Err(anyhow!("{}", error.message)),
1035 };
1036 _ = tx.send(response);
1037 })
1038 .detach();
1039 }),
1040 );
1041 });
1042
1043 let send = outbound_tx
1044 .try_send(message)
1045 .context("failed to write to language server's stdin");
1046
1047 let outbound_tx = outbound_tx.downgrade();
1048 let mut timeout = executor.timer(LSP_REQUEST_TIMEOUT).fuse();
1049 let started = Instant::now();
1050 LspRequest::new(id, async move {
1051 handle_response?;
1052 send?;
1053
1054 let cancel_on_drop = util::defer(move || {
1055 if let Some(outbound_tx) = outbound_tx.upgrade() {
1056 Self::notify_internal::<notification::Cancel>(
1057 &outbound_tx,
1058 CancelParams {
1059 id: NumberOrString::Number(id),
1060 },
1061 )
1062 .log_err();
1063 }
1064 });
1065
1066 let method = T::METHOD;
1067 select! {
1068 response = rx.fuse() => {
1069 let elapsed = started.elapsed();
1070 log::trace!("Took {elapsed:?} to receive response to {method:?} id {id}");
1071 cancel_on_drop.abort();
1072 response?
1073 }
1074
1075 _ = timeout => {
1076 log::error!("Cancelled LSP request task for {method:?} id {id} which took over {LSP_REQUEST_TIMEOUT:?}");
1077 anyhow::bail!("LSP request timeout");
1078 }
1079 }
1080 })
1081 }
1082
1083 /// Sends a RPC notification to the language server.
1084 ///
1085 /// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#notificationMessage)
1086 pub fn notify<T: notification::Notification>(&self, params: T::Params) -> Result<()> {
1087 Self::notify_internal::<T>(&self.outbound_tx, params)
1088 }
1089
1090 fn notify_internal<T: notification::Notification>(
1091 outbound_tx: &channel::Sender<String>,
1092 params: T::Params,
1093 ) -> Result<()> {
1094 let message = serde_json::to_string(&Notification {
1095 jsonrpc: JSON_RPC_VERSION,
1096 method: T::METHOD,
1097 params,
1098 })
1099 .unwrap();
1100 outbound_tx.try_send(message)?;
1101 Ok(())
1102 }
1103}
1104
1105impl Drop for LanguageServer {
1106 fn drop(&mut self) {
1107 if let Some(shutdown) = self.shutdown() {
1108 self.executor.spawn(shutdown).detach();
1109 }
1110 }
1111}
1112
1113impl Subscription {
1114 /// Detaching a subscription handle prevents it from unsubscribing on drop.
1115 pub fn detach(&mut self) {
1116 match self {
1117 Subscription::Notification {
1118 notification_handlers,
1119 ..
1120 } => *notification_handlers = None,
1121 Subscription::Io { io_handlers, .. } => *io_handlers = None,
1122 }
1123 }
1124}
1125
1126impl fmt::Display for LanguageServerId {
1127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1128 self.0.fmt(f)
1129 }
1130}
1131
1132impl fmt::Debug for LanguageServer {
1133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1134 f.debug_struct("LanguageServer")
1135 .field("id", &self.server_id.0)
1136 .field("name", &self.name)
1137 .finish_non_exhaustive()
1138 }
1139}
1140
1141impl Drop for Subscription {
1142 fn drop(&mut self) {
1143 match self {
1144 Subscription::Notification {
1145 method,
1146 notification_handlers,
1147 } => {
1148 if let Some(handlers) = notification_handlers {
1149 handlers.lock().remove(method);
1150 }
1151 }
1152 Subscription::Io { id, io_handlers } => {
1153 if let Some(io_handlers) = io_handlers.as_ref().and_then(|h| h.upgrade()) {
1154 io_handlers.lock().remove(id);
1155 }
1156 }
1157 }
1158 }
1159}
1160
1161/// Mock language server for use in tests.
1162#[cfg(any(test, feature = "test-support"))]
1163#[derive(Clone)]
1164pub struct FakeLanguageServer {
1165 pub binary: LanguageServerBinary,
1166 pub server: Arc<LanguageServer>,
1167 notifications_rx: channel::Receiver<(String, String)>,
1168}
1169
1170#[cfg(any(test, feature = "test-support"))]
1171impl FakeLanguageServer {
1172 /// Construct a fake language server.
1173 pub fn new(
1174 server_id: LanguageServerId,
1175 binary: LanguageServerBinary,
1176 name: String,
1177 capabilities: ServerCapabilities,
1178 cx: AsyncAppContext,
1179 ) -> (LanguageServer, FakeLanguageServer) {
1180 let (stdin_writer, stdin_reader) = async_pipe::pipe();
1181 let (stdout_writer, stdout_reader) = async_pipe::pipe();
1182 let (notifications_tx, notifications_rx) = channel::unbounded();
1183
1184 let mut server = LanguageServer::new_internal(
1185 server_id,
1186 stdin_writer,
1187 stdout_reader,
1188 None::<async_pipe::PipeReader>,
1189 Arc::new(Mutex::new(None)),
1190 None,
1191 Path::new("/"),
1192 Path::new("/"),
1193 None,
1194 cx.clone(),
1195 |_| {},
1196 );
1197 server.name = name.as_str().into();
1198 let fake = FakeLanguageServer {
1199 binary,
1200 server: Arc::new({
1201 let mut server = LanguageServer::new_internal(
1202 server_id,
1203 stdout_writer,
1204 stdin_reader,
1205 None::<async_pipe::PipeReader>,
1206 Arc::new(Mutex::new(None)),
1207 None,
1208 Path::new("/"),
1209 Path::new("/"),
1210 None,
1211 cx,
1212 move |msg| {
1213 notifications_tx
1214 .try_send((
1215 msg.method.to_string(),
1216 msg.params.unwrap_or(Value::Null).to_string(),
1217 ))
1218 .ok();
1219 },
1220 );
1221 server.name = name.as_str().into();
1222 server
1223 }),
1224 notifications_rx,
1225 };
1226 fake.handle_request::<request::Initialize, _, _>({
1227 let capabilities = capabilities;
1228 move |_, _| {
1229 let capabilities = capabilities.clone();
1230 let name = name.clone();
1231 async move {
1232 Ok(InitializeResult {
1233 capabilities,
1234 server_info: Some(ServerInfo {
1235 name,
1236 ..Default::default()
1237 }),
1238 })
1239 }
1240 }
1241 });
1242
1243 (server, fake)
1244 }
1245}
1246
1247#[cfg(any(test, feature = "test-support"))]
1248impl LanguageServer {
1249 pub fn full_capabilities() -> ServerCapabilities {
1250 ServerCapabilities {
1251 document_highlight_provider: Some(OneOf::Left(true)),
1252 code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
1253 document_formatting_provider: Some(OneOf::Left(true)),
1254 document_range_formatting_provider: Some(OneOf::Left(true)),
1255 definition_provider: Some(OneOf::Left(true)),
1256 implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
1257 type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
1258 ..Default::default()
1259 }
1260 }
1261}
1262
1263#[cfg(any(test, feature = "test-support"))]
1264impl FakeLanguageServer {
1265 /// See [`LanguageServer::notify`].
1266 pub fn notify<T: notification::Notification>(&self, params: T::Params) {
1267 self.server.notify::<T>(params).ok();
1268 }
1269
1270 /// See [`LanguageServer::request`].
1271 pub async fn request<T>(&self, params: T::Params) -> Result<T::Result>
1272 where
1273 T: request::Request,
1274 T::Result: 'static + Send,
1275 {
1276 self.server.executor.start_waiting();
1277 self.server.request::<T>(params).await
1278 }
1279
1280 /// Attempts [`Self::try_receive_notification`], unwrapping if it has not received the specified type yet.
1281 pub async fn receive_notification<T: notification::Notification>(&mut self) -> T::Params {
1282 self.server.executor.start_waiting();
1283 self.try_receive_notification::<T>().await.unwrap()
1284 }
1285
1286 /// Consumes the notification channel until it finds a notification for the specified type.
1287 pub async fn try_receive_notification<T: notification::Notification>(
1288 &mut self,
1289 ) -> Option<T::Params> {
1290 use futures::StreamExt as _;
1291
1292 loop {
1293 let (method, params) = self.notifications_rx.next().await?;
1294 if method == T::METHOD {
1295 return Some(serde_json::from_str::<T::Params>(¶ms).unwrap());
1296 } else {
1297 log::info!("skipping message in fake language server {:?}", params);
1298 }
1299 }
1300 }
1301
1302 /// Registers a handler for a specific kind of request. Removes any existing handler for specified request type.
1303 pub fn handle_request<T, F, Fut>(
1304 &self,
1305 mut handler: F,
1306 ) -> futures::channel::mpsc::UnboundedReceiver<()>
1307 where
1308 T: 'static + request::Request,
1309 T::Params: 'static + Send,
1310 F: 'static + Send + FnMut(T::Params, gpui::AsyncAppContext) -> Fut,
1311 Fut: 'static + Send + Future<Output = Result<T::Result>>,
1312 {
1313 let (responded_tx, responded_rx) = futures::channel::mpsc::unbounded();
1314 self.server.remove_request_handler::<T>();
1315 self.server
1316 .on_request::<T, _, _>(move |params, cx| {
1317 let result = handler(params, cx.clone());
1318 let responded_tx = responded_tx.clone();
1319 let executor = cx.background_executor().clone();
1320 async move {
1321 executor.simulate_random_delay().await;
1322 let result = result.await;
1323 responded_tx.unbounded_send(()).ok();
1324 result
1325 }
1326 })
1327 .detach();
1328 responded_rx
1329 }
1330
1331 /// Registers a handler for a specific kind of notification. Removes any existing handler for specified notification type.
1332 pub fn handle_notification<T, F>(
1333 &self,
1334 mut handler: F,
1335 ) -> futures::channel::mpsc::UnboundedReceiver<()>
1336 where
1337 T: 'static + notification::Notification,
1338 T::Params: 'static + Send,
1339 F: 'static + Send + FnMut(T::Params, gpui::AsyncAppContext),
1340 {
1341 let (handled_tx, handled_rx) = futures::channel::mpsc::unbounded();
1342 self.server.remove_notification_handler::<T>();
1343 self.server
1344 .on_notification::<T, _>(move |params, cx| {
1345 handler(params, cx.clone());
1346 handled_tx.unbounded_send(()).ok();
1347 })
1348 .detach();
1349 handled_rx
1350 }
1351
1352 /// Removes any existing handler for specified notification type.
1353 pub fn remove_request_handler<T>(&mut self)
1354 where
1355 T: 'static + request::Request,
1356 {
1357 self.server.remove_request_handler::<T>();
1358 }
1359
1360 /// Simulate that the server has started work and notifies about its progress with the specified token.
1361 pub async fn start_progress(&self, token: impl Into<String>) {
1362 self.start_progress_with(token, Default::default()).await
1363 }
1364
1365 pub async fn start_progress_with(
1366 &self,
1367 token: impl Into<String>,
1368 progress: WorkDoneProgressBegin,
1369 ) {
1370 let token = token.into();
1371 self.request::<request::WorkDoneProgressCreate>(WorkDoneProgressCreateParams {
1372 token: NumberOrString::String(token.clone()),
1373 })
1374 .await
1375 .unwrap();
1376 self.notify::<notification::Progress>(ProgressParams {
1377 token: NumberOrString::String(token),
1378 value: ProgressParamsValue::WorkDone(WorkDoneProgress::Begin(progress)),
1379 });
1380 }
1381
1382 /// Simulate that the server has completed work and notifies about that with the specified token.
1383 pub fn end_progress(&self, token: impl Into<String>) {
1384 self.notify::<notification::Progress>(ProgressParams {
1385 token: NumberOrString::String(token.into()),
1386 value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(Default::default())),
1387 });
1388 }
1389}
1390
1391#[cfg(test)]
1392mod tests {
1393 use super::*;
1394 use gpui::{SemanticVersion, TestAppContext};
1395 use std::str::FromStr;
1396
1397 #[ctor::ctor]
1398 fn init_logger() {
1399 if std::env::var("RUST_LOG").is_ok() {
1400 env_logger::init();
1401 }
1402 }
1403
1404 #[gpui::test]
1405 async fn test_fake(cx: &mut TestAppContext) {
1406 cx.update(|cx| {
1407 release_channel::init(SemanticVersion::default(), cx);
1408 });
1409 let (server, mut fake) = FakeLanguageServer::new(
1410 LanguageServerId(0),
1411 LanguageServerBinary {
1412 path: "path/to/language-server".into(),
1413 arguments: vec![],
1414 env: None,
1415 },
1416 "the-lsp".to_string(),
1417 Default::default(),
1418 cx.to_async(),
1419 );
1420
1421 let (message_tx, message_rx) = channel::unbounded();
1422 let (diagnostics_tx, diagnostics_rx) = channel::unbounded();
1423 server
1424 .on_notification::<notification::ShowMessage, _>(move |params, _| {
1425 message_tx.try_send(params).unwrap()
1426 })
1427 .detach();
1428 server
1429 .on_notification::<notification::PublishDiagnostics, _>(move |params, _| {
1430 diagnostics_tx.try_send(params).unwrap()
1431 })
1432 .detach();
1433
1434 let server = cx.update(|cx| server.initialize(None, cx)).await.unwrap();
1435 server
1436 .notify::<notification::DidOpenTextDocument>(DidOpenTextDocumentParams {
1437 text_document: TextDocumentItem::new(
1438 Url::from_str("file://a/b").unwrap(),
1439 "rust".to_string(),
1440 0,
1441 "".to_string(),
1442 ),
1443 })
1444 .unwrap();
1445 assert_eq!(
1446 fake.receive_notification::<notification::DidOpenTextDocument>()
1447 .await
1448 .text_document
1449 .uri
1450 .as_str(),
1451 "file://a/b"
1452 );
1453
1454 fake.notify::<notification::ShowMessage>(ShowMessageParams {
1455 typ: MessageType::ERROR,
1456 message: "ok".to_string(),
1457 });
1458 fake.notify::<notification::PublishDiagnostics>(PublishDiagnosticsParams {
1459 uri: Url::from_str("file://b/c").unwrap(),
1460 version: Some(5),
1461 diagnostics: vec![],
1462 });
1463 assert_eq!(message_rx.recv().await.unwrap().message, "ok");
1464 assert_eq!(
1465 diagnostics_rx.recv().await.unwrap().uri.as_str(),
1466 "file://b/c"
1467 );
1468
1469 fake.handle_request::<request::Shutdown, _, _>(|_, _| async move { Ok(()) });
1470
1471 drop(server);
1472 fake.receive_notification::<notification::Exit>().await;
1473 }
1474
1475 #[gpui::test]
1476 fn test_deserialize_string_digit_id() {
1477 let json = r#"{"jsonrpc":"2.0","id":"2","method":"workspace/configuration","params":{"items":[{"scopeUri":"file:///Users/mph/Devel/personal/hello-scala/","section":"metals"}]}}"#;
1478 let notification = serde_json::from_str::<AnyNotification>(json)
1479 .expect("message with string id should be parsed");
1480 let expected_id = RequestId::Str("2".to_string());
1481 assert_eq!(notification.id, Some(expected_id));
1482 }
1483
1484 #[gpui::test]
1485 fn test_deserialize_string_id() {
1486 let json = r#"{"jsonrpc":"2.0","id":"anythingAtAll","method":"workspace/configuration","params":{"items":[{"scopeUri":"file:///Users/mph/Devel/personal/hello-scala/","section":"metals"}]}}"#;
1487 let notification = serde_json::from_str::<AnyNotification>(json)
1488 .expect("message with string id should be parsed");
1489 let expected_id = RequestId::Str("anythingAtAll".to_string());
1490 assert_eq!(notification.id, Some(expected_id));
1491 }
1492
1493 #[gpui::test]
1494 fn test_deserialize_int_id() {
1495 let json = r#"{"jsonrpc":"2.0","id":2,"method":"workspace/configuration","params":{"items":[{"scopeUri":"file:///Users/mph/Devel/personal/hello-scala/","section":"metals"}]}}"#;
1496 let notification = serde_json::from_str::<AnyNotification>(json)
1497 .expect("message with string id should be parsed");
1498 let expected_id = RequestId::Int(2);
1499 assert_eq!(notification.id, Some(expected_id));
1500 }
1501
1502 #[test]
1503 fn test_serialize_has_no_nulls() {
1504 // Ensure we're not setting both result and error variants. (ticket #10595)
1505 let no_tag = Response::<u32> {
1506 jsonrpc: "",
1507 id: RequestId::Int(0),
1508 value: LspResult::Ok(None),
1509 };
1510 assert_eq!(
1511 serde_json::to_string(&no_tag).unwrap(),
1512 "{\"jsonrpc\":\"\",\"id\":0,\"result\":null}"
1513 );
1514 let no_tag = Response::<u32> {
1515 jsonrpc: "",
1516 id: RequestId::Int(0),
1517 value: LspResult::Error(None),
1518 };
1519 assert_eq!(
1520 serde_json::to_string(&no_tag).unwrap(),
1521 "{\"jsonrpc\":\"\",\"id\":0,\"error\":null}"
1522 );
1523 }
1524}