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