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