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