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