1use crate::{
2 buffer_store::{BufferStore, BufferStoreEvent},
3 deserialize_code_actions,
4 environment::ProjectEnvironment,
5 lsp_command::{self, *},
6 lsp_ext_command,
7 prettier_store::{self, PrettierStore, PrettierStoreEvent},
8 project_settings::{LspSettings, ProjectSettings},
9 project_tree::{AdapterQuery, LanguageServerTree, LaunchDisposition, ProjectTree},
10 relativize_path, resolve_path,
11 toolchain_store::{EmptyToolchainStore, ToolchainStoreEvent},
12 worktree_store::{WorktreeStore, WorktreeStoreEvent},
13 yarn::YarnPathStore,
14 ActionVariant, CodeAction, Completion, CoreCompletion, Hover, InlayHint, ProjectItem as _,
15 ProjectPath, ProjectTransaction, ResolveState, Symbol, ToolchainStore,
16};
17use anyhow::{anyhow, Context as _, Result};
18use async_trait::async_trait;
19use client::{proto, TypedEnvelope};
20use collections::{btree_map, BTreeMap, BTreeSet, HashMap, HashSet};
21use futures::{
22 future::{join_all, Shared},
23 select,
24 stream::FuturesUnordered,
25 AsyncWriteExt, Future, FutureExt, StreamExt,
26};
27use globset::{Glob, GlobBuilder, GlobMatcher, GlobSet, GlobSetBuilder};
28use gpui::{
29 App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, PromptLevel, SharedString, Task,
30 WeakEntity,
31};
32use http_client::HttpClient;
33use itertools::Itertools as _;
34use language::{
35 language_settings::{
36 language_settings, FormatOnSave, Formatter, LanguageSettings, SelectedFormatter,
37 },
38 point_to_lsp,
39 proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
40 range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CodeLabel,
41 Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, File as _, Language, LanguageRegistry,
42 LanguageServerBinaryStatus, LanguageToolchainStore, LocalFile, LspAdapter, LspAdapterDelegate,
43 Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped,
44};
45use lsp::{
46 notification::DidRenameFiles, CodeActionKind, CompletionContext, DiagnosticSeverity,
47 DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, Edit, FileOperationFilter,
48 FileOperationPatternKind, FileOperationRegistrationOptions, FileRename, FileSystemWatcher,
49 InsertTextFormat, LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions,
50 LanguageServerId, LanguageServerName, LspRequestFuture, MessageActionItem, MessageType, OneOf,
51 RenameFilesParams, ServerHealthStatus, ServerStatus, SymbolKind, TextEdit, WillRenameFiles,
52 WorkDoneProgressCancelParams, WorkspaceFolder,
53};
54use node_runtime::read_package_installed_version;
55use parking_lot::Mutex;
56use postage::watch;
57use rand::prelude::*;
58
59use rpc::{
60 proto::{FromProto, ToProto},
61 AnyProtoClient,
62};
63use serde::Serialize;
64use settings::{Settings, SettingsLocation, SettingsStore};
65use sha2::{Digest, Sha256};
66use smol::channel::Sender;
67use snippet::Snippet;
68use std::{
69 any::Any,
70 cell::RefCell,
71 cmp::Ordering,
72 convert::TryInto,
73 ffi::OsStr,
74 iter, mem,
75 ops::{ControlFlow, Range},
76 path::{self, Path, PathBuf},
77 rc::Rc,
78 str,
79 sync::Arc,
80 time::{Duration, Instant},
81};
82use text::{Anchor, BufferId, LineEnding, OffsetRangeExt, TransactionId};
83use url::Url;
84use util::{
85 debug_panic, defer, maybe, merge_json_value_into, paths::SanitizedPath, post_inc, ResultExt,
86 TryFutureExt as _,
87};
88
89pub use fs::*;
90pub use language::Location;
91#[cfg(any(test, feature = "test-support"))]
92pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
93pub use worktree::{
94 Entry, EntryKind, File, LocalWorktree, PathChange, ProjectEntryId, UpdatedEntriesSet,
95 UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings, FS_WATCH_LATENCY,
96};
97
98const SERVER_LAUNCHING_BEFORE_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);
99pub const SERVER_PROGRESS_THROTTLE_TIMEOUT: Duration = Duration::from_millis(100);
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub enum FormatTrigger {
103 Save,
104 Manual,
105}
106
107pub enum LspFormatTarget {
108 Buffers,
109 Ranges(BTreeMap<BufferId, Vec<Range<Anchor>>>),
110}
111
112pub type OpenLspBufferHandle = Entity<Entity<Buffer>>;
113
114// Currently, formatting operations are represented differently depending on
115// whether they come from a language server or an external command.
116#[derive(Debug)]
117pub enum FormatOperation {
118 Lsp(Vec<(Range<Anchor>, Arc<str>)>),
119 External(Diff),
120 Prettier(Diff),
121}
122
123impl FormatTrigger {
124 fn from_proto(value: i32) -> FormatTrigger {
125 match value {
126 0 => FormatTrigger::Save,
127 1 => FormatTrigger::Manual,
128 _ => FormatTrigger::Save,
129 }
130 }
131}
132
133pub struct LocalLspStore {
134 weak: WeakEntity<LspStore>,
135 worktree_store: Entity<WorktreeStore>,
136 toolchain_store: Entity<ToolchainStore>,
137 http_client: Arc<dyn HttpClient>,
138 environment: Entity<ProjectEnvironment>,
139 fs: Arc<dyn Fs>,
140 languages: Arc<LanguageRegistry>,
141 language_server_ids: HashMap<(WorktreeId, LanguageServerName), BTreeSet<LanguageServerId>>,
142 yarn: Entity<YarnPathStore>,
143 pub language_servers: HashMap<LanguageServerId, LanguageServerState>,
144 buffers_being_formatted: HashSet<BufferId>,
145 last_workspace_edits_by_language_server: HashMap<LanguageServerId, ProjectTransaction>,
146 language_server_watched_paths: HashMap<LanguageServerId, LanguageServerWatchedPaths>,
147 language_server_paths_watched_for_rename:
148 HashMap<LanguageServerId, RenamePathsWatchedForServer>,
149 language_server_watcher_registrations:
150 HashMap<LanguageServerId, HashMap<String, Vec<FileSystemWatcher>>>,
151 supplementary_language_servers:
152 HashMap<LanguageServerId, (LanguageServerName, Arc<LanguageServer>)>,
153 prettier_store: Entity<PrettierStore>,
154 next_diagnostic_group_id: usize,
155 diagnostics: HashMap<
156 WorktreeId,
157 HashMap<
158 Arc<Path>,
159 Vec<(
160 LanguageServerId,
161 Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
162 )>,
163 >,
164 >,
165 buffer_snapshots: HashMap<BufferId, HashMap<LanguageServerId, Vec<LspBufferSnapshot>>>, // buffer_id -> server_id -> vec of snapshots
166 _subscription: gpui::Subscription,
167 lsp_tree: Entity<LanguageServerTree>,
168 registered_buffers: HashMap<BufferId, usize>,
169}
170
171impl LocalLspStore {
172 /// Returns the running language server for the given ID. Note if the language server is starting, it will not be returned.
173 pub fn running_language_server_for_id(
174 &self,
175 id: LanguageServerId,
176 ) -> Option<&Arc<LanguageServer>> {
177 let language_server_state = self.language_servers.get(&id)?;
178
179 match language_server_state {
180 LanguageServerState::Running { server, .. } => Some(server),
181 LanguageServerState::Starting { .. } => None,
182 }
183 }
184
185 fn start_language_server(
186 &mut self,
187 worktree_handle: &Entity<Worktree>,
188 delegate: Arc<LocalLspAdapterDelegate>,
189 adapter: Arc<CachedLspAdapter>,
190 settings: Arc<LspSettings>,
191 cx: &mut App,
192 ) -> LanguageServerId {
193 let worktree = worktree_handle.read(cx);
194 let worktree_id = worktree.id();
195 let root_path = worktree.abs_path();
196 let key = (worktree_id, adapter.name.clone());
197
198 let override_options = settings.initialization_options.clone();
199
200 let stderr_capture = Arc::new(Mutex::new(Some(String::new())));
201
202 let server_id = self.languages.next_language_server_id();
203 log::info!(
204 "attempting to start language server {:?}, path: {root_path:?}, id: {server_id}",
205 adapter.name.0
206 );
207
208 let binary = self.get_language_server_binary(adapter.clone(), delegate.clone(), true, cx);
209 let pending_workspace_folders: Arc<Mutex<BTreeSet<Url>>> = Default::default();
210 let pending_server = cx.spawn({
211 let adapter = adapter.clone();
212 let server_name = adapter.name.clone();
213 let stderr_capture = stderr_capture.clone();
214 #[cfg(any(test, feature = "test-support"))]
215 let lsp_store = self.weak.clone();
216 let pending_workspace_folders = pending_workspace_folders.clone();
217 move |cx| async move {
218 let binary = binary.await?;
219 #[cfg(any(test, feature = "test-support"))]
220 if let Some(server) = lsp_store
221 .update(&mut cx.clone(), |this, cx| {
222 this.languages.create_fake_language_server(
223 server_id,
224 &server_name,
225 binary.clone(),
226 cx.to_async(),
227 )
228 })
229 .ok()
230 .flatten()
231 {
232 return Ok(server);
233 }
234
235 lsp::LanguageServer::new(
236 stderr_capture,
237 server_id,
238 server_name,
239 binary,
240 &root_path,
241 adapter.code_action_kinds(),
242 pending_workspace_folders,
243 cx,
244 )
245 }
246 });
247
248 let startup = {
249 let server_name = adapter.name.0.clone();
250 let delegate = delegate as Arc<dyn LspAdapterDelegate>;
251 let key = key.clone();
252 let adapter = adapter.clone();
253 let this = self.weak.clone();
254 let pending_workspace_folders = pending_workspace_folders.clone();
255 let fs = self.fs.clone();
256 cx.spawn(move |mut cx| async move {
257 let result = {
258 let delegate = delegate.clone();
259 let adapter = adapter.clone();
260 let this = this.clone();
261 let toolchains = this
262 .update(&mut cx, |this, cx| this.toolchain_store(cx))
263 .ok()?;
264 let mut cx = cx.clone();
265 async move {
266 let language_server = pending_server.await?;
267
268 let workspace_config = adapter
269 .adapter
270 .clone()
271 .workspace_configuration(
272 fs.as_ref(),
273 &delegate,
274 toolchains.clone(),
275 &mut cx,
276 )
277 .await?;
278
279 let mut initialization_options = adapter
280 .adapter
281 .clone()
282 .initialization_options(fs.as_ref(), &(delegate))
283 .await?;
284
285 match (&mut initialization_options, override_options) {
286 (Some(initialization_options), Some(override_options)) => {
287 merge_json_value_into(override_options, initialization_options);
288 }
289 (None, override_options) => initialization_options = override_options,
290 _ => {}
291 }
292
293 let initialization_params = cx.update(|cx| {
294 let mut params = language_server.default_initialize_params(cx);
295 params.initialization_options = initialization_options;
296 adapter.adapter.prepare_initialize_params(params)
297 })??;
298
299 Self::setup_lsp_messages(
300 this.clone(),
301 fs,
302 &language_server,
303 delegate,
304 adapter,
305 );
306
307 let did_change_configuration_params =
308 Arc::new(lsp::DidChangeConfigurationParams {
309 settings: workspace_config,
310 });
311 let language_server = cx
312 .update(|cx| {
313 language_server.initialize(
314 initialization_params,
315 did_change_configuration_params.clone(),
316 cx,
317 )
318 })?
319 .await
320 .inspect_err(|_| {
321 if let Some(this) = this.upgrade() {
322 this.update(&mut cx, |_, cx| {
323 cx.emit(LspStoreEvent::LanguageServerRemoved(server_id))
324 })
325 .ok();
326 }
327 })?;
328
329 language_server
330 .notify::<lsp::notification::DidChangeConfiguration>(
331 &did_change_configuration_params,
332 )
333 .ok();
334
335 anyhow::Ok(language_server)
336 }
337 }
338 .await;
339
340 match result {
341 Ok(server) => {
342 this.update(&mut cx, |this, mut cx| {
343 this.insert_newly_running_language_server(
344 adapter,
345 server.clone(),
346 server_id,
347 key,
348 pending_workspace_folders,
349 &mut cx,
350 );
351 })
352 .ok();
353 stderr_capture.lock().take();
354 Some(server)
355 }
356
357 Err(err) => {
358 let log = stderr_capture.lock().take().unwrap_or_default();
359 delegate.update_status(
360 adapter.name(),
361 LanguageServerBinaryStatus::Failed {
362 error: format!("{err}\n-- stderr--\n{}", log),
363 },
364 );
365 log::error!("Failed to start language server {server_name:?}: {err}");
366 log::error!("server stderr: {:?}", log);
367 None
368 }
369 }
370 })
371 };
372 let state = LanguageServerState::Starting {
373 startup,
374 pending_workspace_folders,
375 };
376
377 self.language_servers.insert(server_id, state);
378 self.language_server_ids
379 .entry(key)
380 .or_default()
381 .insert(server_id);
382 server_id
383 }
384
385 fn get_language_server_binary(
386 &self,
387 adapter: Arc<CachedLspAdapter>,
388 delegate: Arc<dyn LspAdapterDelegate>,
389 allow_binary_download: bool,
390 cx: &mut App,
391 ) -> Task<Result<LanguageServerBinary>> {
392 let settings = ProjectSettings::get(
393 Some(SettingsLocation {
394 worktree_id: delegate.worktree_id(),
395 path: Path::new(""),
396 }),
397 cx,
398 )
399 .lsp
400 .get(&adapter.name)
401 .and_then(|s| s.binary.clone());
402
403 if settings.as_ref().is_some_and(|b| b.path.is_some()) {
404 let settings = settings.unwrap();
405 return cx.spawn(|_| async move {
406 Ok(LanguageServerBinary {
407 path: PathBuf::from(&settings.path.unwrap()),
408 env: Some(delegate.shell_env().await),
409 arguments: settings
410 .arguments
411 .unwrap_or_default()
412 .iter()
413 .map(Into::into)
414 .collect(),
415 })
416 });
417 }
418 let lsp_binary_options = LanguageServerBinaryOptions {
419 allow_path_lookup: !settings
420 .as_ref()
421 .and_then(|b| b.ignore_system_version)
422 .unwrap_or_default(),
423 allow_binary_download,
424 };
425 let toolchains = self.toolchain_store.read(cx).as_language_toolchain_store();
426 cx.spawn(|mut cx| async move {
427 let binary_result = adapter
428 .clone()
429 .get_language_server_command(
430 delegate.clone(),
431 toolchains,
432 lsp_binary_options,
433 &mut cx,
434 )
435 .await;
436
437 delegate.update_status(adapter.name.clone(), LanguageServerBinaryStatus::None);
438
439 let mut binary = binary_result?;
440 if let Some(arguments) = settings.and_then(|b| b.arguments) {
441 binary.arguments = arguments.into_iter().map(Into::into).collect();
442 }
443
444 let mut shell_env = delegate.shell_env().await;
445 shell_env.extend(binary.env.unwrap_or_default());
446 binary.env = Some(shell_env);
447 Ok(binary)
448 })
449 }
450
451 fn setup_lsp_messages(
452 this: WeakEntity<LspStore>,
453 fs: Arc<dyn Fs>,
454 language_server: &LanguageServer,
455 delegate: Arc<dyn LspAdapterDelegate>,
456 adapter: Arc<CachedLspAdapter>,
457 ) {
458 let name = language_server.name();
459 let server_id = language_server.server_id();
460 language_server
461 .on_notification::<lsp::notification::PublishDiagnostics, _>({
462 let adapter = adapter.clone();
463 let this = this.clone();
464 move |mut params, mut cx| {
465 let adapter = adapter.clone();
466 if let Some(this) = this.upgrade() {
467 adapter.process_diagnostics(&mut params);
468 this.update(&mut cx, |this, cx| {
469 this.update_diagnostics(
470 server_id,
471 params,
472 &adapter.disk_based_diagnostic_sources,
473 cx,
474 )
475 .log_err();
476 })
477 .ok();
478 }
479 }
480 })
481 .detach();
482 language_server
483 .on_request::<lsp::request::WorkspaceConfiguration, _, _>({
484 let adapter = adapter.adapter.clone();
485 let delegate = delegate.clone();
486 let this = this.clone();
487 let fs = fs.clone();
488 move |params, mut cx| {
489 let adapter = adapter.clone();
490 let delegate = delegate.clone();
491 let this = this.clone();
492 let fs = fs.clone();
493 async move {
494 let toolchains =
495 this.update(&mut cx, |this, cx| this.toolchain_store(cx))?;
496 let workspace_config = adapter
497 .workspace_configuration(fs.as_ref(), &delegate, toolchains, &mut cx)
498 .await?;
499 Ok(params
500 .items
501 .into_iter()
502 .map(|item| {
503 if let Some(section) = &item.section {
504 workspace_config
505 .get(section)
506 .cloned()
507 .unwrap_or(serde_json::Value::Null)
508 } else {
509 workspace_config.clone()
510 }
511 })
512 .collect())
513 }
514 }
515 })
516 .detach();
517
518 language_server
519 .on_request::<lsp::request::WorkspaceFoldersRequest, _, _>({
520 let this = this.clone();
521 move |_, mut cx| {
522 let this = this.clone();
523 async move {
524 let Some(server) =
525 this.update(&mut cx, |this, _| this.language_server_for_id(server_id))?
526 else {
527 return Ok(None);
528 };
529 let root = server.workspace_folders();
530 Ok(Some(
531 root.iter()
532 .cloned()
533 .map(|uri| WorkspaceFolder {
534 uri,
535 name: Default::default(),
536 })
537 .collect(),
538 ))
539 }
540 }
541 })
542 .detach();
543 // Even though we don't have handling for these requests, respond to them to
544 // avoid stalling any language server like `gopls` which waits for a response
545 // to these requests when initializing.
546 language_server
547 .on_request::<lsp::request::WorkDoneProgressCreate, _, _>({
548 let this = this.clone();
549 move |params, mut cx| {
550 let this = this.clone();
551 async move {
552 this.update(&mut cx, |this, _| {
553 if let Some(status) = this.language_server_statuses.get_mut(&server_id)
554 {
555 if let lsp::NumberOrString::String(token) = params.token {
556 status.progress_tokens.insert(token);
557 }
558 }
559 })?;
560
561 Ok(())
562 }
563 }
564 })
565 .detach();
566
567 language_server
568 .on_request::<lsp::request::RegisterCapability, _, _>({
569 let this = this.clone();
570 move |params, mut cx| {
571 let this = this.clone();
572 async move {
573 for reg in params.registrations {
574 match reg.method.as_str() {
575 "workspace/didChangeWatchedFiles" => {
576 if let Some(options) = reg.register_options {
577 let options = serde_json::from_value(options)?;
578 this.update(&mut cx, |this, cx| {
579 this.as_local_mut()?.on_lsp_did_change_watched_files(
580 server_id, ®.id, options, cx,
581 );
582 Some(())
583 })?;
584 }
585 }
586 "textDocument/rangeFormatting" => {
587 this.update(&mut cx, |this, _| {
588 if let Some(server) = this.language_server_for_id(server_id)
589 {
590 let options = reg
591 .register_options
592 .map(|options| {
593 serde_json::from_value::<
594 lsp::DocumentRangeFormattingOptions,
595 >(
596 options
597 )
598 })
599 .transpose()?;
600 let provider = match options {
601 None => OneOf::Left(true),
602 Some(options) => OneOf::Right(options),
603 };
604 server.update_capabilities(|capabilities| {
605 capabilities.document_range_formatting_provider =
606 Some(provider);
607 })
608 }
609 anyhow::Ok(())
610 })??;
611 }
612 "textDocument/onTypeFormatting" => {
613 this.update(&mut cx, |this, _| {
614 if let Some(server) = this.language_server_for_id(server_id)
615 {
616 let options = reg
617 .register_options
618 .map(|options| {
619 serde_json::from_value::<
620 lsp::DocumentOnTypeFormattingOptions,
621 >(
622 options
623 )
624 })
625 .transpose()?;
626 if let Some(options) = options {
627 server.update_capabilities(|capabilities| {
628 capabilities
629 .document_on_type_formatting_provider =
630 Some(options);
631 })
632 }
633 }
634 anyhow::Ok(())
635 })??;
636 }
637 "textDocument/formatting" => {
638 this.update(&mut cx, |this, _| {
639 if let Some(server) = this.language_server_for_id(server_id)
640 {
641 let options = reg
642 .register_options
643 .map(|options| {
644 serde_json::from_value::<
645 lsp::DocumentFormattingOptions,
646 >(
647 options
648 )
649 })
650 .transpose()?;
651 let provider = match options {
652 None => OneOf::Left(true),
653 Some(options) => OneOf::Right(options),
654 };
655 server.update_capabilities(|capabilities| {
656 capabilities.document_formatting_provider =
657 Some(provider);
658 })
659 }
660 anyhow::Ok(())
661 })??;
662 }
663 "workspace/didChangeConfiguration" => {
664 // Ignore payload since we notify clients of setting changes unconditionally, relying on them pulling the latest settings.
665 }
666 "textDocument/rename" => {
667 this.update(&mut cx, |this, _| {
668 if let Some(server) = this.language_server_for_id(server_id)
669 {
670 let options = reg
671 .register_options
672 .map(|options| {
673 serde_json::from_value::<lsp::RenameOptions>(
674 options,
675 )
676 })
677 .transpose()?;
678 let options = match options {
679 None => OneOf::Left(true),
680 Some(options) => OneOf::Right(options),
681 };
682
683 server.update_capabilities(|capabilities| {
684 capabilities.rename_provider = Some(options);
685 })
686 }
687 anyhow::Ok(())
688 })??;
689 }
690 _ => log::warn!("unhandled capability registration: {reg:?}"),
691 }
692 }
693 Ok(())
694 }
695 }
696 })
697 .detach();
698
699 language_server
700 .on_request::<lsp::request::UnregisterCapability, _, _>({
701 let this = this.clone();
702 move |params, mut cx| {
703 let this = this.clone();
704 async move {
705 for unreg in params.unregisterations.iter() {
706 match unreg.method.as_str() {
707 "workspace/didChangeWatchedFiles" => {
708 this.update(&mut cx, |this, cx| {
709 this.as_local_mut()?
710 .on_lsp_unregister_did_change_watched_files(
711 server_id, &unreg.id, cx,
712 );
713 Some(())
714 })?;
715 }
716 "workspace/didChangeConfiguration" => {
717 // Ignore payload since we notify clients of setting changes unconditionally, relying on them pulling the latest settings.
718 }
719 "textDocument/rename" => {
720 this.update(&mut cx, |this, _| {
721 if let Some(server) = this.language_server_for_id(server_id)
722 {
723 server.update_capabilities(|capabilities| {
724 capabilities.rename_provider = None
725 })
726 }
727 })?;
728 }
729 "textDocument/rangeFormatting" => {
730 this.update(&mut cx, |this, _| {
731 if let Some(server) = this.language_server_for_id(server_id)
732 {
733 server.update_capabilities(|capabilities| {
734 capabilities.document_range_formatting_provider =
735 None
736 })
737 }
738 })?;
739 }
740 "textDocument/onTypeFormatting" => {
741 this.update(&mut cx, |this, _| {
742 if let Some(server) = this.language_server_for_id(server_id)
743 {
744 server.update_capabilities(|capabilities| {
745 capabilities.document_on_type_formatting_provider =
746 None;
747 })
748 }
749 })?;
750 }
751 "textDocument/formatting" => {
752 this.update(&mut cx, |this, _| {
753 if let Some(server) = this.language_server_for_id(server_id)
754 {
755 server.update_capabilities(|capabilities| {
756 capabilities.document_formatting_provider = None;
757 })
758 }
759 })?;
760 }
761 _ => log::warn!("unhandled capability unregistration: {unreg:?}"),
762 }
763 }
764 Ok(())
765 }
766 }
767 })
768 .detach();
769
770 language_server
771 .on_request::<lsp::request::ApplyWorkspaceEdit, _, _>({
772 let adapter = adapter.clone();
773 let this = this.clone();
774 move |params, cx| {
775 LocalLspStore::on_lsp_workspace_edit(
776 this.clone(),
777 params,
778 server_id,
779 adapter.clone(),
780 cx,
781 )
782 }
783 })
784 .detach();
785
786 language_server
787 .on_request::<lsp::request::InlayHintRefreshRequest, _, _>({
788 let this = this.clone();
789 move |(), mut cx| {
790 let this = this.clone();
791 async move {
792 this.update(&mut cx, |this, cx| {
793 cx.emit(LspStoreEvent::RefreshInlayHints);
794 this.downstream_client.as_ref().map(|(client, project_id)| {
795 client.send(proto::RefreshInlayHints {
796 project_id: *project_id,
797 })
798 })
799 })?
800 .transpose()?;
801 Ok(())
802 }
803 }
804 })
805 .detach();
806
807 language_server
808 .on_request::<lsp::request::ShowMessageRequest, _, _>({
809 let this = this.clone();
810 let name = name.to_string();
811 move |params, mut cx| {
812 let this = this.clone();
813 let name = name.to_string();
814 async move {
815 let actions = params.actions.unwrap_or_default();
816 let (tx, rx) = smol::channel::bounded(1);
817 let request = LanguageServerPromptRequest {
818 level: match params.typ {
819 lsp::MessageType::ERROR => PromptLevel::Critical,
820 lsp::MessageType::WARNING => PromptLevel::Warning,
821 _ => PromptLevel::Info,
822 },
823 message: params.message,
824 actions,
825 response_channel: tx,
826 lsp_name: name.clone(),
827 };
828
829 let did_update = this
830 .update(&mut cx, |_, cx| {
831 cx.emit(LspStoreEvent::LanguageServerPrompt(request));
832 })
833 .is_ok();
834 if did_update {
835 let response = rx.recv().await.ok();
836 Ok(response)
837 } else {
838 Ok(None)
839 }
840 }
841 }
842 })
843 .detach();
844
845 language_server
846 .on_notification::<ServerStatus, _>({
847 let this = this.clone();
848 let name = name.to_string();
849 move |params, mut cx| {
850 let this = this.clone();
851 let name = name.to_string();
852 if let Some(ref message) = params.message {
853 let message = message.trim();
854 if !message.is_empty() {
855 let formatted_message = format!(
856 "Language server {name} (id {server_id}) status update: {message}"
857 );
858 match params.health {
859 ServerHealthStatus::Ok => log::info!("{}", formatted_message),
860 ServerHealthStatus::Warning => log::warn!("{}", formatted_message),
861 ServerHealthStatus::Error => {
862 log::error!("{}", formatted_message);
863 let (tx, _rx) = smol::channel::bounded(1);
864 let request = LanguageServerPromptRequest {
865 level: PromptLevel::Critical,
866 message: params.message.unwrap_or_default(),
867 actions: Vec::new(),
868 response_channel: tx,
869 lsp_name: name.clone(),
870 };
871 let _ = this
872 .update(&mut cx, |_, cx| {
873 cx.emit(LspStoreEvent::LanguageServerPrompt(request));
874 })
875 .ok();
876 }
877 ServerHealthStatus::Other(status) => {
878 log::info!(
879 "Unknown server health: {status}\n{formatted_message}"
880 )
881 }
882 }
883 }
884 }
885 }
886 })
887 .detach();
888 language_server
889 .on_notification::<lsp::notification::ShowMessage, _>({
890 let this = this.clone();
891 let name = name.to_string();
892 move |params, mut cx| {
893 let this = this.clone();
894 let name = name.to_string();
895
896 let (tx, _) = smol::channel::bounded(1);
897 let request = LanguageServerPromptRequest {
898 level: match params.typ {
899 lsp::MessageType::ERROR => PromptLevel::Critical,
900 lsp::MessageType::WARNING => PromptLevel::Warning,
901 _ => PromptLevel::Info,
902 },
903 message: params.message,
904 actions: vec![],
905 response_channel: tx,
906 lsp_name: name.clone(),
907 };
908
909 let _ = this.update(&mut cx, |_, cx| {
910 cx.emit(LspStoreEvent::LanguageServerPrompt(request));
911 });
912 }
913 })
914 .detach();
915
916 let disk_based_diagnostics_progress_token =
917 adapter.disk_based_diagnostics_progress_token.clone();
918
919 language_server
920 .on_notification::<lsp::notification::Progress, _>({
921 let this = this.clone();
922 move |params, mut cx| {
923 if let Some(this) = this.upgrade() {
924 this.update(&mut cx, |this, cx| {
925 this.on_lsp_progress(
926 params,
927 server_id,
928 disk_based_diagnostics_progress_token.clone(),
929 cx,
930 );
931 })
932 .ok();
933 }
934 }
935 })
936 .detach();
937
938 language_server
939 .on_notification::<lsp::notification::LogMessage, _>({
940 let this = this.clone();
941 move |params, mut cx| {
942 if let Some(this) = this.upgrade() {
943 this.update(&mut cx, |_, cx| {
944 cx.emit(LspStoreEvent::LanguageServerLog(
945 server_id,
946 LanguageServerLogType::Log(params.typ),
947 params.message,
948 ));
949 })
950 .ok();
951 }
952 }
953 })
954 .detach();
955
956 language_server
957 .on_notification::<lsp::notification::LogTrace, _>({
958 let this = this.clone();
959 move |params, mut cx| {
960 if let Some(this) = this.upgrade() {
961 this.update(&mut cx, |_, cx| {
962 cx.emit(LspStoreEvent::LanguageServerLog(
963 server_id,
964 LanguageServerLogType::Trace(params.verbose),
965 params.message,
966 ));
967 })
968 .ok();
969 }
970 }
971 })
972 .detach();
973 }
974
975 fn shutdown_language_servers(
976 &mut self,
977 _cx: &mut Context<LspStore>,
978 ) -> impl Future<Output = ()> {
979 let shutdown_futures = self
980 .language_servers
981 .drain()
982 .map(|(_, server_state)| async {
983 use LanguageServerState::*;
984 match server_state {
985 Running { server, .. } => server.shutdown()?.await,
986 Starting { startup, .. } => startup.await?.shutdown()?.await,
987 }
988 })
989 .collect::<Vec<_>>();
990
991 async move {
992 futures::future::join_all(shutdown_futures).await;
993 }
994 }
995
996 fn language_servers_for_worktree(
997 &self,
998 worktree_id: WorktreeId,
999 ) -> impl Iterator<Item = &Arc<LanguageServer>> {
1000 self.language_server_ids
1001 .iter()
1002 .flat_map(move |((language_server_path, _), ids)| {
1003 ids.iter().filter_map(move |id| {
1004 if *language_server_path != worktree_id {
1005 return None;
1006 }
1007 if let Some(LanguageServerState::Running { server, .. }) =
1008 self.language_servers.get(id)
1009 {
1010 return Some(server);
1011 } else {
1012 None
1013 }
1014 })
1015 })
1016 }
1017
1018 fn language_server_ids_for_project_path(
1019 &self,
1020 project_path: ProjectPath,
1021 language: &Language,
1022 cx: &mut App,
1023 ) -> Vec<LanguageServerId> {
1024 let Some(worktree) = self
1025 .worktree_store
1026 .read(cx)
1027 .worktree_for_id(project_path.worktree_id, cx)
1028 else {
1029 return vec![];
1030 };
1031 let delegate = LocalLspAdapterDelegate::from_local_lsp(self, &worktree, cx);
1032 let root = self.lsp_tree.update(cx, |this, cx| {
1033 this.get(
1034 project_path,
1035 AdapterQuery::Language(&language.name()),
1036 delegate,
1037 cx,
1038 )
1039 .filter_map(|node| node.server_id())
1040 .collect::<Vec<_>>()
1041 });
1042
1043 root
1044 }
1045
1046 fn language_server_ids_for_buffer(
1047 &self,
1048 buffer: &Buffer,
1049 cx: &mut App,
1050 ) -> Vec<LanguageServerId> {
1051 if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
1052 let worktree_id = file.worktree_id(cx);
1053
1054 let path: Arc<Path> = file
1055 .path()
1056 .parent()
1057 .map(Arc::from)
1058 .unwrap_or_else(|| file.path().clone());
1059 let worktree_path = ProjectPath { worktree_id, path };
1060 self.language_server_ids_for_project_path(worktree_path, language, cx)
1061 } else {
1062 Vec::new()
1063 }
1064 }
1065
1066 fn language_servers_for_buffer<'a>(
1067 &'a self,
1068 buffer: &'a Buffer,
1069 cx: &'a mut App,
1070 ) -> impl Iterator<Item = (&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
1071 self.language_server_ids_for_buffer(buffer, cx)
1072 .into_iter()
1073 .filter_map(|server_id| match self.language_servers.get(&server_id)? {
1074 LanguageServerState::Running {
1075 adapter, server, ..
1076 } => Some((adapter, server)),
1077 _ => None,
1078 })
1079 }
1080
1081 fn primary_language_server_for_buffer<'a>(
1082 &'a self,
1083 buffer: &'a Buffer,
1084 cx: &'a mut App,
1085 ) -> Option<(&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
1086 // The list of language servers is ordered based on the `language_servers` setting
1087 // for each language, thus we can consider the first one in the list to be the
1088 // primary one.
1089 self.language_servers_for_buffer(buffer, cx).next()
1090 }
1091
1092 async fn execute_code_action_kind_locally(
1093 lsp_store: WeakEntity<LspStore>,
1094 mut buffers: Vec<Entity<Buffer>>,
1095 kind: CodeActionKind,
1096 push_to_history: bool,
1097 mut cx: AsyncApp,
1098 ) -> anyhow::Result<ProjectTransaction> {
1099 // Do not allow multiple concurrent code actions requests for the
1100 // same buffer.
1101 lsp_store.update(&mut cx, |this, cx| {
1102 let this = this.as_local_mut().unwrap();
1103 buffers.retain(|buffer| {
1104 this.buffers_being_formatted
1105 .insert(buffer.read(cx).remote_id())
1106 });
1107 })?;
1108 let _cleanup = defer({
1109 let this = lsp_store.clone();
1110 let mut cx = cx.clone();
1111 let buffers = &buffers;
1112 move || {
1113 this.update(&mut cx, |this, cx| {
1114 let this = this.as_local_mut().unwrap();
1115 for buffer in buffers {
1116 this.buffers_being_formatted
1117 .remove(&buffer.read(cx).remote_id());
1118 }
1119 })
1120 .ok();
1121 }
1122 });
1123 let mut project_transaction = ProjectTransaction::default();
1124
1125 for buffer in &buffers {
1126 let adapters_and_servers = lsp_store.update(&mut cx, |lsp_store, cx| {
1127 buffer.update(cx, |buffer, cx| {
1128 lsp_store
1129 .as_local()
1130 .unwrap()
1131 .language_servers_for_buffer(buffer, cx)
1132 .map(|(adapter, lsp)| (adapter.clone(), lsp.clone()))
1133 .collect::<Vec<_>>()
1134 })
1135 })?;
1136 Self::execute_code_actions_on_servers(
1137 &lsp_store,
1138 &adapters_and_servers,
1139 vec![kind.clone()],
1140 &buffer,
1141 push_to_history,
1142 &mut project_transaction,
1143 &mut cx,
1144 )
1145 .await?;
1146 }
1147 Ok(project_transaction)
1148 }
1149
1150 async fn format_locally(
1151 lsp_store: WeakEntity<LspStore>,
1152 mut buffers: Vec<FormattableBuffer>,
1153 push_to_history: bool,
1154 trigger: FormatTrigger,
1155 mut cx: AsyncApp,
1156 ) -> anyhow::Result<ProjectTransaction> {
1157 // Do not allow multiple concurrent formatting requests for the
1158 // same buffer.
1159 lsp_store.update(&mut cx, |this, cx| {
1160 let this = this.as_local_mut().unwrap();
1161 buffers.retain(|buffer| {
1162 this.buffers_being_formatted
1163 .insert(buffer.handle.read(cx).remote_id())
1164 });
1165 })?;
1166
1167 let _cleanup = defer({
1168 let this = lsp_store.clone();
1169 let mut cx = cx.clone();
1170 let buffers = &buffers;
1171 move || {
1172 this.update(&mut cx, |this, cx| {
1173 let this = this.as_local_mut().unwrap();
1174 for buffer in buffers {
1175 this.buffers_being_formatted
1176 .remove(&buffer.handle.read(cx).remote_id());
1177 }
1178 })
1179 .ok();
1180 }
1181 });
1182
1183 let mut project_transaction = ProjectTransaction::default();
1184 for buffer in &buffers {
1185 let adapters_and_servers = lsp_store.update(&mut cx, |lsp_store, cx| {
1186 buffer.handle.update(cx, |buffer, cx| {
1187 lsp_store
1188 .as_local()
1189 .unwrap()
1190 .language_servers_for_buffer(buffer, cx)
1191 .map(|(adapter, lsp)| (adapter.clone(), lsp.clone()))
1192 .collect::<Vec<_>>()
1193 })
1194 })?;
1195
1196 let settings = buffer.handle.update(&mut cx, |buffer, cx| {
1197 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
1198 .into_owned()
1199 })?;
1200
1201 let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save;
1202 let ensure_final_newline = settings.ensure_final_newline_on_save;
1203
1204 // First, format buffer's whitespace according to the settings.
1205 let trailing_whitespace_diff = if remove_trailing_whitespace {
1206 Some(
1207 buffer
1208 .handle
1209 .update(&mut cx, |b, cx| b.remove_trailing_whitespace(cx))?
1210 .await,
1211 )
1212 } else {
1213 None
1214 };
1215 let whitespace_transaction_id = buffer.handle.update(&mut cx, |buffer, cx| {
1216 buffer.finalize_last_transaction();
1217 buffer.start_transaction();
1218 if let Some(diff) = trailing_whitespace_diff {
1219 buffer.apply_diff(diff, cx);
1220 }
1221 if ensure_final_newline {
1222 buffer.ensure_final_newline(cx);
1223 }
1224 buffer.end_transaction(cx)
1225 })?;
1226
1227 let initial_transaction_id = whitespace_transaction_id;
1228
1229 // Apply the `code_actions_on_format` before we run the formatter.
1230 let code_actions = deserialize_code_actions(&settings.code_actions_on_format);
1231 #[allow(clippy::nonminimal_bool)]
1232 if !code_actions.is_empty()
1233 && !(trigger == FormatTrigger::Save && settings.format_on_save == FormatOnSave::Off)
1234 {
1235 Self::execute_code_actions_on_servers(
1236 &lsp_store,
1237 &adapters_and_servers,
1238 code_actions,
1239 &buffer.handle,
1240 push_to_history,
1241 &mut project_transaction,
1242 &mut cx,
1243 )
1244 .await?;
1245 }
1246
1247 let prettier_settings = buffer.handle.read_with(&cx, |buffer, cx| {
1248 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
1249 .prettier
1250 .clone()
1251 })?;
1252
1253 let formatters = match (trigger, &settings.format_on_save) {
1254 (FormatTrigger::Save, FormatOnSave::Off) => &[],
1255 (FormatTrigger::Save, FormatOnSave::List(formatters)) => formatters.as_ref(),
1256 (FormatTrigger::Manual, _) | (FormatTrigger::Save, FormatOnSave::On) => {
1257 match &settings.formatter {
1258 SelectedFormatter::Auto => {
1259 if prettier_settings.allowed {
1260 std::slice::from_ref(&Formatter::Prettier)
1261 } else {
1262 std::slice::from_ref(&Formatter::LanguageServer { name: None })
1263 }
1264 }
1265 SelectedFormatter::List(formatter_list) => formatter_list.as_ref(),
1266 }
1267 }
1268 };
1269 Self::execute_formatters(
1270 lsp_store.clone(),
1271 formatters,
1272 buffer,
1273 &settings,
1274 &adapters_and_servers,
1275 push_to_history,
1276 initial_transaction_id,
1277 &mut project_transaction,
1278 &mut cx,
1279 )
1280 .await?;
1281 }
1282
1283 Ok(project_transaction)
1284 }
1285
1286 #[allow(clippy::too_many_arguments)]
1287 async fn execute_formatters(
1288 lsp_store: WeakEntity<LspStore>,
1289 formatters: &[Formatter],
1290 buffer: &FormattableBuffer,
1291 settings: &LanguageSettings,
1292 adapters_and_servers: &[(Arc<CachedLspAdapter>, Arc<LanguageServer>)],
1293 push_to_history: bool,
1294 mut initial_transaction_id: Option<TransactionId>,
1295 project_transaction: &mut ProjectTransaction,
1296 cx: &mut AsyncApp,
1297 ) -> anyhow::Result<()> {
1298 let mut prev_transaction_id = initial_transaction_id;
1299
1300 for formatter in formatters {
1301 let operation = match formatter {
1302 Formatter::LanguageServer { name } => {
1303 let Some(language_server) = lsp_store.update(cx, |lsp_store, cx| {
1304 buffer.handle.update(cx, |buffer, cx| {
1305 lsp_store
1306 .as_local()
1307 .unwrap()
1308 .primary_language_server_for_buffer(buffer, cx)
1309 .map(|(_, lsp)| lsp.clone())
1310 })
1311 })?
1312 else {
1313 continue;
1314 };
1315 let Some(buffer_abs_path) = buffer.abs_path.as_ref() else {
1316 continue;
1317 };
1318
1319 let language_server = if let Some(name) = name {
1320 adapters_and_servers
1321 .iter()
1322 .find_map(|(adapter, server)| {
1323 adapter
1324 .name
1325 .0
1326 .as_ref()
1327 .eq(name.as_str())
1328 .then_some(server.clone())
1329 })
1330 .unwrap_or(language_server)
1331 } else {
1332 language_server
1333 };
1334
1335 let result = if let Some(ranges) = &buffer.ranges {
1336 Self::format_ranges_via_lsp(
1337 &lsp_store,
1338 &buffer.handle,
1339 ranges,
1340 buffer_abs_path,
1341 &language_server,
1342 settings,
1343 cx,
1344 )
1345 .await
1346 .context("failed to format ranges via language server")?
1347 } else {
1348 Self::format_via_lsp(
1349 &lsp_store,
1350 &buffer.handle,
1351 buffer_abs_path,
1352 &language_server,
1353 settings,
1354 cx,
1355 )
1356 .await
1357 .context("failed to format via language server")?
1358 };
1359
1360 Some(FormatOperation::Lsp(result))
1361 }
1362 Formatter::Prettier => {
1363 let prettier = lsp_store.update(cx, |lsp_store, _cx| {
1364 lsp_store.prettier_store().unwrap().downgrade()
1365 })?;
1366 prettier_store::format_with_prettier(&prettier, &buffer.handle, cx)
1367 .await
1368 .transpose()?
1369 }
1370 Formatter::External { command, arguments } => {
1371 Self::format_via_external_command(buffer, command, arguments.as_deref(), cx)
1372 .await
1373 .context(format!(
1374 "failed to format via external command {:?}",
1375 command
1376 ))?
1377 .map(FormatOperation::External)
1378 }
1379 Formatter::CodeActions(code_actions) => {
1380 let code_actions = deserialize_code_actions(code_actions);
1381 if !code_actions.is_empty() {
1382 Self::execute_code_actions_on_servers(
1383 &lsp_store,
1384 adapters_and_servers,
1385 code_actions,
1386 &buffer.handle,
1387 push_to_history,
1388 project_transaction,
1389 cx,
1390 )
1391 .await?;
1392 let buf_transaction_id =
1393 project_transaction.0.get(&buffer.handle).map(|t| t.id);
1394 // NOTE: same logic as in buffer.handle.update below
1395 if initial_transaction_id.is_none() {
1396 initial_transaction_id = buf_transaction_id;
1397 }
1398 if buf_transaction_id.is_some() {
1399 prev_transaction_id = buf_transaction_id;
1400 }
1401 }
1402 None
1403 }
1404 };
1405 let Some(operation) = operation else {
1406 continue;
1407 };
1408
1409 let should_continue_formatting = buffer.handle.update(cx, |b, cx| {
1410 // If a previous format succeeded and the buffer was edited while the language-specific
1411 // formatting information for this format was being computed, avoid applying the
1412 // language-specific formatting, because it can't be grouped with the previous formatting
1413 // in the undo history.
1414 let should_continue_formatting = match (prev_transaction_id, b.peek_undo_stack()) {
1415 (Some(prev_transaction_id), Some(last_history_entry)) => {
1416 let last_history_transaction_id = last_history_entry.transaction_id();
1417 let is_same_as_prev = last_history_transaction_id == prev_transaction_id;
1418 is_same_as_prev
1419 }
1420 (Some(_), None) => false,
1421 (_, _) => true,
1422 };
1423
1424 if should_continue_formatting {
1425 // Apply any language-specific formatting, and group the two formatting operations
1426 // in the buffer's undo history.
1427 let this_transaction_id = match operation {
1428 FormatOperation::Lsp(edits) => b.edit(edits, None, cx),
1429 FormatOperation::External(diff) => b.apply_diff(diff, cx),
1430 FormatOperation::Prettier(diff) => b.apply_diff(diff, cx),
1431 };
1432 if initial_transaction_id.is_none() {
1433 initial_transaction_id = this_transaction_id;
1434 }
1435 if this_transaction_id.is_some() {
1436 prev_transaction_id = this_transaction_id;
1437 }
1438 }
1439
1440 if let Some(transaction_id) = initial_transaction_id {
1441 b.group_until_transaction(transaction_id);
1442 } else if let Some(transaction) = project_transaction.0.get(&buffer.handle) {
1443 b.group_until_transaction(transaction.id)
1444 }
1445 return should_continue_formatting;
1446 })?;
1447 if !should_continue_formatting {
1448 break;
1449 }
1450 }
1451
1452 buffer.handle.update(cx, |b, _cx| {
1453 if let Some(transaction) = b.finalize_last_transaction().cloned() {
1454 if !push_to_history {
1455 b.forget_transaction(transaction.id);
1456 project_transaction
1457 .0
1458 .insert(buffer.handle.clone(), transaction);
1459 }
1460 }
1461 })?;
1462 return Ok(());
1463 }
1464
1465 pub async fn format_ranges_via_lsp(
1466 this: &WeakEntity<LspStore>,
1467 buffer_handle: &Entity<Buffer>,
1468 ranges: &[Range<Anchor>],
1469 abs_path: &Path,
1470 language_server: &Arc<LanguageServer>,
1471 settings: &LanguageSettings,
1472 cx: &mut AsyncApp,
1473 ) -> Result<Vec<(Range<Anchor>, Arc<str>)>> {
1474 let capabilities = &language_server.capabilities();
1475 let range_formatting_provider = capabilities.document_range_formatting_provider.as_ref();
1476 if range_formatting_provider.map_or(false, |provider| provider == &OneOf::Left(false)) {
1477 return Err(anyhow!(
1478 "{} language server does not support range formatting",
1479 language_server.name()
1480 ));
1481 }
1482
1483 let uri = lsp::Url::from_file_path(abs_path)
1484 .map_err(|_| anyhow!("failed to convert abs path to uri"))?;
1485 let text_document = lsp::TextDocumentIdentifier::new(uri);
1486
1487 let lsp_edits = {
1488 let mut lsp_ranges = Vec::new();
1489 this.update(cx, |_this, cx| {
1490 // TODO(#22930): In the case of formatting multibuffer selections, this buffer may
1491 // not have been sent to the language server. This seems like a fairly systemic
1492 // issue, though, the resolution probably is not specific to formatting.
1493 //
1494 // TODO: Instead of using current snapshot, should use the latest snapshot sent to
1495 // LSP.
1496 let snapshot = buffer_handle.read(cx).snapshot();
1497 for range in ranges {
1498 lsp_ranges.push(range_to_lsp(range.to_point_utf16(&snapshot))?);
1499 }
1500 anyhow::Ok(())
1501 })??;
1502
1503 let mut edits = None;
1504 for range in lsp_ranges {
1505 if let Some(mut edit) = language_server
1506 .request::<lsp::request::RangeFormatting>(lsp::DocumentRangeFormattingParams {
1507 text_document: text_document.clone(),
1508 range,
1509 options: lsp_command::lsp_formatting_options(settings),
1510 work_done_progress_params: Default::default(),
1511 })
1512 .await?
1513 {
1514 edits.get_or_insert_with(Vec::new).append(&mut edit);
1515 }
1516 }
1517 edits
1518 };
1519
1520 if let Some(lsp_edits) = lsp_edits {
1521 this.update(cx, |this, cx| {
1522 this.as_local_mut().unwrap().edits_from_lsp(
1523 &buffer_handle,
1524 lsp_edits,
1525 language_server.server_id(),
1526 None,
1527 cx,
1528 )
1529 })?
1530 .await
1531 } else {
1532 Ok(Vec::with_capacity(0))
1533 }
1534 }
1535
1536 #[allow(clippy::too_many_arguments)]
1537 async fn format_via_lsp(
1538 this: &WeakEntity<LspStore>,
1539 buffer: &Entity<Buffer>,
1540 abs_path: &Path,
1541 language_server: &Arc<LanguageServer>,
1542 settings: &LanguageSettings,
1543 cx: &mut AsyncApp,
1544 ) -> Result<Vec<(Range<Anchor>, Arc<str>)>> {
1545 let uri = lsp::Url::from_file_path(abs_path)
1546 .map_err(|_| anyhow!("failed to convert abs path to uri"))?;
1547 let text_document = lsp::TextDocumentIdentifier::new(uri);
1548 let capabilities = &language_server.capabilities();
1549
1550 let formatting_provider = capabilities.document_formatting_provider.as_ref();
1551 let range_formatting_provider = capabilities.document_range_formatting_provider.as_ref();
1552
1553 let lsp_edits = if matches!(formatting_provider, Some(p) if *p != OneOf::Left(false)) {
1554 language_server
1555 .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
1556 text_document,
1557 options: lsp_command::lsp_formatting_options(settings),
1558 work_done_progress_params: Default::default(),
1559 })
1560 .await?
1561 } else if matches!(range_formatting_provider, Some(p) if *p != OneOf::Left(false)) {
1562 let buffer_start = lsp::Position::new(0, 0);
1563 let buffer_end = buffer.update(cx, |b, _| point_to_lsp(b.max_point_utf16()))?;
1564 language_server
1565 .request::<lsp::request::RangeFormatting>(lsp::DocumentRangeFormattingParams {
1566 text_document: text_document.clone(),
1567 range: lsp::Range::new(buffer_start, buffer_end),
1568 options: lsp_command::lsp_formatting_options(settings),
1569 work_done_progress_params: Default::default(),
1570 })
1571 .await?
1572 } else {
1573 None
1574 };
1575
1576 if let Some(lsp_edits) = lsp_edits {
1577 this.update(cx, |this, cx| {
1578 this.as_local_mut().unwrap().edits_from_lsp(
1579 buffer,
1580 lsp_edits,
1581 language_server.server_id(),
1582 None,
1583 cx,
1584 )
1585 })?
1586 .await
1587 } else {
1588 Ok(Vec::with_capacity(0))
1589 }
1590 }
1591
1592 async fn format_via_external_command(
1593 buffer: &FormattableBuffer,
1594 command: &str,
1595 arguments: Option<&[String]>,
1596 cx: &mut AsyncApp,
1597 ) -> Result<Option<Diff>> {
1598 let working_dir_path = buffer.handle.update(cx, |buffer, cx| {
1599 let file = File::from_dyn(buffer.file())?;
1600 let worktree = file.worktree.read(cx);
1601 let mut worktree_path = worktree.abs_path().to_path_buf();
1602 if worktree.root_entry()?.is_file() {
1603 worktree_path.pop();
1604 }
1605 Some(worktree_path)
1606 })?;
1607
1608 let mut child = util::command::new_smol_command(command);
1609
1610 if let Some(buffer_env) = buffer.env.as_ref() {
1611 child.envs(buffer_env);
1612 }
1613
1614 if let Some(working_dir_path) = working_dir_path {
1615 child.current_dir(working_dir_path);
1616 }
1617
1618 if let Some(arguments) = arguments {
1619 child.args(arguments.iter().map(|arg| {
1620 if let Some(buffer_abs_path) = buffer.abs_path.as_ref() {
1621 arg.replace("{buffer_path}", &buffer_abs_path.to_string_lossy())
1622 } else {
1623 arg.replace("{buffer_path}", "Untitled")
1624 }
1625 }));
1626 }
1627
1628 let mut child = child
1629 .stdin(smol::process::Stdio::piped())
1630 .stdout(smol::process::Stdio::piped())
1631 .stderr(smol::process::Stdio::piped())
1632 .spawn()?;
1633
1634 let stdin = child
1635 .stdin
1636 .as_mut()
1637 .ok_or_else(|| anyhow!("failed to acquire stdin"))?;
1638 let text = buffer
1639 .handle
1640 .update(cx, |buffer, _| buffer.as_rope().clone())?;
1641 for chunk in text.chunks() {
1642 stdin.write_all(chunk.as_bytes()).await?;
1643 }
1644 stdin.flush().await?;
1645
1646 let output = child.output().await?;
1647 if !output.status.success() {
1648 return Err(anyhow!(
1649 "command failed with exit code {:?}:\nstdout: {}\nstderr: {}",
1650 output.status.code(),
1651 String::from_utf8_lossy(&output.stdout),
1652 String::from_utf8_lossy(&output.stderr),
1653 ));
1654 }
1655
1656 let stdout = String::from_utf8(output.stdout)?;
1657 Ok(Some(
1658 buffer
1659 .handle
1660 .update(cx, |buffer, cx| buffer.diff(stdout, cx))?
1661 .await,
1662 ))
1663 }
1664
1665 async fn try_resolve_code_action(
1666 lang_server: &LanguageServer,
1667 action: &mut CodeAction,
1668 ) -> anyhow::Result<()> {
1669 match &mut action.lsp_action {
1670 ActionVariant::Action(lsp_action) => {
1671 if GetCodeActions::can_resolve_actions(&lang_server.capabilities())
1672 && lsp_action.data.is_some()
1673 && (lsp_action.command.is_none() || lsp_action.edit.is_none())
1674 {
1675 *lsp_action = Box::new(
1676 lang_server
1677 .request::<lsp::request::CodeActionResolveRequest>(*lsp_action.clone())
1678 .await?,
1679 );
1680 }
1681 }
1682 ActionVariant::Command(_) => {}
1683 }
1684 anyhow::Ok(())
1685 }
1686
1687 fn initialize_buffer(&mut self, buffer_handle: &Entity<Buffer>, cx: &mut Context<LspStore>) {
1688 let buffer = buffer_handle.read(cx);
1689
1690 let file = buffer.file().cloned();
1691 let Some(file) = File::from_dyn(file.as_ref()) else {
1692 return;
1693 };
1694 if !file.is_local() {
1695 return;
1696 }
1697
1698 let worktree_id = file.worktree_id(cx);
1699 let language = buffer.language().cloned();
1700
1701 if let Some(diagnostics) = self.diagnostics.get(&worktree_id) {
1702 for (server_id, diagnostics) in
1703 diagnostics.get(file.path()).cloned().unwrap_or_default()
1704 {
1705 self.update_buffer_diagnostics(buffer_handle, server_id, None, diagnostics, cx)
1706 .log_err();
1707 }
1708 }
1709 let Some(language) = language else {
1710 return;
1711 };
1712 for adapter in self.languages.lsp_adapters(&language.name()) {
1713 let servers = self
1714 .language_server_ids
1715 .get(&(worktree_id, adapter.name.clone()));
1716 if let Some(server_ids) = servers {
1717 for server_id in server_ids {
1718 let server = self
1719 .language_servers
1720 .get(server_id)
1721 .and_then(|server_state| {
1722 if let LanguageServerState::Running { server, .. } = server_state {
1723 Some(server.clone())
1724 } else {
1725 None
1726 }
1727 });
1728 let server = match server {
1729 Some(server) => server,
1730 None => continue,
1731 };
1732
1733 buffer_handle.update(cx, |buffer, cx| {
1734 buffer.set_completion_triggers(
1735 server.server_id(),
1736 server
1737 .capabilities()
1738 .completion_provider
1739 .as_ref()
1740 .and_then(|provider| {
1741 provider
1742 .trigger_characters
1743 .as_ref()
1744 .map(|characters| characters.iter().cloned().collect())
1745 })
1746 .unwrap_or_default(),
1747 cx,
1748 );
1749 });
1750 }
1751 }
1752 }
1753 }
1754
1755 pub(crate) fn reset_buffer(&mut self, buffer: &Entity<Buffer>, old_file: &File, cx: &mut App) {
1756 buffer.update(cx, |buffer, cx| {
1757 let Some(language) = buffer.language() else {
1758 return;
1759 };
1760 let path = ProjectPath {
1761 worktree_id: old_file.worktree_id(cx),
1762 path: old_file.path.clone(),
1763 };
1764 for server_id in self.language_server_ids_for_project_path(path, language, cx) {
1765 buffer.update_diagnostics(server_id, DiagnosticSet::new([], buffer), cx);
1766 buffer.set_completion_triggers(server_id, Default::default(), cx);
1767 }
1768 });
1769 }
1770
1771 fn update_buffer_diagnostics(
1772 &mut self,
1773 buffer: &Entity<Buffer>,
1774 server_id: LanguageServerId,
1775 version: Option<i32>,
1776 mut diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
1777 cx: &mut Context<LspStore>,
1778 ) -> Result<()> {
1779 fn compare_diagnostics(a: &Diagnostic, b: &Diagnostic) -> Ordering {
1780 Ordering::Equal
1781 .then_with(|| b.is_primary.cmp(&a.is_primary))
1782 .then_with(|| a.is_disk_based.cmp(&b.is_disk_based))
1783 .then_with(|| a.severity.cmp(&b.severity))
1784 .then_with(|| a.message.cmp(&b.message))
1785 }
1786
1787 diagnostics.sort_unstable_by(|a, b| {
1788 Ordering::Equal
1789 .then_with(|| a.range.start.cmp(&b.range.start))
1790 .then_with(|| b.range.end.cmp(&a.range.end))
1791 .then_with(|| compare_diagnostics(&a.diagnostic, &b.diagnostic))
1792 });
1793
1794 let snapshot = self.buffer_snapshot_for_lsp_version(buffer, server_id, version, cx)?;
1795
1796 let edits_since_save = std::cell::LazyCell::new(|| {
1797 let saved_version = buffer.read(cx).saved_version();
1798 Patch::new(snapshot.edits_since::<PointUtf16>(saved_version).collect())
1799 });
1800
1801 let mut sanitized_diagnostics = Vec::new();
1802
1803 for entry in diagnostics {
1804 let start;
1805 let end;
1806 if entry.diagnostic.is_disk_based {
1807 // Some diagnostics are based on files on disk instead of buffers'
1808 // current contents. Adjust these diagnostics' ranges to reflect
1809 // any unsaved edits.
1810 start = Unclipped((*edits_since_save).old_to_new(entry.range.start.0));
1811 end = Unclipped((*edits_since_save).old_to_new(entry.range.end.0));
1812 } else {
1813 start = entry.range.start;
1814 end = entry.range.end;
1815 }
1816
1817 let mut range = snapshot.clip_point_utf16(start, Bias::Left)
1818 ..snapshot.clip_point_utf16(end, Bias::Right);
1819
1820 // Expand empty ranges by one codepoint
1821 if range.start == range.end {
1822 // This will be go to the next boundary when being clipped
1823 range.end.column += 1;
1824 range.end = snapshot.clip_point_utf16(Unclipped(range.end), Bias::Right);
1825 if range.start == range.end && range.end.column > 0 {
1826 range.start.column -= 1;
1827 range.start = snapshot.clip_point_utf16(Unclipped(range.start), Bias::Left);
1828 }
1829 }
1830
1831 sanitized_diagnostics.push(DiagnosticEntry {
1832 range,
1833 diagnostic: entry.diagnostic,
1834 });
1835 }
1836 drop(edits_since_save);
1837
1838 let set = DiagnosticSet::new(sanitized_diagnostics, &snapshot);
1839 buffer.update(cx, |buffer, cx| {
1840 buffer.update_diagnostics(server_id, set, cx)
1841 });
1842 Ok(())
1843 }
1844
1845 fn register_buffer_with_language_servers(
1846 &mut self,
1847 buffer_handle: &Entity<Buffer>,
1848 cx: &mut Context<LspStore>,
1849 ) {
1850 let buffer = buffer_handle.read(cx);
1851 let buffer_id = buffer.remote_id();
1852
1853 let Some(file) = File::from_dyn(buffer.file()) else {
1854 return;
1855 };
1856 if !file.is_local() {
1857 return;
1858 }
1859
1860 let abs_path = file.abs_path(cx);
1861 let Some(uri) = lsp::Url::from_file_path(&abs_path).log_err() else {
1862 return;
1863 };
1864 let initial_snapshot = buffer.text_snapshot();
1865 let worktree_id = file.worktree_id(cx);
1866
1867 let Some(language) = buffer.language().cloned() else {
1868 return;
1869 };
1870 let path: Arc<Path> = file
1871 .path()
1872 .parent()
1873 .map(Arc::from)
1874 .unwrap_or_else(|| file.path().clone());
1875 let Some(worktree) = self
1876 .worktree_store
1877 .read(cx)
1878 .worktree_for_id(worktree_id, cx)
1879 else {
1880 return;
1881 };
1882 let delegate = LocalLspAdapterDelegate::from_local_lsp(self, &worktree, cx);
1883 let servers = self.lsp_tree.clone().update(cx, |this, cx| {
1884 this.get(
1885 ProjectPath { worktree_id, path },
1886 AdapterQuery::Language(&language.name()),
1887 delegate.clone(),
1888 cx,
1889 )
1890 .collect::<Vec<_>>()
1891 });
1892 let servers = servers
1893 .into_iter()
1894 .filter_map(|server_node| {
1895 let server_id = server_node.server_id_or_init(
1896 |LaunchDisposition {
1897 server_name,
1898 attach,
1899 path,
1900 settings,
1901 }| match attach {
1902 language::Attach::InstancePerRoot => {
1903 // todo: handle instance per root proper.
1904 if let Some(server_ids) = self
1905 .language_server_ids
1906 .get(&(worktree_id, server_name.clone()))
1907 {
1908 server_ids.iter().cloned().next().unwrap()
1909 } else {
1910 let language_name = language.name();
1911
1912 self.start_language_server(
1913 &worktree,
1914 delegate.clone(),
1915 self.languages
1916 .lsp_adapters(&language_name)
1917 .into_iter()
1918 .find(|adapter| &adapter.name() == server_name)
1919 .expect("To find LSP adapter"),
1920 settings,
1921 cx,
1922 )
1923 }
1924 }
1925 language::Attach::Shared => {
1926 let uri = Url::from_file_path(
1927 worktree.read(cx).abs_path().join(&path.path),
1928 );
1929 let key = (worktree_id, server_name.clone());
1930 if !self.language_server_ids.contains_key(&key) {
1931 let language_name = language.name();
1932 self.start_language_server(
1933 &worktree,
1934 delegate.clone(),
1935 self.languages
1936 .lsp_adapters(&language_name)
1937 .into_iter()
1938 .find(|adapter| &adapter.name() == server_name)
1939 .expect("To find LSP adapter"),
1940 settings,
1941 cx,
1942 );
1943 }
1944 if let Some(server_ids) = self
1945 .language_server_ids
1946 .get(&key)
1947 {
1948 debug_assert_eq!(server_ids.len(), 1);
1949 let server_id = server_ids.iter().cloned().next().unwrap();
1950
1951 if let Some(state) = self.language_servers.get(&server_id) {
1952 if let Ok(uri) = uri {
1953 state.add_workspace_folder(uri);
1954 };
1955 }
1956 server_id
1957 } else {
1958 unreachable!("Language server ID should be available, as it's registered on demand")
1959 }
1960 }
1961 },
1962 )?;
1963 let server_state = self.language_servers.get(&server_id)?;
1964 if let LanguageServerState::Running { server, .. } = server_state {
1965 Some(server.clone())
1966 } else {
1967 None
1968 }
1969 })
1970 .collect::<Vec<_>>();
1971 for server in servers {
1972 buffer_handle.update(cx, |buffer, cx| {
1973 buffer.set_completion_triggers(
1974 server.server_id(),
1975 server
1976 .capabilities()
1977 .completion_provider
1978 .as_ref()
1979 .and_then(|provider| {
1980 provider
1981 .trigger_characters
1982 .as_ref()
1983 .map(|characters| characters.iter().cloned().collect())
1984 })
1985 .unwrap_or_default(),
1986 cx,
1987 );
1988 });
1989 }
1990 for adapter in self.languages.lsp_adapters(&language.name()) {
1991 let servers = self
1992 .language_server_ids
1993 .get(&(worktree_id, adapter.name.clone()))
1994 .map(|ids| {
1995 ids.iter().flat_map(|id| {
1996 self.language_servers.get(id).and_then(|server_state| {
1997 if let LanguageServerState::Running { server, .. } = server_state {
1998 Some(server.clone())
1999 } else {
2000 None
2001 }
2002 })
2003 })
2004 });
2005 let servers = match servers {
2006 Some(server) => server,
2007 None => continue,
2008 };
2009
2010 for server in servers {
2011 let snapshot = LspBufferSnapshot {
2012 version: 0,
2013 snapshot: initial_snapshot.clone(),
2014 };
2015 self.buffer_snapshots
2016 .entry(buffer_id)
2017 .or_default()
2018 .entry(server.server_id())
2019 .or_insert_with(|| {
2020 server.register_buffer(
2021 uri.clone(),
2022 adapter.language_id(&language.name()),
2023 0,
2024 initial_snapshot.text(),
2025 );
2026
2027 vec![snapshot]
2028 });
2029 }
2030 }
2031 }
2032
2033 pub(crate) fn unregister_old_buffer_from_language_servers(
2034 &mut self,
2035 buffer: &Entity<Buffer>,
2036 old_file: &File,
2037 cx: &mut App,
2038 ) {
2039 let old_path = match old_file.as_local() {
2040 Some(local) => local.abs_path(cx),
2041 None => return,
2042 };
2043
2044 let Ok(file_url) = lsp::Url::from_file_path(old_path.as_path()) else {
2045 debug_panic!(
2046 "`{}` is not parseable as an URI",
2047 old_path.to_string_lossy()
2048 );
2049 return;
2050 };
2051 self.unregister_buffer_from_language_servers(buffer, &file_url, cx);
2052 }
2053
2054 pub(crate) fn unregister_buffer_from_language_servers(
2055 &mut self,
2056 buffer: &Entity<Buffer>,
2057 file_url: &lsp::Url,
2058 cx: &mut App,
2059 ) {
2060 buffer.update(cx, |buffer, cx| {
2061 let _ = self.buffer_snapshots.remove(&buffer.remote_id());
2062
2063 for (_, language_server) in self.language_servers_for_buffer(buffer, cx) {
2064 language_server.unregister_buffer(file_url.clone());
2065 }
2066 });
2067 }
2068
2069 fn buffer_snapshot_for_lsp_version(
2070 &mut self,
2071 buffer: &Entity<Buffer>,
2072 server_id: LanguageServerId,
2073 version: Option<i32>,
2074 cx: &App,
2075 ) -> Result<TextBufferSnapshot> {
2076 const OLD_VERSIONS_TO_RETAIN: i32 = 10;
2077
2078 if let Some(version) = version {
2079 let buffer_id = buffer.read(cx).remote_id();
2080 let snapshots = if let Some(snapshots) = self
2081 .buffer_snapshots
2082 .get_mut(&buffer_id)
2083 .and_then(|m| m.get_mut(&server_id))
2084 {
2085 snapshots
2086 } else if version == 0 {
2087 // Some language servers report version 0 even if the buffer hasn't been opened yet.
2088 // We detect this case and treat it as if the version was `None`.
2089 return Ok(buffer.read(cx).text_snapshot());
2090 } else {
2091 return Err(anyhow!(
2092 "no snapshots found for buffer {buffer_id} and server {server_id}"
2093 ));
2094 };
2095
2096 let found_snapshot = snapshots
2097 .binary_search_by_key(&version, |e| e.version)
2098 .map(|ix| snapshots[ix].snapshot.clone())
2099 .map_err(|_| {
2100 anyhow!("snapshot not found for buffer {buffer_id} server {server_id} at version {version}")
2101 })?;
2102
2103 snapshots.retain(|snapshot| snapshot.version + OLD_VERSIONS_TO_RETAIN >= version);
2104 Ok(found_snapshot)
2105 } else {
2106 Ok((buffer.read(cx)).text_snapshot())
2107 }
2108 }
2109
2110 async fn execute_code_actions_on_servers(
2111 lsp_store: &WeakEntity<LspStore>,
2112 adapters_and_servers: &[(Arc<CachedLspAdapter>, Arc<LanguageServer>)],
2113 code_actions: Vec<lsp::CodeActionKind>,
2114 buffer: &Entity<Buffer>,
2115 push_to_history: bool,
2116 project_transaction: &mut ProjectTransaction,
2117 cx: &mut AsyncApp,
2118 ) -> Result<(), anyhow::Error> {
2119 for (lsp_adapter, language_server) in adapters_and_servers.iter() {
2120 let code_actions = code_actions.clone();
2121
2122 let actions = lsp_store
2123 .update(cx, move |this, cx| {
2124 let request = GetCodeActions {
2125 range: text::Anchor::MIN..text::Anchor::MAX,
2126 kinds: Some(code_actions),
2127 };
2128 let server = LanguageServerToQuery::Other(language_server.server_id());
2129 this.request_lsp(buffer.clone(), server, request, cx)
2130 })?
2131 .await?;
2132
2133 for mut action in actions {
2134 Self::try_resolve_code_action(language_server, &mut action)
2135 .await
2136 .context("resolving a formatting code action")?;
2137
2138 if let Some(edit) = action.lsp_action.edit() {
2139 if edit.changes.is_none() && edit.document_changes.is_none() {
2140 continue;
2141 }
2142
2143 let new = Self::deserialize_workspace_edit(
2144 lsp_store.upgrade().context("project dropped")?,
2145 edit.clone(),
2146 push_to_history,
2147 lsp_adapter.clone(),
2148 language_server.clone(),
2149 cx,
2150 )
2151 .await?;
2152 project_transaction.0.extend(new.0);
2153 }
2154
2155 if let Some(command) = action.lsp_action.command() {
2156 let server_capabilities = language_server.capabilities();
2157 let available_commands = server_capabilities
2158 .execute_command_provider
2159 .as_ref()
2160 .map(|options| options.commands.as_slice())
2161 .unwrap_or_default();
2162 if available_commands.contains(&command.command) {
2163 lsp_store.update(cx, |lsp_store, _| {
2164 if let LspStoreMode::Local(mode) = &mut lsp_store.mode {
2165 mode.last_workspace_edits_by_language_server
2166 .remove(&language_server.server_id());
2167 }
2168 })?;
2169
2170 language_server
2171 .request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
2172 command: command.command.clone(),
2173 arguments: command.arguments.clone().unwrap_or_default(),
2174 ..Default::default()
2175 })
2176 .await?;
2177
2178 lsp_store.update(cx, |this, _| {
2179 if let LspStoreMode::Local(mode) = &mut this.mode {
2180 project_transaction.0.extend(
2181 mode.last_workspace_edits_by_language_server
2182 .remove(&language_server.server_id())
2183 .unwrap_or_default()
2184 .0,
2185 )
2186 }
2187 })?;
2188 } else {
2189 log::warn!("Cannot execute a command {} not listed in the language server capabilities", command.command)
2190 }
2191 }
2192 }
2193 }
2194
2195 Ok(())
2196 }
2197
2198 pub async fn deserialize_text_edits(
2199 this: Entity<LspStore>,
2200 buffer_to_edit: Entity<Buffer>,
2201 edits: Vec<lsp::TextEdit>,
2202 push_to_history: bool,
2203 _: Arc<CachedLspAdapter>,
2204 language_server: Arc<LanguageServer>,
2205 cx: &mut AsyncApp,
2206 ) -> Result<Option<Transaction>> {
2207 let edits = this
2208 .update(cx, |this, cx| {
2209 this.as_local_mut().unwrap().edits_from_lsp(
2210 &buffer_to_edit,
2211 edits,
2212 language_server.server_id(),
2213 None,
2214 cx,
2215 )
2216 })?
2217 .await?;
2218
2219 let transaction = buffer_to_edit.update(cx, |buffer, cx| {
2220 buffer.finalize_last_transaction();
2221 buffer.start_transaction();
2222 for (range, text) in edits {
2223 buffer.edit([(range, text)], None, cx);
2224 }
2225
2226 if buffer.end_transaction(cx).is_some() {
2227 let transaction = buffer.finalize_last_transaction().unwrap().clone();
2228 if !push_to_history {
2229 buffer.forget_transaction(transaction.id);
2230 }
2231 Some(transaction)
2232 } else {
2233 None
2234 }
2235 })?;
2236
2237 Ok(transaction)
2238 }
2239
2240 #[allow(clippy::type_complexity)]
2241 pub(crate) fn edits_from_lsp(
2242 &mut self,
2243 buffer: &Entity<Buffer>,
2244 lsp_edits: impl 'static + Send + IntoIterator<Item = lsp::TextEdit>,
2245 server_id: LanguageServerId,
2246 version: Option<i32>,
2247 cx: &mut Context<LspStore>,
2248 ) -> Task<Result<Vec<(Range<Anchor>, Arc<str>)>>> {
2249 let snapshot = self.buffer_snapshot_for_lsp_version(buffer, server_id, version, cx);
2250 cx.background_spawn(async move {
2251 let snapshot = snapshot?;
2252 let mut lsp_edits = lsp_edits
2253 .into_iter()
2254 .map(|edit| (range_from_lsp(edit.range), edit.new_text))
2255 .collect::<Vec<_>>();
2256 lsp_edits.sort_by_key(|(range, _)| range.start);
2257
2258 let mut lsp_edits = lsp_edits.into_iter().peekable();
2259 let mut edits = Vec::new();
2260 while let Some((range, mut new_text)) = lsp_edits.next() {
2261 // Clip invalid ranges provided by the language server.
2262 let mut range = snapshot.clip_point_utf16(range.start, Bias::Left)
2263 ..snapshot.clip_point_utf16(range.end, Bias::Left);
2264
2265 // Combine any LSP edits that are adjacent.
2266 //
2267 // Also, combine LSP edits that are separated from each other by only
2268 // a newline. This is important because for some code actions,
2269 // Rust-analyzer rewrites the entire buffer via a series of edits that
2270 // are separated by unchanged newline characters.
2271 //
2272 // In order for the diffing logic below to work properly, any edits that
2273 // cancel each other out must be combined into one.
2274 while let Some((next_range, next_text)) = lsp_edits.peek() {
2275 if next_range.start.0 > range.end {
2276 if next_range.start.0.row > range.end.row + 1
2277 || next_range.start.0.column > 0
2278 || snapshot.clip_point_utf16(
2279 Unclipped(PointUtf16::new(range.end.row, u32::MAX)),
2280 Bias::Left,
2281 ) > range.end
2282 {
2283 break;
2284 }
2285 new_text.push('\n');
2286 }
2287 range.end = snapshot.clip_point_utf16(next_range.end, Bias::Left);
2288 new_text.push_str(next_text);
2289 lsp_edits.next();
2290 }
2291
2292 // For multiline edits, perform a diff of the old and new text so that
2293 // we can identify the changes more precisely, preserving the locations
2294 // of any anchors positioned in the unchanged regions.
2295 if range.end.row > range.start.row {
2296 let offset = range.start.to_offset(&snapshot);
2297 let old_text = snapshot.text_for_range(range).collect::<String>();
2298 let range_edits = language::text_diff(old_text.as_str(), &new_text);
2299 edits.extend(range_edits.into_iter().map(|(range, replacement)| {
2300 (
2301 snapshot.anchor_after(offset + range.start)
2302 ..snapshot.anchor_before(offset + range.end),
2303 replacement,
2304 )
2305 }));
2306 } else if range.end == range.start {
2307 let anchor = snapshot.anchor_after(range.start);
2308 edits.push((anchor..anchor, new_text.into()));
2309 } else {
2310 let edit_start = snapshot.anchor_after(range.start);
2311 let edit_end = snapshot.anchor_before(range.end);
2312 edits.push((edit_start..edit_end, new_text.into()));
2313 }
2314 }
2315
2316 Ok(edits)
2317 })
2318 }
2319
2320 pub(crate) async fn deserialize_workspace_edit(
2321 this: Entity<LspStore>,
2322 edit: lsp::WorkspaceEdit,
2323 push_to_history: bool,
2324 lsp_adapter: Arc<CachedLspAdapter>,
2325 language_server: Arc<LanguageServer>,
2326 cx: &mut AsyncApp,
2327 ) -> Result<ProjectTransaction> {
2328 let fs = this.read_with(cx, |this, _| this.as_local().unwrap().fs.clone())?;
2329
2330 let mut operations = Vec::new();
2331 if let Some(document_changes) = edit.document_changes {
2332 match document_changes {
2333 lsp::DocumentChanges::Edits(edits) => {
2334 operations.extend(edits.into_iter().map(lsp::DocumentChangeOperation::Edit))
2335 }
2336 lsp::DocumentChanges::Operations(ops) => operations = ops,
2337 }
2338 } else if let Some(changes) = edit.changes {
2339 operations.extend(changes.into_iter().map(|(uri, edits)| {
2340 lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
2341 text_document: lsp::OptionalVersionedTextDocumentIdentifier {
2342 uri,
2343 version: None,
2344 },
2345 edits: edits.into_iter().map(Edit::Plain).collect(),
2346 })
2347 }));
2348 }
2349
2350 let mut project_transaction = ProjectTransaction::default();
2351 for operation in operations {
2352 match operation {
2353 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(op)) => {
2354 let abs_path = op
2355 .uri
2356 .to_file_path()
2357 .map_err(|_| anyhow!("can't convert URI to path"))?;
2358
2359 if let Some(parent_path) = abs_path.parent() {
2360 fs.create_dir(parent_path).await?;
2361 }
2362 if abs_path.ends_with("/") {
2363 fs.create_dir(&abs_path).await?;
2364 } else {
2365 fs.create_file(
2366 &abs_path,
2367 op.options
2368 .map(|options| fs::CreateOptions {
2369 overwrite: options.overwrite.unwrap_or(false),
2370 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
2371 })
2372 .unwrap_or_default(),
2373 )
2374 .await?;
2375 }
2376 }
2377
2378 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Rename(op)) => {
2379 let source_abs_path = op
2380 .old_uri
2381 .to_file_path()
2382 .map_err(|_| anyhow!("can't convert URI to path"))?;
2383 let target_abs_path = op
2384 .new_uri
2385 .to_file_path()
2386 .map_err(|_| anyhow!("can't convert URI to path"))?;
2387 fs.rename(
2388 &source_abs_path,
2389 &target_abs_path,
2390 op.options
2391 .map(|options| fs::RenameOptions {
2392 overwrite: options.overwrite.unwrap_or(false),
2393 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
2394 })
2395 .unwrap_or_default(),
2396 )
2397 .await?;
2398 }
2399
2400 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Delete(op)) => {
2401 let abs_path = op
2402 .uri
2403 .to_file_path()
2404 .map_err(|_| anyhow!("can't convert URI to path"))?;
2405 let options = op
2406 .options
2407 .map(|options| fs::RemoveOptions {
2408 recursive: options.recursive.unwrap_or(false),
2409 ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false),
2410 })
2411 .unwrap_or_default();
2412 if abs_path.ends_with("/") {
2413 fs.remove_dir(&abs_path, options).await?;
2414 } else {
2415 fs.remove_file(&abs_path, options).await?;
2416 }
2417 }
2418
2419 lsp::DocumentChangeOperation::Edit(op) => {
2420 let buffer_to_edit = this
2421 .update(cx, |this, cx| {
2422 this.open_local_buffer_via_lsp(
2423 op.text_document.uri.clone(),
2424 language_server.server_id(),
2425 lsp_adapter.name.clone(),
2426 cx,
2427 )
2428 })?
2429 .await?;
2430
2431 let edits = this
2432 .update(cx, |this, cx| {
2433 let path = buffer_to_edit.read(cx).project_path(cx);
2434 let active_entry = this.active_entry;
2435 let is_active_entry = path.clone().map_or(false, |project_path| {
2436 this.worktree_store
2437 .read(cx)
2438 .entry_for_path(&project_path, cx)
2439 .map_or(false, |entry| Some(entry.id) == active_entry)
2440 });
2441 let local = this.as_local_mut().unwrap();
2442
2443 let (mut edits, mut snippet_edits) = (vec![], vec![]);
2444 for edit in op.edits {
2445 match edit {
2446 Edit::Plain(edit) => {
2447 if !edits.contains(&edit) {
2448 edits.push(edit)
2449 }
2450 }
2451 Edit::Annotated(edit) => {
2452 if !edits.contains(&edit.text_edit) {
2453 edits.push(edit.text_edit)
2454 }
2455 }
2456 Edit::Snippet(edit) => {
2457 let Ok(snippet) = Snippet::parse(&edit.snippet.value)
2458 else {
2459 continue;
2460 };
2461
2462 if is_active_entry {
2463 snippet_edits.push((edit.range, snippet));
2464 } else {
2465 // Since this buffer is not focused, apply a normal edit.
2466 let new_edit = TextEdit {
2467 range: edit.range,
2468 new_text: snippet.text,
2469 };
2470 if !edits.contains(&new_edit) {
2471 edits.push(new_edit);
2472 }
2473 }
2474 }
2475 }
2476 }
2477 if !snippet_edits.is_empty() {
2478 let buffer_id = buffer_to_edit.read(cx).remote_id();
2479 let version = if let Some(buffer_version) = op.text_document.version
2480 {
2481 local
2482 .buffer_snapshot_for_lsp_version(
2483 &buffer_to_edit,
2484 language_server.server_id(),
2485 Some(buffer_version),
2486 cx,
2487 )
2488 .ok()
2489 .map(|snapshot| snapshot.version)
2490 } else {
2491 Some(buffer_to_edit.read(cx).saved_version().clone())
2492 };
2493
2494 let most_recent_edit = version.and_then(|version| {
2495 version.iter().max_by_key(|timestamp| timestamp.value)
2496 });
2497 // Check if the edit that triggered that edit has been made by this participant.
2498
2499 if let Some(most_recent_edit) = most_recent_edit {
2500 cx.emit(LspStoreEvent::SnippetEdit {
2501 buffer_id,
2502 edits: snippet_edits,
2503 most_recent_edit,
2504 });
2505 }
2506 }
2507
2508 local.edits_from_lsp(
2509 &buffer_to_edit,
2510 edits,
2511 language_server.server_id(),
2512 op.text_document.version,
2513 cx,
2514 )
2515 })?
2516 .await?;
2517
2518 let transaction = buffer_to_edit.update(cx, |buffer, cx| {
2519 buffer.finalize_last_transaction();
2520 buffer.start_transaction();
2521 for (range, text) in edits {
2522 buffer.edit([(range, text)], None, cx);
2523 }
2524 let transaction = if buffer.end_transaction(cx).is_some() {
2525 let transaction = buffer.finalize_last_transaction().unwrap().clone();
2526 if !push_to_history {
2527 buffer.forget_transaction(transaction.id);
2528 }
2529 Some(transaction)
2530 } else {
2531 None
2532 };
2533
2534 transaction
2535 })?;
2536 if let Some(transaction) = transaction {
2537 project_transaction.0.insert(buffer_to_edit, transaction);
2538 }
2539 }
2540 }
2541 }
2542
2543 Ok(project_transaction)
2544 }
2545
2546 async fn on_lsp_workspace_edit(
2547 this: WeakEntity<LspStore>,
2548 params: lsp::ApplyWorkspaceEditParams,
2549 server_id: LanguageServerId,
2550 adapter: Arc<CachedLspAdapter>,
2551 mut cx: AsyncApp,
2552 ) -> Result<lsp::ApplyWorkspaceEditResponse> {
2553 let this = this
2554 .upgrade()
2555 .ok_or_else(|| anyhow!("project project closed"))?;
2556 let language_server = this
2557 .update(&mut cx, |this, _| this.language_server_for_id(server_id))?
2558 .ok_or_else(|| anyhow!("language server not found"))?;
2559 let transaction = Self::deserialize_workspace_edit(
2560 this.clone(),
2561 params.edit,
2562 true,
2563 adapter.clone(),
2564 language_server.clone(),
2565 &mut cx,
2566 )
2567 .await
2568 .log_err();
2569 this.update(&mut cx, |this, _| {
2570 if let Some(transaction) = transaction {
2571 this.as_local_mut()
2572 .unwrap()
2573 .last_workspace_edits_by_language_server
2574 .insert(server_id, transaction);
2575 }
2576 })?;
2577 Ok(lsp::ApplyWorkspaceEditResponse {
2578 applied: true,
2579 failed_change: None,
2580 failure_reason: None,
2581 })
2582 }
2583
2584 fn remove_worktree(
2585 &mut self,
2586 id_to_remove: WorktreeId,
2587 cx: &mut Context<'_, LspStore>,
2588 ) -> Vec<LanguageServerId> {
2589 self.diagnostics.remove(&id_to_remove);
2590 self.prettier_store.update(cx, |prettier_store, cx| {
2591 prettier_store.remove_worktree(id_to_remove, cx);
2592 });
2593
2594 let mut servers_to_remove = BTreeMap::default();
2595 let mut servers_to_preserve = HashSet::default();
2596 for ((path, server_name), ref server_ids) in &self.language_server_ids {
2597 if *path == id_to_remove {
2598 servers_to_remove.extend(server_ids.iter().map(|id| (*id, server_name.clone())));
2599 } else {
2600 servers_to_preserve.extend(server_ids.iter().cloned());
2601 }
2602 }
2603 servers_to_remove.retain(|server_id, _| !servers_to_preserve.contains(server_id));
2604
2605 for (server_id_to_remove, _) in &servers_to_remove {
2606 self.language_server_ids
2607 .values_mut()
2608 .for_each(|server_ids| {
2609 server_ids.remove(server_id_to_remove);
2610 });
2611 self.language_server_watched_paths
2612 .remove(&server_id_to_remove);
2613 self.language_server_paths_watched_for_rename
2614 .remove(&server_id_to_remove);
2615 self.last_workspace_edits_by_language_server
2616 .remove(&server_id_to_remove);
2617 self.language_servers.remove(&server_id_to_remove);
2618 cx.emit(LspStoreEvent::LanguageServerRemoved(*server_id_to_remove));
2619 }
2620 servers_to_remove.into_keys().collect()
2621 }
2622
2623 fn rebuild_watched_paths_inner<'a>(
2624 &'a self,
2625 language_server_id: LanguageServerId,
2626 watchers: impl Iterator<Item = &'a FileSystemWatcher>,
2627 cx: &mut Context<LspStore>,
2628 ) -> LanguageServerWatchedPathsBuilder {
2629 let worktrees = self
2630 .worktree_store
2631 .read(cx)
2632 .worktrees()
2633 .filter_map(|worktree| {
2634 self.language_servers_for_worktree(worktree.read(cx).id())
2635 .find(|server| server.server_id() == language_server_id)
2636 .map(|_| worktree)
2637 })
2638 .collect::<Vec<_>>();
2639
2640 let mut worktree_globs = HashMap::default();
2641 let mut abs_globs = HashMap::default();
2642 log::trace!(
2643 "Processing new watcher paths for language server with id {}",
2644 language_server_id
2645 );
2646
2647 enum PathToWatch {
2648 Worktree {
2649 literal_prefix: Arc<Path>,
2650 pattern: String,
2651 },
2652 Absolute {
2653 path: Arc<Path>,
2654 pattern: String,
2655 },
2656 }
2657 for watcher in watchers {
2658 let mut found_host = false;
2659 for worktree in &worktrees {
2660 let glob_is_inside_worktree = worktree.update(cx, |tree, _| {
2661 let worktree_root_path = tree.abs_path();
2662 let path_to_watch = match &watcher.glob_pattern {
2663 lsp::GlobPattern::String(s) => {
2664 let watcher_path = SanitizedPath::from(s);
2665 match watcher_path.as_path().strip_prefix(&worktree_root_path) {
2666 Ok(relative) => {
2667 let pattern = relative.to_string_lossy().to_string();
2668 let literal_prefix = glob_literal_prefix(relative).into();
2669
2670 PathToWatch::Worktree {
2671 literal_prefix,
2672 pattern,
2673 }
2674 }
2675 Err(_) => {
2676 let path = glob_literal_prefix(watcher_path.as_path());
2677 let pattern = watcher_path
2678 .as_path()
2679 .strip_prefix(&path)
2680 .map(|p| p.to_string_lossy().to_string())
2681 .unwrap_or_else(|e| {
2682 debug_panic!(
2683 "Failed to strip prefix for string pattern: {}, with prefix: {}, with error: {}",
2684 s,
2685 path.display(),
2686 e
2687 );
2688 watcher_path.as_path().to_string_lossy().to_string()
2689 });
2690 let path = if path.components().next().is_none() {
2691 worktree_root_path.clone()
2692 } else {
2693 path.into()
2694 };
2695
2696 PathToWatch::Absolute { path, pattern }
2697 }
2698 }
2699 }
2700 lsp::GlobPattern::Relative(rp) => {
2701 let Ok(mut base_uri) = match &rp.base_uri {
2702 lsp::OneOf::Left(workspace_folder) => &workspace_folder.uri,
2703 lsp::OneOf::Right(base_uri) => base_uri,
2704 }
2705 .to_file_path() else {
2706 return false;
2707 };
2708
2709 match base_uri.strip_prefix(&worktree_root_path) {
2710 Ok(relative) => {
2711 let mut literal_prefix = relative.to_owned();
2712 literal_prefix
2713 .push(glob_literal_prefix(Path::new(&rp.pattern)));
2714
2715 PathToWatch::Worktree {
2716 literal_prefix: literal_prefix.into(),
2717 pattern: rp.pattern.clone(),
2718 }
2719 }
2720 Err(_) => {
2721 let path = glob_literal_prefix(Path::new(&rp.pattern));
2722 let pattern = Path::new(&rp.pattern)
2723 .strip_prefix(&path)
2724 .map(|p| p.to_string_lossy().to_string())
2725 .unwrap_or_else(|e| {
2726 debug_panic!(
2727 "Failed to strip prefix for relative pattern: {}, with prefix: {}, with error: {}",
2728 rp.pattern,
2729 path.display(),
2730 e
2731 );
2732 rp.pattern.clone()
2733 });
2734 base_uri.push(path);
2735
2736 let path = if base_uri.components().next().is_none() {
2737 debug_panic!("base_uri is empty, {}", base_uri.display());
2738 worktree_root_path.clone()
2739 } else {
2740 base_uri.into()
2741 };
2742 PathToWatch::Absolute { path, pattern }
2743 }
2744 }
2745 }
2746 };
2747 match path_to_watch {
2748 PathToWatch::Worktree {
2749 literal_prefix,
2750 pattern,
2751 } => {
2752 if let Some((tree, glob)) =
2753 tree.as_local_mut().zip(Glob::new(&pattern).log_err())
2754 {
2755 tree.add_path_prefix_to_scan(literal_prefix);
2756 worktree_globs
2757 .entry(tree.id())
2758 .or_insert_with(GlobSetBuilder::new)
2759 .add(glob);
2760 } else {
2761 return false;
2762 }
2763 }
2764 PathToWatch::Absolute { path, pattern } => {
2765 if let Some(glob) = Glob::new(&pattern).log_err() {
2766 abs_globs
2767 .entry(path)
2768 .or_insert_with(GlobSetBuilder::new)
2769 .add(glob);
2770 }
2771 }
2772 }
2773 true
2774 });
2775 if glob_is_inside_worktree {
2776 log::trace!(
2777 "Watcher pattern `{}` has been attached to the worktree at `{}`",
2778 serde_json::to_string(&watcher.glob_pattern).unwrap(),
2779 worktree.read(cx).abs_path().display()
2780 );
2781 found_host = true;
2782 }
2783 }
2784 if !found_host {
2785 log::error!(
2786 "Watcher pattern `{}` has not been attached to any worktree or absolute path",
2787 serde_json::to_string(&watcher.glob_pattern).unwrap()
2788 )
2789 }
2790 }
2791
2792 let mut watch_builder = LanguageServerWatchedPathsBuilder::default();
2793 for (worktree_id, builder) in worktree_globs {
2794 if let Ok(globset) = builder.build() {
2795 watch_builder.watch_worktree(worktree_id, globset);
2796 }
2797 }
2798 for (abs_path, builder) in abs_globs {
2799 if let Ok(globset) = builder.build() {
2800 watch_builder.watch_abs_path(abs_path, globset);
2801 }
2802 }
2803 watch_builder
2804 }
2805
2806 fn rebuild_watched_paths(
2807 &mut self,
2808 language_server_id: LanguageServerId,
2809 cx: &mut Context<LspStore>,
2810 ) {
2811 let Some(watchers) = self
2812 .language_server_watcher_registrations
2813 .get(&language_server_id)
2814 else {
2815 return;
2816 };
2817
2818 let watch_builder =
2819 self.rebuild_watched_paths_inner(language_server_id, watchers.values().flatten(), cx);
2820 let watcher = watch_builder.build(self.fs.clone(), language_server_id, cx);
2821 self.language_server_watched_paths
2822 .insert(language_server_id, watcher);
2823
2824 cx.notify();
2825 }
2826
2827 fn on_lsp_did_change_watched_files(
2828 &mut self,
2829 language_server_id: LanguageServerId,
2830 registration_id: &str,
2831 params: DidChangeWatchedFilesRegistrationOptions,
2832 cx: &mut Context<LspStore>,
2833 ) {
2834 let registrations = self
2835 .language_server_watcher_registrations
2836 .entry(language_server_id)
2837 .or_default();
2838
2839 registrations.insert(registration_id.to_string(), params.watchers);
2840
2841 self.rebuild_watched_paths(language_server_id, cx);
2842 }
2843
2844 fn on_lsp_unregister_did_change_watched_files(
2845 &mut self,
2846 language_server_id: LanguageServerId,
2847 registration_id: &str,
2848 cx: &mut Context<LspStore>,
2849 ) {
2850 let registrations = self
2851 .language_server_watcher_registrations
2852 .entry(language_server_id)
2853 .or_default();
2854
2855 if registrations.remove(registration_id).is_some() {
2856 log::info!(
2857 "language server {}: unregistered workspace/DidChangeWatchedFiles capability with id {}",
2858 language_server_id,
2859 registration_id
2860 );
2861 } else {
2862 log::warn!(
2863 "language server {}: failed to unregister workspace/DidChangeWatchedFiles capability with id {}. not registered.",
2864 language_server_id,
2865 registration_id
2866 );
2867 }
2868
2869 self.rebuild_watched_paths(language_server_id, cx);
2870 }
2871}
2872
2873#[derive(Debug)]
2874pub struct FormattableBuffer {
2875 handle: Entity<Buffer>,
2876 abs_path: Option<PathBuf>,
2877 env: Option<HashMap<String, String>>,
2878 ranges: Option<Vec<Range<Anchor>>>,
2879}
2880
2881pub struct RemoteLspStore {
2882 upstream_client: Option<AnyProtoClient>,
2883 upstream_project_id: u64,
2884}
2885
2886#[allow(clippy::large_enum_variant)]
2887pub(crate) enum LspStoreMode {
2888 Local(LocalLspStore), // ssh host and collab host
2889 Remote(RemoteLspStore), // collab guest
2890}
2891
2892impl LspStoreMode {
2893 fn is_local(&self) -> bool {
2894 matches!(self, LspStoreMode::Local(_))
2895 }
2896}
2897
2898pub struct LspStore {
2899 mode: LspStoreMode,
2900 last_formatting_failure: Option<String>,
2901 downstream_client: Option<(AnyProtoClient, u64)>,
2902 nonce: u128,
2903 buffer_store: Entity<BufferStore>,
2904 worktree_store: Entity<WorktreeStore>,
2905 toolchain_store: Option<Entity<ToolchainStore>>,
2906 pub languages: Arc<LanguageRegistry>,
2907 pub language_server_statuses: BTreeMap<LanguageServerId, LanguageServerStatus>,
2908 active_entry: Option<ProjectEntryId>,
2909 _maintain_workspace_config: (Task<Result<()>>, watch::Sender<()>),
2910 _maintain_buffer_languages: Task<()>,
2911 diagnostic_summaries:
2912 HashMap<WorktreeId, HashMap<Arc<Path>, HashMap<LanguageServerId, DiagnosticSummary>>>,
2913}
2914
2915pub enum LspStoreEvent {
2916 LanguageServerAdded(LanguageServerId, LanguageServerName, Option<WorktreeId>),
2917 LanguageServerRemoved(LanguageServerId),
2918 LanguageServerUpdate {
2919 language_server_id: LanguageServerId,
2920 message: proto::update_language_server::Variant,
2921 },
2922 LanguageServerLog(LanguageServerId, LanguageServerLogType, String),
2923 LanguageServerPrompt(LanguageServerPromptRequest),
2924 LanguageDetected {
2925 buffer: Entity<Buffer>,
2926 new_language: Option<Arc<Language>>,
2927 },
2928 Notification(String),
2929 RefreshInlayHints,
2930 DiagnosticsUpdated {
2931 language_server_id: LanguageServerId,
2932 path: ProjectPath,
2933 },
2934 DiskBasedDiagnosticsStarted {
2935 language_server_id: LanguageServerId,
2936 },
2937 DiskBasedDiagnosticsFinished {
2938 language_server_id: LanguageServerId,
2939 },
2940 SnippetEdit {
2941 buffer_id: BufferId,
2942 edits: Vec<(lsp::Range, Snippet)>,
2943 most_recent_edit: clock::Lamport,
2944 },
2945}
2946
2947#[derive(Clone, Debug, Serialize)]
2948pub struct LanguageServerStatus {
2949 pub name: String,
2950 pub pending_work: BTreeMap<String, LanguageServerProgress>,
2951 pub has_pending_diagnostic_updates: bool,
2952 progress_tokens: HashSet<String>,
2953}
2954
2955#[derive(Clone, Debug)]
2956struct CoreSymbol {
2957 pub language_server_name: LanguageServerName,
2958 pub source_worktree_id: WorktreeId,
2959 pub source_language_server_id: LanguageServerId,
2960 pub path: ProjectPath,
2961 pub name: String,
2962 pub kind: lsp::SymbolKind,
2963 pub range: Range<Unclipped<PointUtf16>>,
2964 pub signature: [u8; 32],
2965}
2966
2967impl LspStore {
2968 pub fn init(client: &AnyProtoClient) {
2969 client.add_entity_request_handler(Self::handle_multi_lsp_query);
2970 client.add_entity_request_handler(Self::handle_restart_language_servers);
2971 client.add_entity_request_handler(Self::handle_cancel_language_server_work);
2972 client.add_entity_message_handler(Self::handle_start_language_server);
2973 client.add_entity_message_handler(Self::handle_update_language_server);
2974 client.add_entity_message_handler(Self::handle_language_server_log);
2975 client.add_entity_message_handler(Self::handle_update_diagnostic_summary);
2976 client.add_entity_request_handler(Self::handle_format_buffers);
2977 client.add_entity_request_handler(Self::handle_apply_code_action_kind);
2978 client.add_entity_request_handler(Self::handle_resolve_completion_documentation);
2979 client.add_entity_request_handler(Self::handle_apply_code_action);
2980 client.add_entity_request_handler(Self::handle_inlay_hints);
2981 client.add_entity_request_handler(Self::handle_get_project_symbols);
2982 client.add_entity_request_handler(Self::handle_resolve_inlay_hint);
2983 client.add_entity_request_handler(Self::handle_open_buffer_for_symbol);
2984 client.add_entity_request_handler(Self::handle_refresh_inlay_hints);
2985 client.add_entity_request_handler(Self::handle_on_type_formatting);
2986 client.add_entity_request_handler(Self::handle_apply_additional_edits_for_completion);
2987 client.add_entity_request_handler(Self::handle_register_buffer_with_language_servers);
2988 client.add_entity_request_handler(Self::handle_rename_project_entry);
2989 client.add_entity_request_handler(Self::handle_lsp_command::<GetCodeActions>);
2990 client.add_entity_request_handler(Self::handle_lsp_command::<GetCompletions>);
2991 client.add_entity_request_handler(Self::handle_lsp_command::<GetHover>);
2992 client.add_entity_request_handler(Self::handle_lsp_command::<GetDefinition>);
2993 client.add_entity_request_handler(Self::handle_lsp_command::<GetDeclaration>);
2994 client.add_entity_request_handler(Self::handle_lsp_command::<GetTypeDefinition>);
2995 client.add_entity_request_handler(Self::handle_lsp_command::<GetDocumentHighlights>);
2996 client.add_entity_request_handler(Self::handle_lsp_command::<GetReferences>);
2997 client.add_entity_request_handler(Self::handle_lsp_command::<PrepareRename>);
2998 client.add_entity_request_handler(Self::handle_lsp_command::<PerformRename>);
2999 client.add_entity_request_handler(Self::handle_lsp_command::<lsp_ext_command::ExpandMacro>);
3000 client.add_entity_request_handler(Self::handle_lsp_command::<LinkedEditingRange>);
3001 }
3002
3003 pub fn as_remote(&self) -> Option<&RemoteLspStore> {
3004 match &self.mode {
3005 LspStoreMode::Remote(remote_lsp_store) => Some(remote_lsp_store),
3006 _ => None,
3007 }
3008 }
3009
3010 pub fn as_local(&self) -> Option<&LocalLspStore> {
3011 match &self.mode {
3012 LspStoreMode::Local(local_lsp_store) => Some(local_lsp_store),
3013 _ => None,
3014 }
3015 }
3016
3017 pub fn as_local_mut(&mut self) -> Option<&mut LocalLspStore> {
3018 match &mut self.mode {
3019 LspStoreMode::Local(local_lsp_store) => Some(local_lsp_store),
3020 _ => None,
3021 }
3022 }
3023
3024 pub fn upstream_client(&self) -> Option<(AnyProtoClient, u64)> {
3025 match &self.mode {
3026 LspStoreMode::Remote(RemoteLspStore {
3027 upstream_client: Some(upstream_client),
3028 upstream_project_id,
3029 ..
3030 }) => Some((upstream_client.clone(), *upstream_project_id)),
3031
3032 LspStoreMode::Remote(RemoteLspStore {
3033 upstream_client: None,
3034 ..
3035 }) => None,
3036 LspStoreMode::Local(_) => None,
3037 }
3038 }
3039
3040 #[allow(clippy::too_many_arguments)]
3041 pub fn new_local(
3042 buffer_store: Entity<BufferStore>,
3043 worktree_store: Entity<WorktreeStore>,
3044 prettier_store: Entity<PrettierStore>,
3045 toolchain_store: Entity<ToolchainStore>,
3046 environment: Entity<ProjectEnvironment>,
3047 languages: Arc<LanguageRegistry>,
3048 http_client: Arc<dyn HttpClient>,
3049 fs: Arc<dyn Fs>,
3050 cx: &mut Context<Self>,
3051 ) -> Self {
3052 let yarn = YarnPathStore::new(fs.clone(), cx);
3053 cx.subscribe(&buffer_store, Self::on_buffer_store_event)
3054 .detach();
3055 cx.subscribe(&worktree_store, Self::on_worktree_store_event)
3056 .detach();
3057 cx.subscribe(&prettier_store, Self::on_prettier_store_event)
3058 .detach();
3059 cx.subscribe(&toolchain_store, Self::on_toolchain_store_event)
3060 .detach();
3061 cx.observe_global::<SettingsStore>(Self::on_settings_changed)
3062 .detach();
3063
3064 let _maintain_workspace_config = {
3065 let (sender, receiver) = watch::channel();
3066 (
3067 Self::maintain_workspace_config(fs.clone(), receiver, cx),
3068 sender,
3069 )
3070 };
3071 let project_tree = ProjectTree::new(worktree_store.clone(), cx);
3072 Self {
3073 mode: LspStoreMode::Local(LocalLspStore {
3074 weak: cx.weak_entity(),
3075 worktree_store: worktree_store.clone(),
3076 toolchain_store: toolchain_store.clone(),
3077 supplementary_language_servers: Default::default(),
3078 languages: languages.clone(),
3079 language_server_ids: Default::default(),
3080 language_servers: Default::default(),
3081 last_workspace_edits_by_language_server: Default::default(),
3082 language_server_watched_paths: Default::default(),
3083 language_server_paths_watched_for_rename: Default::default(),
3084 language_server_watcher_registrations: Default::default(),
3085 buffers_being_formatted: Default::default(),
3086 buffer_snapshots: Default::default(),
3087 prettier_store,
3088 environment,
3089 http_client,
3090 fs,
3091 yarn,
3092 next_diagnostic_group_id: Default::default(),
3093 diagnostics: Default::default(),
3094 _subscription: cx.on_app_quit(|this, cx| {
3095 this.as_local_mut().unwrap().shutdown_language_servers(cx)
3096 }),
3097 lsp_tree: LanguageServerTree::new(project_tree, languages.clone(), cx),
3098 registered_buffers: Default::default(),
3099 }),
3100 last_formatting_failure: None,
3101 downstream_client: None,
3102 buffer_store,
3103 worktree_store,
3104 toolchain_store: Some(toolchain_store),
3105 languages: languages.clone(),
3106 language_server_statuses: Default::default(),
3107 nonce: StdRng::from_entropy().gen(),
3108 diagnostic_summaries: Default::default(),
3109 active_entry: None,
3110
3111 _maintain_workspace_config,
3112 _maintain_buffer_languages: Self::maintain_buffer_languages(languages, cx),
3113 }
3114 }
3115
3116 fn send_lsp_proto_request<R: LspCommand>(
3117 &self,
3118 buffer: Entity<Buffer>,
3119 client: AnyProtoClient,
3120 upstream_project_id: u64,
3121 request: R,
3122 cx: &mut Context<'_, LspStore>,
3123 ) -> Task<anyhow::Result<<R as LspCommand>::Response>> {
3124 let message = request.to_proto(upstream_project_id, buffer.read(cx));
3125 cx.spawn(move |this, cx| async move {
3126 let response = client.request(message).await?;
3127 let this = this.upgrade().context("project dropped")?;
3128 request
3129 .response_from_proto(response, this, buffer, cx)
3130 .await
3131 })
3132 }
3133
3134 #[allow(clippy::too_many_arguments)]
3135 pub(super) fn new_remote(
3136 buffer_store: Entity<BufferStore>,
3137 worktree_store: Entity<WorktreeStore>,
3138 toolchain_store: Option<Entity<ToolchainStore>>,
3139 languages: Arc<LanguageRegistry>,
3140 upstream_client: AnyProtoClient,
3141 project_id: u64,
3142 fs: Arc<dyn Fs>,
3143 cx: &mut Context<Self>,
3144 ) -> Self {
3145 cx.subscribe(&buffer_store, Self::on_buffer_store_event)
3146 .detach();
3147 cx.subscribe(&worktree_store, Self::on_worktree_store_event)
3148 .detach();
3149 let _maintain_workspace_config = {
3150 let (sender, receiver) = watch::channel();
3151 (Self::maintain_workspace_config(fs, receiver, cx), sender)
3152 };
3153 Self {
3154 mode: LspStoreMode::Remote(RemoteLspStore {
3155 upstream_client: Some(upstream_client),
3156 upstream_project_id: project_id,
3157 }),
3158 downstream_client: None,
3159 last_formatting_failure: None,
3160 buffer_store,
3161 worktree_store,
3162 languages: languages.clone(),
3163 language_server_statuses: Default::default(),
3164 nonce: StdRng::from_entropy().gen(),
3165 diagnostic_summaries: Default::default(),
3166 active_entry: None,
3167 toolchain_store,
3168 _maintain_workspace_config,
3169 _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx),
3170 }
3171 }
3172
3173 fn on_buffer_store_event(
3174 &mut self,
3175 _: Entity<BufferStore>,
3176 event: &BufferStoreEvent,
3177 cx: &mut Context<Self>,
3178 ) {
3179 match event {
3180 BufferStoreEvent::BufferAdded(buffer) => {
3181 self.on_buffer_added(buffer, cx).log_err();
3182 }
3183 BufferStoreEvent::BufferChangedFilePath { buffer, old_file } => {
3184 let buffer_id = buffer.read(cx).remote_id();
3185 if let Some(local) = self.as_local_mut() {
3186 if let Some(old_file) = File::from_dyn(old_file.as_ref()) {
3187 local.reset_buffer(buffer, old_file, cx);
3188
3189 if local.registered_buffers.contains_key(&buffer_id) {
3190 local.unregister_old_buffer_from_language_servers(buffer, old_file, cx);
3191 }
3192 }
3193 }
3194
3195 self.detect_language_for_buffer(buffer, cx);
3196 if let Some(local) = self.as_local_mut() {
3197 local.initialize_buffer(buffer, cx);
3198 if local.registered_buffers.contains_key(&buffer_id) {
3199 local.register_buffer_with_language_servers(buffer, cx);
3200 }
3201 }
3202 }
3203 _ => {}
3204 }
3205 }
3206
3207 fn on_worktree_store_event(
3208 &mut self,
3209 _: Entity<WorktreeStore>,
3210 event: &WorktreeStoreEvent,
3211 cx: &mut Context<Self>,
3212 ) {
3213 match event {
3214 WorktreeStoreEvent::WorktreeAdded(worktree) => {
3215 if !worktree.read(cx).is_local() {
3216 return;
3217 }
3218 cx.subscribe(worktree, |this, worktree, event, cx| match event {
3219 worktree::Event::UpdatedEntries(changes) => {
3220 this.update_local_worktree_language_servers(&worktree, changes, cx);
3221 }
3222 worktree::Event::UpdatedGitRepositories(_)
3223 | worktree::Event::DeletedEntry(_) => {}
3224 })
3225 .detach()
3226 }
3227 WorktreeStoreEvent::WorktreeRemoved(_, id) => self.remove_worktree(*id, cx),
3228 WorktreeStoreEvent::WorktreeUpdateSent(worktree) => {
3229 worktree.update(cx, |worktree, _cx| self.send_diagnostic_summaries(worktree));
3230 }
3231 WorktreeStoreEvent::WorktreeReleased(..)
3232 | WorktreeStoreEvent::WorktreeOrderChanged
3233 | WorktreeStoreEvent::WorktreeUpdatedEntries(..)
3234 | WorktreeStoreEvent::WorktreeUpdatedGitRepositories(..)
3235 | WorktreeStoreEvent::WorktreeDeletedEntry(..) => {}
3236 }
3237 }
3238
3239 fn on_prettier_store_event(
3240 &mut self,
3241 _: Entity<PrettierStore>,
3242 event: &PrettierStoreEvent,
3243 cx: &mut Context<Self>,
3244 ) {
3245 match event {
3246 PrettierStoreEvent::LanguageServerRemoved(prettier_server_id) => {
3247 self.unregister_supplementary_language_server(*prettier_server_id, cx);
3248 }
3249 PrettierStoreEvent::LanguageServerAdded {
3250 new_server_id,
3251 name,
3252 prettier_server,
3253 } => {
3254 self.register_supplementary_language_server(
3255 *new_server_id,
3256 name.clone(),
3257 prettier_server.clone(),
3258 cx,
3259 );
3260 }
3261 }
3262 }
3263
3264 fn on_toolchain_store_event(
3265 &mut self,
3266 _: Entity<ToolchainStore>,
3267 event: &ToolchainStoreEvent,
3268 _: &mut Context<Self>,
3269 ) {
3270 match event {
3271 ToolchainStoreEvent::ToolchainActivated { .. } => {
3272 self.request_workspace_config_refresh()
3273 }
3274 }
3275 }
3276
3277 fn request_workspace_config_refresh(&mut self) {
3278 *self._maintain_workspace_config.1.borrow_mut() = ();
3279 }
3280
3281 pub fn prettier_store(&self) -> Option<Entity<PrettierStore>> {
3282 self.as_local().map(|local| local.prettier_store.clone())
3283 }
3284
3285 fn on_buffer_event(
3286 &mut self,
3287 buffer: Entity<Buffer>,
3288 event: &language::BufferEvent,
3289 cx: &mut Context<Self>,
3290 ) {
3291 match event {
3292 language::BufferEvent::Edited { .. } => {
3293 self.on_buffer_edited(buffer, cx);
3294 }
3295
3296 language::BufferEvent::Saved => {
3297 self.on_buffer_saved(buffer, cx);
3298 }
3299
3300 _ => {}
3301 }
3302 }
3303
3304 fn on_buffer_added(&mut self, buffer: &Entity<Buffer>, cx: &mut Context<Self>) -> Result<()> {
3305 buffer.update(cx, |buffer, _| {
3306 buffer.set_language_registry(self.languages.clone())
3307 });
3308
3309 cx.subscribe(buffer, |this, buffer, event, cx| {
3310 this.on_buffer_event(buffer, event, cx);
3311 })
3312 .detach();
3313
3314 self.detect_language_for_buffer(buffer, cx);
3315 if let Some(local) = self.as_local_mut() {
3316 local.initialize_buffer(buffer, cx);
3317 }
3318
3319 Ok(())
3320 }
3321
3322 pub(crate) fn register_buffer_with_language_servers(
3323 &mut self,
3324 buffer: &Entity<Buffer>,
3325 ignore_refcounts: bool,
3326 cx: &mut Context<Self>,
3327 ) -> OpenLspBufferHandle {
3328 let buffer_id = buffer.read(cx).remote_id();
3329 let handle = cx.new(|_| buffer.clone());
3330 if let Some(local) = self.as_local_mut() {
3331 let Some(file) = File::from_dyn(buffer.read(cx).file()) else {
3332 return handle;
3333 };
3334 if !file.is_local() {
3335 return handle;
3336 }
3337
3338 let refcount = local.registered_buffers.entry(buffer_id).or_insert(0);
3339 if !ignore_refcounts {
3340 *refcount += 1;
3341 }
3342
3343 if !ignore_refcounts || *refcount == 1 {
3344 local.register_buffer_with_language_servers(buffer, cx);
3345 }
3346 if !ignore_refcounts {
3347 cx.observe_release(&handle, move |this, buffer, cx| {
3348 let local = this.as_local_mut().unwrap();
3349 let Some(refcount) = local.registered_buffers.get_mut(&buffer_id) else {
3350 debug_panic!("bad refcounting");
3351 return;
3352 };
3353
3354 *refcount -= 1;
3355 if *refcount == 0 {
3356 local.registered_buffers.remove(&buffer_id);
3357 if let Some(file) = File::from_dyn(buffer.read(cx).file()).cloned() {
3358 local.unregister_old_buffer_from_language_servers(&buffer, &file, cx);
3359 }
3360 }
3361 })
3362 .detach();
3363 }
3364 } else if let Some((upstream_client, upstream_project_id)) = self.upstream_client() {
3365 let buffer_id = buffer.read(cx).remote_id().to_proto();
3366 cx.background_spawn(async move {
3367 upstream_client
3368 .request(proto::RegisterBufferWithLanguageServers {
3369 project_id: upstream_project_id,
3370 buffer_id,
3371 })
3372 .await
3373 })
3374 .detach();
3375 } else {
3376 panic!("oops!");
3377 }
3378 handle
3379 }
3380
3381 fn maintain_buffer_languages(
3382 languages: Arc<LanguageRegistry>,
3383 cx: &mut Context<Self>,
3384 ) -> Task<()> {
3385 let mut subscription = languages.subscribe();
3386 let mut prev_reload_count = languages.reload_count();
3387 cx.spawn(move |this, mut cx| async move {
3388 while let Some(()) = subscription.next().await {
3389 if let Some(this) = this.upgrade() {
3390 // If the language registry has been reloaded, then remove and
3391 // re-assign the languages on all open buffers.
3392 let reload_count = languages.reload_count();
3393 if reload_count > prev_reload_count {
3394 prev_reload_count = reload_count;
3395 this.update(&mut cx, |this, cx| {
3396 this.buffer_store.clone().update(cx, |buffer_store, cx| {
3397 for buffer in buffer_store.buffers() {
3398 if let Some(f) = File::from_dyn(buffer.read(cx).file()).cloned()
3399 {
3400 buffer
3401 .update(cx, |buffer, cx| buffer.set_language(None, cx));
3402 if let Some(local) = this.as_local_mut() {
3403 local.reset_buffer(&buffer, &f, cx);
3404
3405 if local
3406 .registered_buffers
3407 .contains_key(&buffer.read(cx).remote_id())
3408 {
3409 if let Some(file_url) =
3410 lsp::Url::from_file_path(&f.abs_path(cx))
3411 .log_err()
3412 {
3413 local.unregister_buffer_from_language_servers(
3414 &buffer, &file_url, cx,
3415 );
3416 }
3417 }
3418 }
3419 }
3420 }
3421 });
3422 })
3423 .ok();
3424 }
3425
3426 this.update(&mut cx, |this, cx| {
3427 let mut plain_text_buffers = Vec::new();
3428 let mut buffers_with_unknown_injections = Vec::new();
3429 for handle in this.buffer_store.read(cx).buffers() {
3430 let buffer = handle.read(cx);
3431 if buffer.language().is_none()
3432 || buffer.language() == Some(&*language::PLAIN_TEXT)
3433 {
3434 plain_text_buffers.push(handle);
3435 } else if buffer.contains_unknown_injections() {
3436 buffers_with_unknown_injections.push(handle);
3437 }
3438 }
3439 for buffer in plain_text_buffers {
3440 this.detect_language_for_buffer(&buffer, cx);
3441 if let Some(local) = this.as_local_mut() {
3442 local.initialize_buffer(&buffer, cx);
3443 if local
3444 .registered_buffers
3445 .contains_key(&buffer.read(cx).remote_id())
3446 {
3447 local.register_buffer_with_language_servers(&buffer, cx);
3448 }
3449 }
3450 }
3451
3452 for buffer in buffers_with_unknown_injections {
3453 buffer.update(cx, |buffer, cx| buffer.reparse(cx));
3454 }
3455 })
3456 .ok();
3457 }
3458 }
3459 })
3460 }
3461
3462 fn detect_language_for_buffer(
3463 &mut self,
3464 buffer_handle: &Entity<Buffer>,
3465 cx: &mut Context<Self>,
3466 ) -> Option<language::AvailableLanguage> {
3467 // If the buffer has a language, set it and start the language server if we haven't already.
3468 let buffer = buffer_handle.read(cx);
3469 let file = buffer.file()?;
3470
3471 let content = buffer.as_rope();
3472 let available_language = self.languages.language_for_file(file, Some(content), cx);
3473 if let Some(available_language) = &available_language {
3474 if let Some(Ok(Ok(new_language))) = self
3475 .languages
3476 .load_language(available_language)
3477 .now_or_never()
3478 {
3479 self.set_language_for_buffer(buffer_handle, new_language, cx);
3480 }
3481 } else {
3482 cx.emit(LspStoreEvent::LanguageDetected {
3483 buffer: buffer_handle.clone(),
3484 new_language: None,
3485 });
3486 }
3487
3488 available_language
3489 }
3490
3491 pub(crate) fn set_language_for_buffer(
3492 &mut self,
3493 buffer_entity: &Entity<Buffer>,
3494 new_language: Arc<Language>,
3495 cx: &mut Context<Self>,
3496 ) {
3497 let buffer = buffer_entity.read(cx);
3498 let buffer_file = buffer.file().cloned();
3499 let buffer_id = buffer.remote_id();
3500 if let Some(local_store) = self.as_local_mut() {
3501 if local_store.registered_buffers.contains_key(&buffer_id) {
3502 if let Some(abs_path) =
3503 File::from_dyn(buffer_file.as_ref()).map(|file| file.abs_path(cx))
3504 {
3505 if let Some(file_url) = lsp::Url::from_file_path(&abs_path).log_err() {
3506 local_store.unregister_buffer_from_language_servers(
3507 buffer_entity,
3508 &file_url,
3509 cx,
3510 );
3511 }
3512 }
3513 }
3514 }
3515 buffer_entity.update(cx, |buffer, cx| {
3516 if buffer.language().map_or(true, |old_language| {
3517 !Arc::ptr_eq(old_language, &new_language)
3518 }) {
3519 buffer.set_language(Some(new_language.clone()), cx);
3520 }
3521 });
3522
3523 let settings =
3524 language_settings(Some(new_language.name()), buffer_file.as_ref(), cx).into_owned();
3525 let buffer_file = File::from_dyn(buffer_file.as_ref());
3526
3527 let worktree_id = if let Some(file) = buffer_file {
3528 let worktree = file.worktree.clone();
3529
3530 if let Some(local) = self.as_local_mut() {
3531 if local.registered_buffers.contains_key(&buffer_id) {
3532 local.register_buffer_with_language_servers(buffer_entity, cx);
3533 }
3534 }
3535 Some(worktree.read(cx).id())
3536 } else {
3537 None
3538 };
3539
3540 if settings.prettier.allowed {
3541 if let Some(prettier_plugins) = prettier_store::prettier_plugins_for_language(&settings)
3542 {
3543 let prettier_store = self.as_local().map(|s| s.prettier_store.clone());
3544 if let Some(prettier_store) = prettier_store {
3545 prettier_store.update(cx, |prettier_store, cx| {
3546 prettier_store.install_default_prettier(
3547 worktree_id,
3548 prettier_plugins.iter().map(|s| Arc::from(s.as_str())),
3549 cx,
3550 )
3551 })
3552 }
3553 }
3554 }
3555
3556 cx.emit(LspStoreEvent::LanguageDetected {
3557 buffer: buffer_entity.clone(),
3558 new_language: Some(new_language),
3559 })
3560 }
3561
3562 pub fn buffer_store(&self) -> Entity<BufferStore> {
3563 self.buffer_store.clone()
3564 }
3565
3566 pub fn set_active_entry(&mut self, active_entry: Option<ProjectEntryId>) {
3567 self.active_entry = active_entry;
3568 }
3569
3570 pub(crate) fn send_diagnostic_summaries(&self, worktree: &mut Worktree) {
3571 if let Some((client, downstream_project_id)) = self.downstream_client.clone() {
3572 if let Some(summaries) = self.diagnostic_summaries.get(&worktree.id()) {
3573 for (path, summaries) in summaries {
3574 for (&server_id, summary) in summaries {
3575 client
3576 .send(proto::UpdateDiagnosticSummary {
3577 project_id: downstream_project_id,
3578 worktree_id: worktree.id().to_proto(),
3579 summary: Some(summary.to_proto(server_id, path)),
3580 })
3581 .log_err();
3582 }
3583 }
3584 }
3585 }
3586 }
3587
3588 pub fn request_lsp<R: LspCommand>(
3589 &mut self,
3590 buffer_handle: Entity<Buffer>,
3591 server: LanguageServerToQuery,
3592 request: R,
3593 cx: &mut Context<Self>,
3594 ) -> Task<Result<R::Response>>
3595 where
3596 <R::LspRequest as lsp::request::Request>::Result: Send,
3597 <R::LspRequest as lsp::request::Request>::Params: Send,
3598 {
3599 if let Some((upstream_client, upstream_project_id)) = self.upstream_client() {
3600 return self.send_lsp_proto_request(
3601 buffer_handle,
3602 upstream_client,
3603 upstream_project_id,
3604 request,
3605 cx,
3606 );
3607 }
3608
3609 let Some(language_server) = buffer_handle.update(cx, |buffer, cx| match server {
3610 LanguageServerToQuery::FirstCapable => self.as_local().and_then(|local| {
3611 local
3612 .language_servers_for_buffer(buffer, cx)
3613 .find(|(_, server)| {
3614 request.check_capabilities(server.adapter_server_capabilities())
3615 })
3616 .map(|(_, server)| server.clone())
3617 }),
3618 LanguageServerToQuery::Other(id) => self
3619 .language_server_for_local_buffer(buffer, id, cx)
3620 .and_then(|(_, server)| {
3621 request
3622 .check_capabilities(server.adapter_server_capabilities())
3623 .then(|| Arc::clone(server))
3624 }),
3625 }) else {
3626 return Task::ready(Ok(Default::default()));
3627 };
3628
3629 let buffer = buffer_handle.read(cx);
3630 let file = File::from_dyn(buffer.file()).and_then(File::as_local);
3631
3632 let Some(file) = file else {
3633 return Task::ready(Ok(Default::default()));
3634 };
3635
3636 let lsp_params = match request.to_lsp_params_or_response(
3637 &file.abs_path(cx),
3638 buffer,
3639 &language_server,
3640 cx,
3641 ) {
3642 Ok(LspParamsOrResponse::Params(lsp_params)) => lsp_params,
3643 Ok(LspParamsOrResponse::Response(response)) => return Task::ready(Ok(response)),
3644
3645 Err(err) => {
3646 let message = format!(
3647 "{} via {} failed: {}",
3648 request.display_name(),
3649 language_server.name(),
3650 err
3651 );
3652 log::warn!("{}", message);
3653 return Task::ready(Err(anyhow!(message)));
3654 }
3655 };
3656
3657 let status = request.status();
3658 if !request.check_capabilities(language_server.adapter_server_capabilities()) {
3659 return Task::ready(Ok(Default::default()));
3660 }
3661 return cx.spawn(move |this, cx| async move {
3662 let lsp_request = language_server.request::<R::LspRequest>(lsp_params);
3663
3664 let id = lsp_request.id();
3665 let _cleanup = if status.is_some() {
3666 cx.update(|cx| {
3667 this.update(cx, |this, cx| {
3668 this.on_lsp_work_start(
3669 language_server.server_id(),
3670 id.to_string(),
3671 LanguageServerProgress {
3672 is_disk_based_diagnostics_progress: false,
3673 is_cancellable: false,
3674 title: None,
3675 message: status.clone(),
3676 percentage: None,
3677 last_update_at: cx.background_executor().now(),
3678 },
3679 cx,
3680 );
3681 })
3682 })
3683 .log_err();
3684
3685 Some(defer(|| {
3686 cx.update(|cx| {
3687 this.update(cx, |this, cx| {
3688 this.on_lsp_work_end(language_server.server_id(), id.to_string(), cx);
3689 })
3690 })
3691 .log_err();
3692 }))
3693 } else {
3694 None
3695 };
3696
3697 let result = lsp_request.await;
3698
3699 let response = result.map_err(|err| {
3700 let message = format!(
3701 "{} via {} failed: {}",
3702 request.display_name(),
3703 language_server.name(),
3704 err
3705 );
3706 log::warn!("{}", message);
3707 anyhow!(message)
3708 })?;
3709
3710 let response = request
3711 .response_from_lsp(
3712 response,
3713 this.upgrade().ok_or_else(|| anyhow!("no app context"))?,
3714 buffer_handle,
3715 language_server.server_id(),
3716 cx.clone(),
3717 )
3718 .await;
3719 response
3720 });
3721 }
3722
3723 fn on_settings_changed(&mut self, cx: &mut Context<Self>) {
3724 let mut language_formatters_to_check = Vec::new();
3725 for buffer in self.buffer_store.read(cx).buffers() {
3726 let buffer = buffer.read(cx);
3727 let buffer_file = File::from_dyn(buffer.file());
3728 let buffer_language = buffer.language();
3729 let settings = language_settings(buffer_language.map(|l| l.name()), buffer.file(), cx);
3730 if buffer_language.is_some() {
3731 language_formatters_to_check.push((
3732 buffer_file.map(|f| f.worktree_id(cx)),
3733 settings.into_owned(),
3734 ));
3735 }
3736 }
3737
3738 self.refresh_server_tree(cx);
3739
3740 if let Some(prettier_store) = self.as_local().map(|s| s.prettier_store.clone()) {
3741 prettier_store.update(cx, |prettier_store, cx| {
3742 prettier_store.on_settings_changed(language_formatters_to_check, cx)
3743 })
3744 }
3745
3746 cx.notify();
3747 }
3748
3749 fn refresh_server_tree(&mut self, cx: &mut Context<'_, Self>) {
3750 let buffer_store = self.buffer_store.clone();
3751 if let Some(local) = self.as_local_mut() {
3752 let mut adapters = BTreeMap::default();
3753 let to_stop = local.lsp_tree.clone().update(cx, |lsp_tree, cx| {
3754 let get_adapter = {
3755 let languages = local.languages.clone();
3756 let environment = local.environment.clone();
3757 let weak = local.weak.clone();
3758 let worktree_store = local.worktree_store.clone();
3759 let http_client = local.http_client.clone();
3760 let fs = local.fs.clone();
3761 move |worktree_id, cx: &mut App| {
3762 let worktree = worktree_store.read(cx).worktree_for_id(worktree_id, cx)?;
3763 Some(LocalLspAdapterDelegate::new(
3764 languages.clone(),
3765 &environment,
3766 weak.clone(),
3767 &worktree,
3768 http_client.clone(),
3769 fs.clone(),
3770 cx,
3771 ))
3772 }
3773 };
3774
3775 let mut rebase = lsp_tree.rebase();
3776 for buffer in buffer_store.read(cx).buffers().collect::<Vec<_>>() {
3777 let buffer = buffer.read(cx);
3778 if !local.registered_buffers.contains_key(&buffer.remote_id()) {
3779 continue;
3780 }
3781 if let Some((file, language)) = File::from_dyn(buffer.file())
3782 .cloned()
3783 .zip(buffer.language().map(|l| l.name()))
3784 {
3785 let worktree_id = file.worktree_id(cx);
3786 let Some(worktree) = local
3787 .worktree_store
3788 .read(cx)
3789 .worktree_for_id(worktree_id, cx)
3790 else {
3791 continue;
3792 };
3793 let path: Arc<Path> = file
3794 .path()
3795 .parent()
3796 .map(Arc::from)
3797 .unwrap_or_else(|| file.path().clone());
3798 let worktree_path = ProjectPath { worktree_id, path };
3799
3800 let Some(delegate) = adapters
3801 .entry(worktree_id)
3802 .or_insert_with(|| get_adapter(worktree_id, cx))
3803 .clone()
3804 else {
3805 continue;
3806 };
3807 let nodes = rebase.get(
3808 worktree_path,
3809 AdapterQuery::Language(&language),
3810 delegate.clone(),
3811 cx,
3812 );
3813 for node in nodes {
3814 node.server_id_or_init(
3815 |LaunchDisposition {
3816 server_name,
3817 attach,
3818 path,
3819 settings,
3820 }| match attach {
3821 language::Attach::InstancePerRoot => {
3822 // todo: handle instance per root proper.
3823 if let Some(server_ids) = local
3824 .language_server_ids
3825 .get(&(worktree_id, server_name.clone()))
3826 {
3827 server_ids.iter().cloned().next().unwrap()
3828 } else {
3829 local.start_language_server(
3830 &worktree,
3831 delegate.clone(),
3832 local
3833 .languages
3834 .lsp_adapters(&language)
3835 .into_iter()
3836 .find(|adapter| &adapter.name() == server_name)
3837 .expect("To find LSP adapter"),
3838 settings,
3839 cx,
3840 )
3841 }
3842 }
3843 language::Attach::Shared => {
3844 let uri = Url::from_file_path(
3845 worktree.read(cx).abs_path().join(&path.path),
3846 );
3847 let key = (worktree_id, server_name.clone());
3848 local.language_server_ids.remove(&key);
3849
3850 let server_id = local.start_language_server(
3851 &worktree,
3852 delegate.clone(),
3853 local
3854 .languages
3855 .lsp_adapters(&language)
3856 .into_iter()
3857 .find(|adapter| &adapter.name() == server_name)
3858 .expect("To find LSP adapter"),
3859 settings,
3860 cx,
3861 );
3862 if let Some(state) = local.language_servers.get(&server_id)
3863 {
3864 if let Ok(uri) = uri {
3865 state.add_workspace_folder(uri);
3866 };
3867 }
3868 server_id
3869 }
3870 },
3871 );
3872 }
3873 }
3874 }
3875 rebase.finish()
3876 });
3877 for (id, name) in to_stop {
3878 self.stop_local_language_server(id, name, cx).detach();
3879 }
3880 }
3881 }
3882
3883 pub fn apply_code_action(
3884 &self,
3885 buffer_handle: Entity<Buffer>,
3886 mut action: CodeAction,
3887 push_to_history: bool,
3888 cx: &mut Context<Self>,
3889 ) -> Task<Result<ProjectTransaction>> {
3890 if let Some((upstream_client, project_id)) = self.upstream_client() {
3891 let request = proto::ApplyCodeAction {
3892 project_id,
3893 buffer_id: buffer_handle.read(cx).remote_id().into(),
3894 action: Some(Self::serialize_code_action(&action)),
3895 };
3896 let buffer_store = self.buffer_store();
3897 cx.spawn(move |_, mut cx| async move {
3898 let response = upstream_client
3899 .request(request)
3900 .await?
3901 .transaction
3902 .ok_or_else(|| anyhow!("missing transaction"))?;
3903
3904 buffer_store
3905 .update(&mut cx, |buffer_store, cx| {
3906 buffer_store.deserialize_project_transaction(response, push_to_history, cx)
3907 })?
3908 .await
3909 })
3910 } else if self.mode.is_local() {
3911 let Some((lsp_adapter, lang_server)) = buffer_handle.update(cx, |buffer, cx| {
3912 self.language_server_for_local_buffer(buffer, action.server_id, cx)
3913 .map(|(adapter, server)| (adapter.clone(), server.clone()))
3914 }) else {
3915 return Task::ready(Ok(ProjectTransaction::default()));
3916 };
3917 cx.spawn(move |this, mut cx| async move {
3918 LocalLspStore::try_resolve_code_action(&lang_server, &mut action)
3919 .await
3920 .context("resolving a code action")?;
3921 if let Some(edit) = action.lsp_action.edit() {
3922 if edit.changes.is_some() || edit.document_changes.is_some() {
3923 return LocalLspStore::deserialize_workspace_edit(
3924 this.upgrade().ok_or_else(|| anyhow!("no app present"))?,
3925 edit.clone(),
3926 push_to_history,
3927 lsp_adapter.clone(),
3928 lang_server.clone(),
3929 &mut cx,
3930 )
3931 .await;
3932 }
3933 }
3934
3935 if let Some(command) = action.lsp_action.command() {
3936 let server_capabilities = lang_server.capabilities();
3937 let available_commands = server_capabilities
3938 .execute_command_provider
3939 .as_ref()
3940 .map(|options| options.commands.as_slice())
3941 .unwrap_or_default();
3942 if available_commands.contains(&command.command) {
3943 this.update(&mut cx, |this, _| {
3944 this.as_local_mut()
3945 .unwrap()
3946 .last_workspace_edits_by_language_server
3947 .remove(&lang_server.server_id());
3948 })?;
3949
3950 let result = lang_server
3951 .request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
3952 command: command.command.clone(),
3953 arguments: command.arguments.clone().unwrap_or_default(),
3954 ..Default::default()
3955 })
3956 .await;
3957
3958 result?;
3959
3960 return this.update(&mut cx, |this, _| {
3961 this.as_local_mut()
3962 .unwrap()
3963 .last_workspace_edits_by_language_server
3964 .remove(&lang_server.server_id())
3965 .unwrap_or_default()
3966 });
3967 } else {
3968 log::warn!("Cannot execute a command {} not listed in the language server capabilities", command.command);
3969 }
3970 }
3971
3972 Ok(ProjectTransaction::default())
3973 })
3974 } else {
3975 Task::ready(Err(anyhow!("no upstream client and not local")))
3976 }
3977 }
3978
3979 pub fn apply_code_action_kind(
3980 &mut self,
3981 buffers: HashSet<Entity<Buffer>>,
3982 kind: CodeActionKind,
3983 push_to_history: bool,
3984 cx: &mut Context<Self>,
3985 ) -> Task<anyhow::Result<ProjectTransaction>> {
3986 if let Some(_) = self.as_local() {
3987 cx.spawn(move |lsp_store, mut cx| async move {
3988 let buffers = buffers.into_iter().collect::<Vec<_>>();
3989 let result = LocalLspStore::execute_code_action_kind_locally(
3990 lsp_store.clone(),
3991 buffers,
3992 kind,
3993 push_to_history,
3994 cx.clone(),
3995 )
3996 .await;
3997 lsp_store.update(&mut cx, |lsp_store, _| {
3998 lsp_store.update_last_formatting_failure(&result);
3999 })?;
4000 result
4001 })
4002 } else if let Some((client, project_id)) = self.upstream_client() {
4003 let buffer_store = self.buffer_store();
4004 cx.spawn(move |lsp_store, mut cx| async move {
4005 let result = client
4006 .request(proto::ApplyCodeActionKind {
4007 project_id,
4008 kind: kind.as_str().to_owned(),
4009 buffer_ids: buffers
4010 .iter()
4011 .map(|buffer| {
4012 buffer.update(&mut cx, |buffer, _| buffer.remote_id().into())
4013 })
4014 .collect::<Result<_>>()?,
4015 })
4016 .await
4017 .and_then(|result| result.transaction.context("missing transaction"));
4018 lsp_store.update(&mut cx, |lsp_store, _| {
4019 lsp_store.update_last_formatting_failure(&result);
4020 })?;
4021
4022 let transaction_response = result?;
4023 buffer_store
4024 .update(&mut cx, |buffer_store, cx| {
4025 buffer_store.deserialize_project_transaction(
4026 transaction_response,
4027 push_to_history,
4028 cx,
4029 )
4030 })?
4031 .await
4032 })
4033 } else {
4034 Task::ready(Ok(ProjectTransaction::default()))
4035 }
4036 }
4037
4038 pub fn resolve_inlay_hint(
4039 &self,
4040 hint: InlayHint,
4041 buffer_handle: Entity<Buffer>,
4042 server_id: LanguageServerId,
4043 cx: &mut Context<Self>,
4044 ) -> Task<anyhow::Result<InlayHint>> {
4045 if let Some((upstream_client, project_id)) = self.upstream_client() {
4046 let request = proto::ResolveInlayHint {
4047 project_id,
4048 buffer_id: buffer_handle.read(cx).remote_id().into(),
4049 language_server_id: server_id.0 as u64,
4050 hint: Some(InlayHints::project_to_proto_hint(hint.clone())),
4051 };
4052 cx.spawn(move |_, _| async move {
4053 let response = upstream_client
4054 .request(request)
4055 .await
4056 .context("inlay hints proto request")?;
4057 match response.hint {
4058 Some(resolved_hint) => InlayHints::proto_to_project_hint(resolved_hint)
4059 .context("inlay hints proto resolve response conversion"),
4060 None => Ok(hint),
4061 }
4062 })
4063 } else {
4064 let Some(lang_server) = buffer_handle.update(cx, |buffer, cx| {
4065 self.language_server_for_local_buffer(buffer, server_id, cx)
4066 .map(|(_, server)| server.clone())
4067 }) else {
4068 return Task::ready(Ok(hint));
4069 };
4070 if !InlayHints::can_resolve_inlays(&lang_server.capabilities()) {
4071 return Task::ready(Ok(hint));
4072 }
4073 let buffer_snapshot = buffer_handle.read(cx).snapshot();
4074 cx.spawn(move |_, mut cx| async move {
4075 let resolve_task = lang_server.request::<lsp::request::InlayHintResolveRequest>(
4076 InlayHints::project_to_lsp_hint(hint, &buffer_snapshot),
4077 );
4078 let resolved_hint = resolve_task
4079 .await
4080 .context("inlay hint resolve LSP request")?;
4081 let resolved_hint = InlayHints::lsp_to_project_hint(
4082 resolved_hint,
4083 &buffer_handle,
4084 server_id,
4085 ResolveState::Resolved,
4086 false,
4087 &mut cx,
4088 )
4089 .await?;
4090 Ok(resolved_hint)
4091 })
4092 }
4093 }
4094
4095 pub(crate) fn linked_edit(
4096 &mut self,
4097 buffer: &Entity<Buffer>,
4098 position: Anchor,
4099 cx: &mut Context<Self>,
4100 ) -> Task<Result<Vec<Range<Anchor>>>> {
4101 let snapshot = buffer.read(cx).snapshot();
4102 let scope = snapshot.language_scope_at(position);
4103 let Some(server_id) = self
4104 .as_local()
4105 .and_then(|local| {
4106 buffer.update(cx, |buffer, cx| {
4107 local
4108 .language_servers_for_buffer(buffer, cx)
4109 .filter(|(_, server)| {
4110 server
4111 .capabilities()
4112 .linked_editing_range_provider
4113 .is_some()
4114 })
4115 .filter(|(adapter, _)| {
4116 scope
4117 .as_ref()
4118 .map(|scope| scope.language_allowed(&adapter.name))
4119 .unwrap_or(true)
4120 })
4121 .map(|(_, server)| LanguageServerToQuery::Other(server.server_id()))
4122 .next()
4123 })
4124 })
4125 .or_else(|| {
4126 self.upstream_client()
4127 .is_some()
4128 .then_some(LanguageServerToQuery::FirstCapable)
4129 })
4130 .filter(|_| {
4131 maybe!({
4132 let language = buffer.read(cx).language_at(position)?;
4133 Some(
4134 language_settings(Some(language.name()), buffer.read(cx).file(), cx)
4135 .linked_edits,
4136 )
4137 }) == Some(true)
4138 })
4139 else {
4140 return Task::ready(Ok(vec![]));
4141 };
4142
4143 self.request_lsp(
4144 buffer.clone(),
4145 server_id,
4146 LinkedEditingRange { position },
4147 cx,
4148 )
4149 }
4150
4151 fn apply_on_type_formatting(
4152 &mut self,
4153 buffer: Entity<Buffer>,
4154 position: Anchor,
4155 trigger: String,
4156 cx: &mut Context<Self>,
4157 ) -> Task<Result<Option<Transaction>>> {
4158 if let Some((client, project_id)) = self.upstream_client() {
4159 let request = proto::OnTypeFormatting {
4160 project_id,
4161 buffer_id: buffer.read(cx).remote_id().into(),
4162 position: Some(serialize_anchor(&position)),
4163 trigger,
4164 version: serialize_version(&buffer.read(cx).version()),
4165 };
4166 cx.spawn(move |_, _| async move {
4167 client
4168 .request(request)
4169 .await?
4170 .transaction
4171 .map(language::proto::deserialize_transaction)
4172 .transpose()
4173 })
4174 } else if let Some(local) = self.as_local_mut() {
4175 let buffer_id = buffer.read(cx).remote_id();
4176 local.buffers_being_formatted.insert(buffer_id);
4177 cx.spawn(move |this, mut cx| async move {
4178 let _cleanup = defer({
4179 let this = this.clone();
4180 let mut cx = cx.clone();
4181 move || {
4182 this.update(&mut cx, |this, _| {
4183 if let Some(local) = this.as_local_mut() {
4184 local.buffers_being_formatted.remove(&buffer_id);
4185 }
4186 })
4187 .ok();
4188 }
4189 });
4190
4191 buffer
4192 .update(&mut cx, |buffer, _| {
4193 buffer.wait_for_edits(Some(position.timestamp))
4194 })?
4195 .await?;
4196 this.update(&mut cx, |this, cx| {
4197 let position = position.to_point_utf16(buffer.read(cx));
4198 this.on_type_format(buffer, position, trigger, false, cx)
4199 })?
4200 .await
4201 })
4202 } else {
4203 Task::ready(Err(anyhow!("No upstream client or local language server")))
4204 }
4205 }
4206
4207 pub fn on_type_format<T: ToPointUtf16>(
4208 &mut self,
4209 buffer: Entity<Buffer>,
4210 position: T,
4211 trigger: String,
4212 push_to_history: bool,
4213 cx: &mut Context<Self>,
4214 ) -> Task<Result<Option<Transaction>>> {
4215 let position = position.to_point_utf16(buffer.read(cx));
4216 self.on_type_format_impl(buffer, position, trigger, push_to_history, cx)
4217 }
4218
4219 fn on_type_format_impl(
4220 &mut self,
4221 buffer: Entity<Buffer>,
4222 position: PointUtf16,
4223 trigger: String,
4224 push_to_history: bool,
4225 cx: &mut Context<Self>,
4226 ) -> Task<Result<Option<Transaction>>> {
4227 let options = buffer.update(cx, |buffer, cx| {
4228 lsp_command::lsp_formatting_options(
4229 language_settings(
4230 buffer.language_at(position).map(|l| l.name()),
4231 buffer.file(),
4232 cx,
4233 )
4234 .as_ref(),
4235 )
4236 });
4237 self.request_lsp(
4238 buffer.clone(),
4239 LanguageServerToQuery::FirstCapable,
4240 OnTypeFormatting {
4241 position,
4242 trigger,
4243 options,
4244 push_to_history,
4245 },
4246 cx,
4247 )
4248 }
4249 pub fn code_actions(
4250 &mut self,
4251 buffer_handle: &Entity<Buffer>,
4252 range: Range<Anchor>,
4253 kinds: Option<Vec<CodeActionKind>>,
4254 cx: &mut Context<Self>,
4255 ) -> Task<Result<Vec<CodeAction>>> {
4256 if let Some((upstream_client, project_id)) = self.upstream_client() {
4257 let request_task = upstream_client.request(proto::MultiLspQuery {
4258 buffer_id: buffer_handle.read(cx).remote_id().into(),
4259 version: serialize_version(&buffer_handle.read(cx).version()),
4260 project_id,
4261 strategy: Some(proto::multi_lsp_query::Strategy::All(
4262 proto::AllLanguageServers {},
4263 )),
4264 request: Some(proto::multi_lsp_query::Request::GetCodeActions(
4265 GetCodeActions {
4266 range: range.clone(),
4267 kinds: kinds.clone(),
4268 }
4269 .to_proto(project_id, buffer_handle.read(cx)),
4270 )),
4271 });
4272 let buffer = buffer_handle.clone();
4273 cx.spawn(|weak_project, cx| async move {
4274 let Some(project) = weak_project.upgrade() else {
4275 return Ok(Vec::new());
4276 };
4277 let responses = request_task.await?.responses;
4278 let actions = join_all(
4279 responses
4280 .into_iter()
4281 .filter_map(|lsp_response| match lsp_response.response? {
4282 proto::lsp_response::Response::GetCodeActionsResponse(response) => {
4283 Some(response)
4284 }
4285 unexpected => {
4286 debug_panic!("Unexpected response: {unexpected:?}");
4287 None
4288 }
4289 })
4290 .map(|code_actions_response| {
4291 GetCodeActions {
4292 range: range.clone(),
4293 kinds: kinds.clone(),
4294 }
4295 .response_from_proto(
4296 code_actions_response,
4297 project.clone(),
4298 buffer.clone(),
4299 cx.clone(),
4300 )
4301 }),
4302 )
4303 .await;
4304
4305 Ok(actions
4306 .into_iter()
4307 .collect::<Result<Vec<Vec<_>>>>()?
4308 .into_iter()
4309 .flatten()
4310 .collect())
4311 })
4312 } else {
4313 let all_actions_task = self.request_multiple_lsp_locally(
4314 buffer_handle,
4315 Some(range.start),
4316 GetCodeActions {
4317 range: range.clone(),
4318 kinds: kinds.clone(),
4319 },
4320 cx,
4321 );
4322 cx.spawn(
4323 |_, _| async move { Ok(all_actions_task.await.into_iter().flatten().collect()) },
4324 )
4325 }
4326 }
4327
4328 #[inline(never)]
4329 pub fn completions(
4330 &self,
4331 buffer: &Entity<Buffer>,
4332 position: PointUtf16,
4333 context: CompletionContext,
4334 cx: &mut Context<Self>,
4335 ) -> Task<Result<Vec<Completion>>> {
4336 let language_registry = self.languages.clone();
4337
4338 if let Some((upstream_client, project_id)) = self.upstream_client() {
4339 let task = self.send_lsp_proto_request(
4340 buffer.clone(),
4341 upstream_client,
4342 project_id,
4343 GetCompletions { position, context },
4344 cx,
4345 );
4346 let language = buffer.read(cx).language().cloned();
4347
4348 // In the future, we should provide project guests with the names of LSP adapters,
4349 // so that they can use the correct LSP adapter when computing labels. For now,
4350 // guests just use the first LSP adapter associated with the buffer's language.
4351 let lsp_adapter = language.as_ref().and_then(|language| {
4352 language_registry
4353 .lsp_adapters(&language.name())
4354 .first()
4355 .cloned()
4356 });
4357
4358 cx.foreground_executor().spawn(async move {
4359 let completions = task.await?;
4360 let mut result = Vec::new();
4361 populate_labels_for_completions(completions, language, lsp_adapter, &mut result)
4362 .await;
4363 Ok(result)
4364 })
4365 } else if let Some(local) = self.as_local() {
4366 let snapshot = buffer.read(cx).snapshot();
4367 let offset = position.to_offset(&snapshot);
4368 let scope = snapshot.language_scope_at(offset);
4369 let language = snapshot.language().cloned();
4370
4371 let server_ids: Vec<_> = buffer.update(cx, |buffer, cx| {
4372 local
4373 .language_servers_for_buffer(buffer, cx)
4374 .filter(|(_, server)| server.capabilities().completion_provider.is_some())
4375 .filter(|(adapter, _)| {
4376 scope
4377 .as_ref()
4378 .map(|scope| scope.language_allowed(&adapter.name))
4379 .unwrap_or(true)
4380 })
4381 .map(|(_, server)| server.server_id())
4382 .collect()
4383 });
4384
4385 let buffer = buffer.clone();
4386 cx.spawn(move |this, mut cx| async move {
4387 let mut tasks = Vec::with_capacity(server_ids.len());
4388 this.update(&mut cx, |this, cx| {
4389 for server_id in server_ids {
4390 let lsp_adapter = this.language_server_adapter_for_id(server_id);
4391 tasks.push((
4392 lsp_adapter,
4393 this.request_lsp(
4394 buffer.clone(),
4395 LanguageServerToQuery::Other(server_id),
4396 GetCompletions {
4397 position,
4398 context: context.clone(),
4399 },
4400 cx,
4401 ),
4402 ));
4403 }
4404 })?;
4405
4406 let mut completions = Vec::new();
4407 for (lsp_adapter, task) in tasks {
4408 if let Ok(new_completions) = task.await {
4409 populate_labels_for_completions(
4410 new_completions,
4411 language.clone(),
4412 lsp_adapter,
4413 &mut completions,
4414 )
4415 .await;
4416 }
4417 }
4418
4419 Ok(completions)
4420 })
4421 } else {
4422 Task::ready(Err(anyhow!("No upstream client or local language server")))
4423 }
4424 }
4425
4426 pub fn resolve_completions(
4427 &self,
4428 buffer: Entity<Buffer>,
4429 completion_indices: Vec<usize>,
4430 completions: Rc<RefCell<Box<[Completion]>>>,
4431 cx: &mut Context<Self>,
4432 ) -> Task<Result<bool>> {
4433 let client = self.upstream_client();
4434
4435 let buffer_id = buffer.read(cx).remote_id();
4436 let buffer_snapshot = buffer.read(cx).snapshot();
4437
4438 cx.spawn(move |this, cx| async move {
4439 let mut did_resolve = false;
4440 if let Some((client, project_id)) = client {
4441 for completion_index in completion_indices {
4442 let server_id = completions.borrow()[completion_index].server_id;
4443
4444 if Self::resolve_completion_remote(
4445 project_id,
4446 server_id,
4447 buffer_id,
4448 completions.clone(),
4449 completion_index,
4450 client.clone(),
4451 )
4452 .await
4453 .log_err()
4454 .is_some()
4455 {
4456 did_resolve = true;
4457 }
4458 }
4459 } else {
4460 for completion_index in completion_indices {
4461 let server_id = completions.borrow()[completion_index].server_id;
4462
4463 let server_and_adapter = this
4464 .read_with(&cx, |lsp_store, _| {
4465 let server = lsp_store.language_server_for_id(server_id)?;
4466 let adapter =
4467 lsp_store.language_server_adapter_for_id(server.server_id())?;
4468 Some((server, adapter))
4469 })
4470 .ok()
4471 .flatten();
4472 let Some((server, adapter)) = server_and_adapter else {
4473 continue;
4474 };
4475
4476 let resolved = Self::resolve_completion_local(
4477 server,
4478 &buffer_snapshot,
4479 completions.clone(),
4480 completion_index,
4481 )
4482 .await
4483 .log_err()
4484 .is_some();
4485 if resolved {
4486 Self::regenerate_completion_labels(
4487 adapter,
4488 &buffer_snapshot,
4489 completions.clone(),
4490 completion_index,
4491 )
4492 .await
4493 .log_err();
4494 did_resolve = true;
4495 }
4496 }
4497 }
4498
4499 Ok(did_resolve)
4500 })
4501 }
4502
4503 async fn resolve_completion_local(
4504 server: Arc<lsp::LanguageServer>,
4505 snapshot: &BufferSnapshot,
4506 completions: Rc<RefCell<Box<[Completion]>>>,
4507 completion_index: usize,
4508 ) -> Result<()> {
4509 let can_resolve = server
4510 .capabilities()
4511 .completion_provider
4512 .as_ref()
4513 .and_then(|options| options.resolve_provider)
4514 .unwrap_or(false);
4515 if !can_resolve {
4516 return Ok(());
4517 }
4518
4519 let request = {
4520 let completion = &completions.borrow()[completion_index];
4521 if completion.resolved {
4522 return Ok(());
4523 }
4524 server.request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion.clone())
4525 };
4526 let completion_item = request.await?;
4527
4528 if let Some(text_edit) = completion_item.text_edit.as_ref() {
4529 // Technically we don't have to parse the whole `text_edit`, since the only
4530 // language server we currently use that does update `text_edit` in `completionItem/resolve`
4531 // is `typescript-language-server` and they only update `text_edit.new_text`.
4532 // But we should not rely on that.
4533 let edit = parse_completion_text_edit(text_edit, snapshot);
4534
4535 if let Some((old_range, mut new_text)) = edit {
4536 LineEnding::normalize(&mut new_text);
4537
4538 let mut completions = completions.borrow_mut();
4539 let completion = &mut completions[completion_index];
4540
4541 completion.new_text = new_text;
4542 completion.old_range = old_range;
4543 }
4544 }
4545 if completion_item.insert_text_format == Some(InsertTextFormat::SNIPPET) {
4546 // vtsls might change the type of completion after resolution.
4547 let mut completions = completions.borrow_mut();
4548 let completion = &mut completions[completion_index];
4549 if completion_item.insert_text_format != completion.lsp_completion.insert_text_format {
4550 completion.lsp_completion.insert_text_format = completion_item.insert_text_format;
4551 }
4552 }
4553
4554 let mut completions = completions.borrow_mut();
4555 let completion = &mut completions[completion_index];
4556 completion.lsp_completion = completion_item;
4557 completion.resolved = true;
4558 Ok(())
4559 }
4560
4561 async fn regenerate_completion_labels(
4562 adapter: Arc<CachedLspAdapter>,
4563 snapshot: &BufferSnapshot,
4564 completions: Rc<RefCell<Box<[Completion]>>>,
4565 completion_index: usize,
4566 ) -> Result<()> {
4567 let completion_item = completions.borrow()[completion_index]
4568 .lsp_completion
4569 .clone();
4570 if let Some(lsp_documentation) = completion_item.documentation.clone() {
4571 let mut completions = completions.borrow_mut();
4572 let completion = &mut completions[completion_index];
4573 completion.documentation = Some(lsp_documentation.into());
4574 } else {
4575 let mut completions = completions.borrow_mut();
4576 let completion = &mut completions[completion_index];
4577 completion.documentation = Some(CompletionDocumentation::Undocumented);
4578 }
4579
4580 // NB: Zed does not have `details` inside the completion resolve capabilities, but certain language servers violate the spec and do not return `details` immediately, e.g. https://github.com/yioneko/vtsls/issues/213
4581 // So we have to update the label here anyway...
4582 let language = snapshot.language();
4583 let mut new_label = match language {
4584 Some(language) => {
4585 adapter
4586 .labels_for_completions(&[completion_item.clone()], language)
4587 .await?
4588 }
4589 None => Vec::new(),
4590 }
4591 .pop()
4592 .flatten()
4593 .unwrap_or_else(|| {
4594 CodeLabel::fallback_for_completion(
4595 &completion_item,
4596 language.map(|language| language.as_ref()),
4597 )
4598 });
4599 ensure_uniform_list_compatible_label(&mut new_label);
4600
4601 let mut completions = completions.borrow_mut();
4602 let completion = &mut completions[completion_index];
4603 if completion.label.filter_text() == new_label.filter_text() {
4604 completion.label = new_label;
4605 } else {
4606 log::error!(
4607 "Resolved completion changed display label from {} to {}. \
4608 Refusing to apply this because it changes the fuzzy match text from {} to {}",
4609 completion.label.text(),
4610 new_label.text(),
4611 completion.label.filter_text(),
4612 new_label.filter_text()
4613 );
4614 }
4615
4616 Ok(())
4617 }
4618
4619 #[allow(clippy::too_many_arguments)]
4620 async fn resolve_completion_remote(
4621 project_id: u64,
4622 server_id: LanguageServerId,
4623 buffer_id: BufferId,
4624 completions: Rc<RefCell<Box<[Completion]>>>,
4625 completion_index: usize,
4626 client: AnyProtoClient,
4627 ) -> Result<()> {
4628 let lsp_completion = {
4629 let completion = &completions.borrow()[completion_index];
4630 if completion.resolved {
4631 return Ok(());
4632 }
4633 serde_json::to_string(&completion.lsp_completion)
4634 .unwrap()
4635 .into_bytes()
4636 };
4637 let request = proto::ResolveCompletionDocumentation {
4638 project_id,
4639 language_server_id: server_id.0 as u64,
4640 lsp_completion,
4641 buffer_id: buffer_id.into(),
4642 };
4643
4644 let response = client
4645 .request(request)
4646 .await
4647 .context("completion documentation resolve proto request")?;
4648 let lsp_completion = serde_json::from_slice(&response.lsp_completion)?;
4649
4650 let documentation = if response.documentation.is_empty() {
4651 CompletionDocumentation::Undocumented
4652 } else if response.documentation_is_markdown {
4653 CompletionDocumentation::MultiLineMarkdown(response.documentation.into())
4654 } else if response.documentation.lines().count() <= 1 {
4655 CompletionDocumentation::SingleLine(response.documentation.into())
4656 } else {
4657 CompletionDocumentation::MultiLinePlainText(response.documentation.into())
4658 };
4659
4660 let mut completions = completions.borrow_mut();
4661 let completion = &mut completions[completion_index];
4662 completion.documentation = Some(documentation);
4663 completion.lsp_completion = lsp_completion;
4664 completion.resolved = true;
4665
4666 let old_range = response
4667 .old_start
4668 .and_then(deserialize_anchor)
4669 .zip(response.old_end.and_then(deserialize_anchor));
4670 if let Some((old_start, old_end)) = old_range {
4671 if !response.new_text.is_empty() {
4672 completion.new_text = response.new_text;
4673 completion.old_range = old_start..old_end;
4674 }
4675 }
4676
4677 Ok(())
4678 }
4679
4680 pub fn apply_additional_edits_for_completion(
4681 &self,
4682 buffer_handle: Entity<Buffer>,
4683 completions: Rc<RefCell<Box<[Completion]>>>,
4684 completion_index: usize,
4685 push_to_history: bool,
4686 cx: &mut Context<Self>,
4687 ) -> Task<Result<Option<Transaction>>> {
4688 if let Some((client, project_id)) = self.upstream_client() {
4689 let buffer = buffer_handle.read(cx);
4690 let buffer_id = buffer.remote_id();
4691 cx.spawn(move |_, mut cx| async move {
4692 let request = {
4693 let completion = completions.borrow()[completion_index].clone();
4694 proto::ApplyCompletionAdditionalEdits {
4695 project_id,
4696 buffer_id: buffer_id.into(),
4697 completion: Some(Self::serialize_completion(&CoreCompletion {
4698 old_range: completion.old_range,
4699 new_text: completion.new_text,
4700 server_id: completion.server_id,
4701 lsp_completion: completion.lsp_completion,
4702 resolved: completion.resolved,
4703 })),
4704 }
4705 };
4706
4707 let response = client.request(request).await?;
4708 completions.borrow_mut()[completion_index].resolved = true;
4709
4710 if let Some(transaction) = response.transaction {
4711 let transaction = language::proto::deserialize_transaction(transaction)?;
4712 buffer_handle
4713 .update(&mut cx, |buffer, _| {
4714 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
4715 })?
4716 .await?;
4717 if push_to_history {
4718 buffer_handle.update(&mut cx, |buffer, _| {
4719 buffer.push_transaction(transaction.clone(), Instant::now());
4720 })?;
4721 }
4722 Ok(Some(transaction))
4723 } else {
4724 Ok(None)
4725 }
4726 })
4727 } else {
4728 let server_id = completions.borrow()[completion_index].server_id;
4729 let Some(server) = buffer_handle.update(cx, |buffer, cx| {
4730 Some(
4731 self.language_server_for_local_buffer(buffer, server_id, cx)?
4732 .1
4733 .clone(),
4734 )
4735 }) else {
4736 return Task::ready(Ok(None));
4737 };
4738 let snapshot = buffer_handle.read(&cx).snapshot();
4739
4740 cx.spawn(move |this, mut cx| async move {
4741 Self::resolve_completion_local(
4742 server.clone(),
4743 &snapshot,
4744 completions.clone(),
4745 completion_index,
4746 )
4747 .await
4748 .context("resolving completion")?;
4749 let completion = completions.borrow()[completion_index].clone();
4750 let additional_text_edits = completion.lsp_completion.additional_text_edits;
4751 if let Some(edits) = additional_text_edits {
4752 let edits = this
4753 .update(&mut cx, |this, cx| {
4754 this.as_local_mut().unwrap().edits_from_lsp(
4755 &buffer_handle,
4756 edits,
4757 server.server_id(),
4758 None,
4759 cx,
4760 )
4761 })?
4762 .await?;
4763
4764 buffer_handle.update(&mut cx, |buffer, cx| {
4765 buffer.finalize_last_transaction();
4766 buffer.start_transaction();
4767
4768 for (range, text) in edits {
4769 let primary = &completion.old_range;
4770 let start_within = primary.start.cmp(&range.start, buffer).is_le()
4771 && primary.end.cmp(&range.start, buffer).is_ge();
4772 let end_within = range.start.cmp(&primary.end, buffer).is_le()
4773 && range.end.cmp(&primary.end, buffer).is_ge();
4774
4775 //Skip additional edits which overlap with the primary completion edit
4776 //https://github.com/zed-industries/zed/pull/1871
4777 if !start_within && !end_within {
4778 buffer.edit([(range, text)], None, cx);
4779 }
4780 }
4781
4782 let transaction = if buffer.end_transaction(cx).is_some() {
4783 let transaction = buffer.finalize_last_transaction().unwrap().clone();
4784 if !push_to_history {
4785 buffer.forget_transaction(transaction.id);
4786 }
4787 Some(transaction)
4788 } else {
4789 None
4790 };
4791 Ok(transaction)
4792 })?
4793 } else {
4794 Ok(None)
4795 }
4796 })
4797 }
4798 }
4799
4800 pub fn inlay_hints(
4801 &mut self,
4802 buffer_handle: Entity<Buffer>,
4803 range: Range<Anchor>,
4804 cx: &mut Context<Self>,
4805 ) -> Task<anyhow::Result<Vec<InlayHint>>> {
4806 let buffer = buffer_handle.read(cx);
4807 let range_start = range.start;
4808 let range_end = range.end;
4809 let buffer_id = buffer.remote_id().into();
4810 let lsp_request = InlayHints { range };
4811
4812 if let Some((client, project_id)) = self.upstream_client() {
4813 let request = proto::InlayHints {
4814 project_id,
4815 buffer_id,
4816 start: Some(serialize_anchor(&range_start)),
4817 end: Some(serialize_anchor(&range_end)),
4818 version: serialize_version(&buffer_handle.read(cx).version()),
4819 };
4820 cx.spawn(move |project, cx| async move {
4821 let response = client
4822 .request(request)
4823 .await
4824 .context("inlay hints proto request")?;
4825 LspCommand::response_from_proto(
4826 lsp_request,
4827 response,
4828 project.upgrade().ok_or_else(|| anyhow!("No project"))?,
4829 buffer_handle.clone(),
4830 cx.clone(),
4831 )
4832 .await
4833 .context("inlay hints proto response conversion")
4834 })
4835 } else {
4836 let lsp_request_task = self.request_lsp(
4837 buffer_handle.clone(),
4838 LanguageServerToQuery::FirstCapable,
4839 lsp_request,
4840 cx,
4841 );
4842 cx.spawn(move |_, mut cx| async move {
4843 buffer_handle
4844 .update(&mut cx, |buffer, _| {
4845 buffer.wait_for_edits(vec![range_start.timestamp, range_end.timestamp])
4846 })?
4847 .await
4848 .context("waiting for inlay hint request range edits")?;
4849 lsp_request_task.await.context("inlay hints LSP request")
4850 })
4851 }
4852 }
4853
4854 pub fn signature_help<T: ToPointUtf16>(
4855 &mut self,
4856 buffer: &Entity<Buffer>,
4857 position: T,
4858 cx: &mut Context<Self>,
4859 ) -> Task<Vec<SignatureHelp>> {
4860 let position = position.to_point_utf16(buffer.read(cx));
4861
4862 if let Some((client, upstream_project_id)) = self.upstream_client() {
4863 let request_task = client.request(proto::MultiLspQuery {
4864 buffer_id: buffer.read(cx).remote_id().into(),
4865 version: serialize_version(&buffer.read(cx).version()),
4866 project_id: upstream_project_id,
4867 strategy: Some(proto::multi_lsp_query::Strategy::All(
4868 proto::AllLanguageServers {},
4869 )),
4870 request: Some(proto::multi_lsp_query::Request::GetSignatureHelp(
4871 GetSignatureHelp { position }.to_proto(upstream_project_id, buffer.read(cx)),
4872 )),
4873 });
4874 let buffer = buffer.clone();
4875 cx.spawn(|weak_project, cx| async move {
4876 let Some(project) = weak_project.upgrade() else {
4877 return Vec::new();
4878 };
4879 join_all(
4880 request_task
4881 .await
4882 .log_err()
4883 .map(|response| response.responses)
4884 .unwrap_or_default()
4885 .into_iter()
4886 .filter_map(|lsp_response| match lsp_response.response? {
4887 proto::lsp_response::Response::GetSignatureHelpResponse(response) => {
4888 Some(response)
4889 }
4890 unexpected => {
4891 debug_panic!("Unexpected response: {unexpected:?}");
4892 None
4893 }
4894 })
4895 .map(|signature_response| {
4896 let response = GetSignatureHelp { position }.response_from_proto(
4897 signature_response,
4898 project.clone(),
4899 buffer.clone(),
4900 cx.clone(),
4901 );
4902 async move { response.await.log_err().flatten() }
4903 }),
4904 )
4905 .await
4906 .into_iter()
4907 .flatten()
4908 .collect()
4909 })
4910 } else {
4911 let all_actions_task = self.request_multiple_lsp_locally(
4912 buffer,
4913 Some(position),
4914 GetSignatureHelp { position },
4915 cx,
4916 );
4917 cx.spawn(|_, _| async move {
4918 all_actions_task
4919 .await
4920 .into_iter()
4921 .flatten()
4922 .filter(|help| !help.label.is_empty())
4923 .collect::<Vec<_>>()
4924 })
4925 }
4926 }
4927
4928 pub fn hover(
4929 &mut self,
4930 buffer: &Entity<Buffer>,
4931 position: PointUtf16,
4932 cx: &mut Context<Self>,
4933 ) -> Task<Vec<Hover>> {
4934 if let Some((client, upstream_project_id)) = self.upstream_client() {
4935 let request_task = client.request(proto::MultiLspQuery {
4936 buffer_id: buffer.read(cx).remote_id().into(),
4937 version: serialize_version(&buffer.read(cx).version()),
4938 project_id: upstream_project_id,
4939 strategy: Some(proto::multi_lsp_query::Strategy::All(
4940 proto::AllLanguageServers {},
4941 )),
4942 request: Some(proto::multi_lsp_query::Request::GetHover(
4943 GetHover { position }.to_proto(upstream_project_id, buffer.read(cx)),
4944 )),
4945 });
4946 let buffer = buffer.clone();
4947 cx.spawn(|weak_project, cx| async move {
4948 let Some(project) = weak_project.upgrade() else {
4949 return Vec::new();
4950 };
4951 join_all(
4952 request_task
4953 .await
4954 .log_err()
4955 .map(|response| response.responses)
4956 .unwrap_or_default()
4957 .into_iter()
4958 .filter_map(|lsp_response| match lsp_response.response? {
4959 proto::lsp_response::Response::GetHoverResponse(response) => {
4960 Some(response)
4961 }
4962 unexpected => {
4963 debug_panic!("Unexpected response: {unexpected:?}");
4964 None
4965 }
4966 })
4967 .map(|hover_response| {
4968 let response = GetHover { position }.response_from_proto(
4969 hover_response,
4970 project.clone(),
4971 buffer.clone(),
4972 cx.clone(),
4973 );
4974 async move {
4975 response
4976 .await
4977 .log_err()
4978 .flatten()
4979 .and_then(remove_empty_hover_blocks)
4980 }
4981 }),
4982 )
4983 .await
4984 .into_iter()
4985 .flatten()
4986 .collect()
4987 })
4988 } else {
4989 let all_actions_task = self.request_multiple_lsp_locally(
4990 buffer,
4991 Some(position),
4992 GetHover { position },
4993 cx,
4994 );
4995 cx.spawn(|_, _| async move {
4996 all_actions_task
4997 .await
4998 .into_iter()
4999 .filter_map(|hover| remove_empty_hover_blocks(hover?))
5000 .collect::<Vec<Hover>>()
5001 })
5002 }
5003 }
5004
5005 pub fn symbols(&self, query: &str, cx: &mut Context<Self>) -> Task<Result<Vec<Symbol>>> {
5006 let language_registry = self.languages.clone();
5007
5008 if let Some((upstream_client, project_id)) = self.upstream_client().as_ref() {
5009 let request = upstream_client.request(proto::GetProjectSymbols {
5010 project_id: *project_id,
5011 query: query.to_string(),
5012 });
5013 cx.foreground_executor().spawn(async move {
5014 let response = request.await?;
5015 let mut symbols = Vec::new();
5016 let core_symbols = response
5017 .symbols
5018 .into_iter()
5019 .filter_map(|symbol| Self::deserialize_symbol(symbol).log_err())
5020 .collect::<Vec<_>>();
5021 populate_labels_for_symbols(core_symbols, &language_registry, None, &mut symbols)
5022 .await;
5023 Ok(symbols)
5024 })
5025 } else if let Some(local) = self.as_local() {
5026 struct WorkspaceSymbolsResult {
5027 server_id: LanguageServerId,
5028 lsp_adapter: Arc<CachedLspAdapter>,
5029 worktree: WeakEntity<Worktree>,
5030 worktree_abs_path: Arc<Path>,
5031 lsp_symbols: Vec<(String, SymbolKind, lsp::Location)>,
5032 }
5033
5034 let mut requests = Vec::new();
5035 let mut requested_servers = BTreeSet::new();
5036 'next_server: for ((worktree_id, _), server_ids) in local.language_server_ids.iter() {
5037 let Some(worktree_handle) = self
5038 .worktree_store
5039 .read(cx)
5040 .worktree_for_id(*worktree_id, cx)
5041 else {
5042 continue;
5043 };
5044 let worktree = worktree_handle.read(cx);
5045 if !worktree.is_visible() {
5046 continue;
5047 }
5048
5049 let mut servers_to_query = server_ids
5050 .difference(&requested_servers)
5051 .cloned()
5052 .collect::<BTreeSet<_>>();
5053 for server_id in &servers_to_query {
5054 let (lsp_adapter, server) = match local.language_servers.get(server_id) {
5055 Some(LanguageServerState::Running {
5056 adapter, server, ..
5057 }) => (adapter.clone(), server),
5058
5059 _ => continue 'next_server,
5060 };
5061 let worktree_abs_path = worktree.abs_path().clone();
5062 let worktree_handle = worktree_handle.clone();
5063 let server_id = server.server_id();
5064 requests.push(
5065 server
5066 .request::<lsp::request::WorkspaceSymbolRequest>(
5067 lsp::WorkspaceSymbolParams {
5068 query: query.to_string(),
5069 ..Default::default()
5070 },
5071 )
5072 .log_err()
5073 .map(move |response| {
5074 let lsp_symbols = response.flatten().map(|symbol_response| match symbol_response {
5075 lsp::WorkspaceSymbolResponse::Flat(flat_responses) => {
5076 flat_responses.into_iter().map(|lsp_symbol| {
5077 (lsp_symbol.name, lsp_symbol.kind, lsp_symbol.location)
5078 }).collect::<Vec<_>>()
5079 }
5080 lsp::WorkspaceSymbolResponse::Nested(nested_responses) => {
5081 nested_responses.into_iter().filter_map(|lsp_symbol| {
5082 let location = match lsp_symbol.location {
5083 OneOf::Left(location) => location,
5084 OneOf::Right(_) => {
5085 log::error!("Unexpected: client capabilities forbid symbol resolutions in workspace.symbol.resolveSupport");
5086 return None
5087 }
5088 };
5089 Some((lsp_symbol.name, lsp_symbol.kind, location))
5090 }).collect::<Vec<_>>()
5091 }
5092 }).unwrap_or_default();
5093
5094 WorkspaceSymbolsResult {
5095 server_id,
5096 lsp_adapter,
5097 worktree: worktree_handle.downgrade(),
5098 worktree_abs_path,
5099 lsp_symbols,
5100 }
5101 }),
5102 );
5103 }
5104 requested_servers.append(&mut servers_to_query);
5105 }
5106
5107 cx.spawn(move |this, mut cx| async move {
5108 let responses = futures::future::join_all(requests).await;
5109 let this = match this.upgrade() {
5110 Some(this) => this,
5111 None => return Ok(Vec::new()),
5112 };
5113
5114 let mut symbols = Vec::new();
5115 for result in responses {
5116 let core_symbols = this.update(&mut cx, |this, cx| {
5117 result
5118 .lsp_symbols
5119 .into_iter()
5120 .filter_map(|(symbol_name, symbol_kind, symbol_location)| {
5121 let abs_path = symbol_location.uri.to_file_path().ok()?;
5122 let source_worktree = result.worktree.upgrade()?;
5123 let source_worktree_id = source_worktree.read(cx).id();
5124
5125 let path;
5126 let worktree;
5127 if let Some((tree, rel_path)) =
5128 this.worktree_store.read(cx).find_worktree(&abs_path, cx)
5129 {
5130 worktree = tree;
5131 path = rel_path;
5132 } else {
5133 worktree = source_worktree.clone();
5134 path = relativize_path(&result.worktree_abs_path, &abs_path);
5135 }
5136
5137 let worktree_id = worktree.read(cx).id();
5138 let project_path = ProjectPath {
5139 worktree_id,
5140 path: path.into(),
5141 };
5142 let signature = this.symbol_signature(&project_path);
5143 Some(CoreSymbol {
5144 source_language_server_id: result.server_id,
5145 language_server_name: result.lsp_adapter.name.clone(),
5146 source_worktree_id,
5147 path: project_path,
5148 kind: symbol_kind,
5149 name: symbol_name,
5150 range: range_from_lsp(symbol_location.range),
5151 signature,
5152 })
5153 })
5154 .collect()
5155 })?;
5156
5157 populate_labels_for_symbols(
5158 core_symbols,
5159 &language_registry,
5160 Some(result.lsp_adapter),
5161 &mut symbols,
5162 )
5163 .await;
5164 }
5165
5166 Ok(symbols)
5167 })
5168 } else {
5169 Task::ready(Err(anyhow!("No upstream client or local language server")))
5170 }
5171 }
5172
5173 pub fn diagnostic_summary(&self, include_ignored: bool, cx: &App) -> DiagnosticSummary {
5174 let mut summary = DiagnosticSummary::default();
5175 for (_, _, path_summary) in self.diagnostic_summaries(include_ignored, cx) {
5176 summary.error_count += path_summary.error_count;
5177 summary.warning_count += path_summary.warning_count;
5178 }
5179 summary
5180 }
5181
5182 pub fn diagnostic_summaries<'a>(
5183 &'a self,
5184 include_ignored: bool,
5185 cx: &'a App,
5186 ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
5187 self.worktree_store
5188 .read(cx)
5189 .visible_worktrees(cx)
5190 .filter_map(|worktree| {
5191 let worktree = worktree.read(cx);
5192 Some((worktree, self.diagnostic_summaries.get(&worktree.id())?))
5193 })
5194 .flat_map(move |(worktree, summaries)| {
5195 let worktree_id = worktree.id();
5196 summaries
5197 .iter()
5198 .filter(move |(path, _)| {
5199 include_ignored
5200 || worktree
5201 .entry_for_path(path.as_ref())
5202 .map_or(false, |entry| !entry.is_ignored)
5203 })
5204 .flat_map(move |(path, summaries)| {
5205 summaries.iter().map(move |(server_id, summary)| {
5206 (
5207 ProjectPath {
5208 worktree_id,
5209 path: path.clone(),
5210 },
5211 *server_id,
5212 *summary,
5213 )
5214 })
5215 })
5216 })
5217 }
5218
5219 pub fn on_buffer_edited(
5220 &mut self,
5221 buffer: Entity<Buffer>,
5222 cx: &mut Context<Self>,
5223 ) -> Option<()> {
5224 let language_servers: Vec<_> = buffer.update(cx, |buffer, cx| {
5225 Some(
5226 self.as_local()?
5227 .language_servers_for_buffer(buffer, cx)
5228 .map(|i| i.1.clone())
5229 .collect(),
5230 )
5231 })?;
5232
5233 let buffer = buffer.read(cx);
5234 let file = File::from_dyn(buffer.file())?;
5235 let abs_path = file.as_local()?.abs_path(cx);
5236 let uri = lsp::Url::from_file_path(abs_path).unwrap();
5237 let next_snapshot = buffer.text_snapshot();
5238 for language_server in language_servers {
5239 let language_server = language_server.clone();
5240
5241 let buffer_snapshots = self
5242 .as_local_mut()
5243 .unwrap()
5244 .buffer_snapshots
5245 .get_mut(&buffer.remote_id())
5246 .and_then(|m| m.get_mut(&language_server.server_id()))?;
5247 let previous_snapshot = buffer_snapshots.last()?;
5248
5249 let build_incremental_change = || {
5250 buffer
5251 .edits_since::<(PointUtf16, usize)>(previous_snapshot.snapshot.version())
5252 .map(|edit| {
5253 let edit_start = edit.new.start.0;
5254 let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0);
5255 let new_text = next_snapshot
5256 .text_for_range(edit.new.start.1..edit.new.end.1)
5257 .collect();
5258 lsp::TextDocumentContentChangeEvent {
5259 range: Some(lsp::Range::new(
5260 point_to_lsp(edit_start),
5261 point_to_lsp(edit_end),
5262 )),
5263 range_length: None,
5264 text: new_text,
5265 }
5266 })
5267 .collect()
5268 };
5269
5270 let document_sync_kind = language_server
5271 .capabilities()
5272 .text_document_sync
5273 .as_ref()
5274 .and_then(|sync| match sync {
5275 lsp::TextDocumentSyncCapability::Kind(kind) => Some(*kind),
5276 lsp::TextDocumentSyncCapability::Options(options) => options.change,
5277 });
5278
5279 let content_changes: Vec<_> = match document_sync_kind {
5280 Some(lsp::TextDocumentSyncKind::FULL) => {
5281 vec![lsp::TextDocumentContentChangeEvent {
5282 range: None,
5283 range_length: None,
5284 text: next_snapshot.text(),
5285 }]
5286 }
5287 Some(lsp::TextDocumentSyncKind::INCREMENTAL) => build_incremental_change(),
5288 _ => {
5289 #[cfg(any(test, feature = "test-support"))]
5290 {
5291 build_incremental_change()
5292 }
5293
5294 #[cfg(not(any(test, feature = "test-support")))]
5295 {
5296 continue;
5297 }
5298 }
5299 };
5300
5301 let next_version = previous_snapshot.version + 1;
5302 buffer_snapshots.push(LspBufferSnapshot {
5303 version: next_version,
5304 snapshot: next_snapshot.clone(),
5305 });
5306
5307 language_server
5308 .notify::<lsp::notification::DidChangeTextDocument>(
5309 &lsp::DidChangeTextDocumentParams {
5310 text_document: lsp::VersionedTextDocumentIdentifier::new(
5311 uri.clone(),
5312 next_version,
5313 ),
5314 content_changes,
5315 },
5316 )
5317 .log_err();
5318 }
5319
5320 None
5321 }
5322
5323 pub fn on_buffer_saved(
5324 &mut self,
5325 buffer: Entity<Buffer>,
5326 cx: &mut Context<Self>,
5327 ) -> Option<()> {
5328 let file = File::from_dyn(buffer.read(cx).file())?;
5329 let worktree_id = file.worktree_id(cx);
5330 let abs_path = file.as_local()?.abs_path(cx);
5331 let text_document = lsp::TextDocumentIdentifier {
5332 uri: lsp::Url::from_file_path(abs_path).log_err()?,
5333 };
5334 let local = self.as_local()?;
5335
5336 for server in local.language_servers_for_worktree(worktree_id) {
5337 if let Some(include_text) = include_text(server.as_ref()) {
5338 let text = if include_text {
5339 Some(buffer.read(cx).text())
5340 } else {
5341 None
5342 };
5343 server
5344 .notify::<lsp::notification::DidSaveTextDocument>(
5345 &lsp::DidSaveTextDocumentParams {
5346 text_document: text_document.clone(),
5347 text,
5348 },
5349 )
5350 .log_err();
5351 }
5352 }
5353
5354 let language_servers = buffer.update(cx, |buffer, cx| {
5355 local.language_server_ids_for_buffer(buffer, cx)
5356 });
5357 for language_server_id in language_servers {
5358 self.simulate_disk_based_diagnostics_events_if_needed(language_server_id, cx);
5359 }
5360
5361 None
5362 }
5363
5364 pub(crate) async fn refresh_workspace_configurations(
5365 this: &WeakEntity<Self>,
5366 fs: Arc<dyn Fs>,
5367 mut cx: AsyncApp,
5368 ) {
5369 maybe!(async move {
5370 let servers = this
5371 .update(&mut cx, |this, cx| {
5372 let Some(local) = this.as_local() else {
5373 return Vec::default();
5374 };
5375 local
5376 .language_server_ids
5377 .iter()
5378 .flat_map(|((worktree_id, _), server_ids)| {
5379 let worktree = this
5380 .worktree_store
5381 .read(cx)
5382 .worktree_for_id(*worktree_id, cx);
5383 let delegate = worktree.map(|worktree| {
5384 LocalLspAdapterDelegate::new(
5385 local.languages.clone(),
5386 &local.environment,
5387 cx.weak_entity(),
5388 &worktree,
5389 local.http_client.clone(),
5390 local.fs.clone(),
5391 cx,
5392 )
5393 });
5394
5395 server_ids.iter().filter_map(move |server_id| {
5396 let states = local.language_servers.get(server_id)?;
5397
5398 match states {
5399 LanguageServerState::Starting { .. } => None,
5400 LanguageServerState::Running {
5401 adapter, server, ..
5402 } => Some((
5403 adapter.adapter.clone(),
5404 server.clone(),
5405 delegate.clone()? as Arc<dyn LspAdapterDelegate>,
5406 )),
5407 }
5408 })
5409 })
5410 .collect::<Vec<_>>()
5411 })
5412 .ok()?;
5413
5414 let toolchain_store = this
5415 .update(&mut cx, |this, cx| this.toolchain_store(cx))
5416 .ok()?;
5417 for (adapter, server, delegate) in servers {
5418 let settings = adapter
5419 .workspace_configuration(
5420 fs.as_ref(),
5421 &delegate,
5422 toolchain_store.clone(),
5423 &mut cx,
5424 )
5425 .await
5426 .ok()?;
5427
5428 server
5429 .notify::<lsp::notification::DidChangeConfiguration>(
5430 &lsp::DidChangeConfigurationParams { settings },
5431 )
5432 .ok();
5433 }
5434 Some(())
5435 })
5436 .await;
5437 }
5438
5439 fn toolchain_store(&self, cx: &App) -> Arc<dyn LanguageToolchainStore> {
5440 if let Some(toolchain_store) = self.toolchain_store.as_ref() {
5441 toolchain_store.read(cx).as_language_toolchain_store()
5442 } else {
5443 Arc::new(EmptyToolchainStore)
5444 }
5445 }
5446 fn maintain_workspace_config(
5447 fs: Arc<dyn Fs>,
5448 external_refresh_requests: watch::Receiver<()>,
5449 cx: &mut Context<Self>,
5450 ) -> Task<Result<()>> {
5451 let (mut settings_changed_tx, mut settings_changed_rx) = watch::channel();
5452 let _ = postage::stream::Stream::try_recv(&mut settings_changed_rx);
5453
5454 let settings_observation = cx.observe_global::<SettingsStore>(move |_, _| {
5455 *settings_changed_tx.borrow_mut() = ();
5456 });
5457
5458 let mut joint_future =
5459 futures::stream::select(settings_changed_rx, external_refresh_requests);
5460 cx.spawn(move |this, cx| async move {
5461 while let Some(()) = joint_future.next().await {
5462 Self::refresh_workspace_configurations(&this, fs.clone(), cx.clone()).await;
5463 }
5464
5465 drop(settings_observation);
5466 anyhow::Ok(())
5467 })
5468 }
5469
5470 pub fn language_servers_for_local_buffer<'a>(
5471 &'a self,
5472 buffer: &Buffer,
5473 cx: &mut App,
5474 ) -> impl Iterator<Item = (&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
5475 let local = self.as_local();
5476 let language_server_ids = local
5477 .map(|local| local.language_server_ids_for_buffer(buffer, cx))
5478 .unwrap_or_default();
5479
5480 language_server_ids
5481 .into_iter()
5482 .filter_map(
5483 move |server_id| match local?.language_servers.get(&server_id)? {
5484 LanguageServerState::Running {
5485 adapter, server, ..
5486 } => Some((adapter, server)),
5487 _ => None,
5488 },
5489 )
5490 }
5491
5492 pub fn language_server_for_local_buffer<'a>(
5493 &'a self,
5494 buffer: &'a Buffer,
5495 server_id: LanguageServerId,
5496 cx: &'a mut App,
5497 ) -> Option<(&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
5498 self.as_local()?
5499 .language_servers_for_buffer(buffer, cx)
5500 .find(|(_, s)| s.server_id() == server_id)
5501 }
5502
5503 fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut Context<Self>) {
5504 self.diagnostic_summaries.remove(&id_to_remove);
5505 if let Some(local) = self.as_local_mut() {
5506 let to_remove = local.remove_worktree(id_to_remove, cx);
5507 for server in to_remove {
5508 self.language_server_statuses.remove(&server);
5509 }
5510 }
5511 }
5512
5513 pub fn shared(
5514 &mut self,
5515 project_id: u64,
5516 downstream_client: AnyProtoClient,
5517 _: &mut Context<Self>,
5518 ) {
5519 self.downstream_client = Some((downstream_client.clone(), project_id));
5520
5521 for (server_id, status) in &self.language_server_statuses {
5522 downstream_client
5523 .send(proto::StartLanguageServer {
5524 project_id,
5525 server: Some(proto::LanguageServer {
5526 id: server_id.0 as u64,
5527 name: status.name.clone(),
5528 worktree_id: None,
5529 }),
5530 })
5531 .log_err();
5532 }
5533 }
5534
5535 pub fn disconnected_from_host(&mut self) {
5536 self.downstream_client.take();
5537 }
5538
5539 pub fn disconnected_from_ssh_remote(&mut self) {
5540 if let LspStoreMode::Remote(RemoteLspStore {
5541 upstream_client, ..
5542 }) = &mut self.mode
5543 {
5544 upstream_client.take();
5545 }
5546 }
5547
5548 pub(crate) fn set_language_server_statuses_from_proto(
5549 &mut self,
5550 language_servers: Vec<proto::LanguageServer>,
5551 ) {
5552 self.language_server_statuses = language_servers
5553 .into_iter()
5554 .map(|server| {
5555 (
5556 LanguageServerId(server.id as usize),
5557 LanguageServerStatus {
5558 name: server.name,
5559 pending_work: Default::default(),
5560 has_pending_diagnostic_updates: false,
5561 progress_tokens: Default::default(),
5562 },
5563 )
5564 })
5565 .collect();
5566 }
5567
5568 fn register_local_language_server(
5569 &mut self,
5570 worktree: Entity<Worktree>,
5571 language_server_name: LanguageServerName,
5572 language_server_id: LanguageServerId,
5573 cx: &mut App,
5574 ) {
5575 let Some(local) = self.as_local_mut() else {
5576 return;
5577 };
5578 let worktree_id = worktree.read(cx).id();
5579 let path = ProjectPath {
5580 worktree_id,
5581 path: Arc::from("".as_ref()),
5582 };
5583 let delegate = LocalLspAdapterDelegate::from_local_lsp(local, &worktree, cx);
5584 local.lsp_tree.update(cx, |this, cx| {
5585 for node in this.get(
5586 path,
5587 AdapterQuery::Adapter(&language_server_name),
5588 delegate,
5589 cx,
5590 ) {
5591 node.server_id_or_init(|disposition| {
5592 assert_eq!(disposition.server_name, &language_server_name);
5593
5594 language_server_id
5595 });
5596 }
5597 });
5598 local
5599 .language_server_ids
5600 .entry((worktree_id, language_server_name))
5601 .or_default()
5602 .insert(language_server_id);
5603 }
5604
5605 pub fn update_diagnostic_entries(
5606 &mut self,
5607 server_id: LanguageServerId,
5608 abs_path: PathBuf,
5609 version: Option<i32>,
5610 diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
5611 cx: &mut Context<Self>,
5612 ) -> Result<(), anyhow::Error> {
5613 let Some((worktree, relative_path)) =
5614 self.worktree_store.read(cx).find_worktree(&abs_path, cx)
5615 else {
5616 log::warn!("skipping diagnostics update, no worktree found for path {abs_path:?}");
5617 return Ok(());
5618 };
5619
5620 let project_path = ProjectPath {
5621 worktree_id: worktree.read(cx).id(),
5622 path: relative_path.into(),
5623 };
5624
5625 if let Some(buffer) = self.buffer_store.read(cx).get_by_path(&project_path, cx) {
5626 self.as_local_mut().unwrap().update_buffer_diagnostics(
5627 &buffer,
5628 server_id,
5629 version,
5630 diagnostics.clone(),
5631 cx,
5632 )?;
5633 }
5634
5635 let updated = worktree.update(cx, |worktree, cx| {
5636 self.update_worktree_diagnostics(
5637 worktree.id(),
5638 server_id,
5639 project_path.path.clone(),
5640 diagnostics,
5641 cx,
5642 )
5643 })?;
5644 if updated {
5645 cx.emit(LspStoreEvent::DiagnosticsUpdated {
5646 language_server_id: server_id,
5647 path: project_path,
5648 })
5649 }
5650 Ok(())
5651 }
5652
5653 fn update_worktree_diagnostics(
5654 &mut self,
5655 worktree_id: WorktreeId,
5656 server_id: LanguageServerId,
5657 worktree_path: Arc<Path>,
5658 diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
5659 _: &mut Context<Worktree>,
5660 ) -> Result<bool> {
5661 let local = match &mut self.mode {
5662 LspStoreMode::Local(local_lsp_store) => local_lsp_store,
5663 _ => anyhow::bail!("update_worktree_diagnostics called on remote"),
5664 };
5665
5666 let summaries_for_tree = self.diagnostic_summaries.entry(worktree_id).or_default();
5667 let diagnostics_for_tree = local.diagnostics.entry(worktree_id).or_default();
5668 let summaries_by_server_id = summaries_for_tree.entry(worktree_path.clone()).or_default();
5669
5670 let old_summary = summaries_by_server_id
5671 .remove(&server_id)
5672 .unwrap_or_default();
5673
5674 let new_summary = DiagnosticSummary::new(&diagnostics);
5675 if new_summary.is_empty() {
5676 if let Some(diagnostics_by_server_id) = diagnostics_for_tree.get_mut(&worktree_path) {
5677 if let Ok(ix) = diagnostics_by_server_id.binary_search_by_key(&server_id, |e| e.0) {
5678 diagnostics_by_server_id.remove(ix);
5679 }
5680 if diagnostics_by_server_id.is_empty() {
5681 diagnostics_for_tree.remove(&worktree_path);
5682 }
5683 }
5684 } else {
5685 summaries_by_server_id.insert(server_id, new_summary);
5686 let diagnostics_by_server_id = diagnostics_for_tree
5687 .entry(worktree_path.clone())
5688 .or_default();
5689 match diagnostics_by_server_id.binary_search_by_key(&server_id, |e| e.0) {
5690 Ok(ix) => {
5691 diagnostics_by_server_id[ix] = (server_id, diagnostics);
5692 }
5693 Err(ix) => {
5694 diagnostics_by_server_id.insert(ix, (server_id, diagnostics));
5695 }
5696 }
5697 }
5698
5699 if !old_summary.is_empty() || !new_summary.is_empty() {
5700 if let Some((downstream_client, project_id)) = &self.downstream_client {
5701 downstream_client
5702 .send(proto::UpdateDiagnosticSummary {
5703 project_id: *project_id,
5704 worktree_id: worktree_id.to_proto(),
5705 summary: Some(proto::DiagnosticSummary {
5706 path: worktree_path.to_proto(),
5707 language_server_id: server_id.0 as u64,
5708 error_count: new_summary.error_count as u32,
5709 warning_count: new_summary.warning_count as u32,
5710 }),
5711 })
5712 .log_err();
5713 }
5714 }
5715
5716 Ok(!old_summary.is_empty() || !new_summary.is_empty())
5717 }
5718
5719 pub fn open_buffer_for_symbol(
5720 &mut self,
5721 symbol: &Symbol,
5722 cx: &mut Context<Self>,
5723 ) -> Task<Result<Entity<Buffer>>> {
5724 if let Some((client, project_id)) = self.upstream_client() {
5725 let request = client.request(proto::OpenBufferForSymbol {
5726 project_id,
5727 symbol: Some(Self::serialize_symbol(symbol)),
5728 });
5729 cx.spawn(move |this, mut cx| async move {
5730 let response = request.await?;
5731 let buffer_id = BufferId::new(response.buffer_id)?;
5732 this.update(&mut cx, |this, cx| {
5733 this.wait_for_remote_buffer(buffer_id, cx)
5734 })?
5735 .await
5736 })
5737 } else if let Some(local) = self.as_local() {
5738 let Some(language_server_id) = local
5739 .language_server_ids
5740 .get(&(
5741 symbol.source_worktree_id,
5742 symbol.language_server_name.clone(),
5743 ))
5744 .and_then(|ids| {
5745 ids.contains(&symbol.source_language_server_id)
5746 .then_some(symbol.source_language_server_id)
5747 })
5748 else {
5749 return Task::ready(Err(anyhow!(
5750 "language server for worktree and language not found"
5751 )));
5752 };
5753
5754 let worktree_abs_path = if let Some(worktree_abs_path) = self
5755 .worktree_store
5756 .read(cx)
5757 .worktree_for_id(symbol.path.worktree_id, cx)
5758 .map(|worktree| worktree.read(cx).abs_path())
5759 {
5760 worktree_abs_path
5761 } else {
5762 return Task::ready(Err(anyhow!("worktree not found for symbol")));
5763 };
5764
5765 let symbol_abs_path = resolve_path(&worktree_abs_path, &symbol.path.path);
5766 let symbol_uri = if let Ok(uri) = lsp::Url::from_file_path(symbol_abs_path) {
5767 uri
5768 } else {
5769 return Task::ready(Err(anyhow!("invalid symbol path")));
5770 };
5771
5772 self.open_local_buffer_via_lsp(
5773 symbol_uri,
5774 language_server_id,
5775 symbol.language_server_name.clone(),
5776 cx,
5777 )
5778 } else {
5779 Task::ready(Err(anyhow!("no upstream client or local store")))
5780 }
5781 }
5782
5783 pub fn open_local_buffer_via_lsp(
5784 &mut self,
5785 mut abs_path: lsp::Url,
5786 language_server_id: LanguageServerId,
5787 language_server_name: LanguageServerName,
5788 cx: &mut Context<Self>,
5789 ) -> Task<Result<Entity<Buffer>>> {
5790 cx.spawn(move |lsp_store, mut cx| async move {
5791 // Escape percent-encoded string.
5792 let current_scheme = abs_path.scheme().to_owned();
5793 let _ = abs_path.set_scheme("file");
5794
5795 let abs_path = abs_path
5796 .to_file_path()
5797 .map_err(|_| anyhow!("can't convert URI to path"))?;
5798 let p = abs_path.clone();
5799 let yarn_worktree = lsp_store
5800 .update(&mut cx, move |lsp_store, cx| match lsp_store.as_local() {
5801 Some(local_lsp_store) => local_lsp_store.yarn.update(cx, |_, cx| {
5802 cx.spawn(|this, mut cx| async move {
5803 let t = this
5804 .update(&mut cx, |this, cx| {
5805 this.process_path(&p, ¤t_scheme, cx)
5806 })
5807 .ok()?;
5808 t.await
5809 })
5810 }),
5811 None => Task::ready(None),
5812 })?
5813 .await;
5814 let (worktree_root_target, known_relative_path) =
5815 if let Some((zip_root, relative_path)) = yarn_worktree {
5816 (zip_root, Some(relative_path))
5817 } else {
5818 (Arc::<Path>::from(abs_path.as_path()), None)
5819 };
5820 let (worktree, relative_path) = if let Some(result) =
5821 lsp_store.update(&mut cx, |lsp_store, cx| {
5822 lsp_store.worktree_store.update(cx, |worktree_store, cx| {
5823 worktree_store.find_worktree(&worktree_root_target, cx)
5824 })
5825 })? {
5826 let relative_path =
5827 known_relative_path.unwrap_or_else(|| Arc::<Path>::from(result.1));
5828 (result.0, relative_path)
5829 } else {
5830 let worktree = lsp_store
5831 .update(&mut cx, |lsp_store, cx| {
5832 lsp_store.worktree_store.update(cx, |worktree_store, cx| {
5833 worktree_store.create_worktree(&worktree_root_target, false, cx)
5834 })
5835 })?
5836 .await?;
5837 if worktree.update(&mut cx, |worktree, _| worktree.is_local())? {
5838 lsp_store
5839 .update(&mut cx, |lsp_store, cx| {
5840 lsp_store.register_local_language_server(
5841 worktree.clone(),
5842 language_server_name,
5843 language_server_id,
5844 cx,
5845 )
5846 })
5847 .ok();
5848 }
5849 let worktree_root = worktree.update(&mut cx, |worktree, _| worktree.abs_path())?;
5850 let relative_path = if let Some(known_path) = known_relative_path {
5851 known_path
5852 } else {
5853 abs_path.strip_prefix(worktree_root)?.into()
5854 };
5855 (worktree, relative_path)
5856 };
5857 let project_path = ProjectPath {
5858 worktree_id: worktree.update(&mut cx, |worktree, _| worktree.id())?,
5859 path: relative_path,
5860 };
5861 lsp_store
5862 .update(&mut cx, |lsp_store, cx| {
5863 lsp_store.buffer_store().update(cx, |buffer_store, cx| {
5864 buffer_store.open_buffer(project_path, cx)
5865 })
5866 })?
5867 .await
5868 })
5869 }
5870
5871 fn request_multiple_lsp_locally<P, R>(
5872 &mut self,
5873 buffer: &Entity<Buffer>,
5874 position: Option<P>,
5875 request: R,
5876 cx: &mut Context<'_, Self>,
5877 ) -> Task<Vec<R::Response>>
5878 where
5879 P: ToOffset,
5880 R: LspCommand + Clone,
5881 <R::LspRequest as lsp::request::Request>::Result: Send,
5882 <R::LspRequest as lsp::request::Request>::Params: Send,
5883 {
5884 let Some(local) = self.as_local() else {
5885 return Task::ready(Vec::new());
5886 };
5887
5888 let snapshot = buffer.read(cx).snapshot();
5889 let scope = position.and_then(|position| snapshot.language_scope_at(position));
5890
5891 let server_ids = buffer.update(cx, |buffer, cx| {
5892 local
5893 .language_servers_for_buffer(buffer, cx)
5894 .filter(|(adapter, _)| {
5895 scope
5896 .as_ref()
5897 .map(|scope| scope.language_allowed(&adapter.name))
5898 .unwrap_or(true)
5899 })
5900 .map(|(_, server)| server.server_id())
5901 .collect::<Vec<_>>()
5902 });
5903
5904 let mut response_results = server_ids
5905 .into_iter()
5906 .map(|server_id| {
5907 self.request_lsp(
5908 buffer.clone(),
5909 LanguageServerToQuery::Other(server_id),
5910 request.clone(),
5911 cx,
5912 )
5913 })
5914 .collect::<FuturesUnordered<_>>();
5915
5916 cx.spawn(|_, _| async move {
5917 let mut responses = Vec::with_capacity(response_results.len());
5918 while let Some(response_result) = response_results.next().await {
5919 if let Some(response) = response_result.log_err() {
5920 responses.push(response);
5921 }
5922 }
5923 responses
5924 })
5925 }
5926
5927 async fn handle_lsp_command<T: LspCommand>(
5928 this: Entity<Self>,
5929 envelope: TypedEnvelope<T::ProtoRequest>,
5930 mut cx: AsyncApp,
5931 ) -> Result<<T::ProtoRequest as proto::RequestMessage>::Response>
5932 where
5933 <T::LspRequest as lsp::request::Request>::Params: Send,
5934 <T::LspRequest as lsp::request::Request>::Result: Send,
5935 {
5936 let sender_id = envelope.original_sender_id().unwrap_or_default();
5937 let buffer_id = T::buffer_id_from_proto(&envelope.payload)?;
5938 let buffer_handle = this.update(&mut cx, |this, cx| {
5939 this.buffer_store.read(cx).get_existing(buffer_id)
5940 })??;
5941 let request = T::from_proto(
5942 envelope.payload,
5943 this.clone(),
5944 buffer_handle.clone(),
5945 cx.clone(),
5946 )
5947 .await?;
5948 let response = this
5949 .update(&mut cx, |this, cx| {
5950 this.request_lsp(
5951 buffer_handle.clone(),
5952 LanguageServerToQuery::FirstCapable,
5953 request,
5954 cx,
5955 )
5956 })?
5957 .await?;
5958 this.update(&mut cx, |this, cx| {
5959 Ok(T::response_to_proto(
5960 response,
5961 this,
5962 sender_id,
5963 &buffer_handle.read(cx).version(),
5964 cx,
5965 ))
5966 })?
5967 }
5968
5969 async fn handle_multi_lsp_query(
5970 this: Entity<Self>,
5971 envelope: TypedEnvelope<proto::MultiLspQuery>,
5972 mut cx: AsyncApp,
5973 ) -> Result<proto::MultiLspQueryResponse> {
5974 let response_from_ssh = this.update(&mut cx, |this, _| {
5975 let (upstream_client, project_id) = this.upstream_client()?;
5976 let mut payload = envelope.payload.clone();
5977 payload.project_id = project_id;
5978
5979 Some(upstream_client.request(payload))
5980 })?;
5981 if let Some(response_from_ssh) = response_from_ssh {
5982 return response_from_ssh.await;
5983 }
5984
5985 let sender_id = envelope.original_sender_id().unwrap_or_default();
5986 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
5987 let version = deserialize_version(&envelope.payload.version);
5988 let buffer = this.update(&mut cx, |this, cx| {
5989 this.buffer_store.read(cx).get_existing(buffer_id)
5990 })??;
5991 buffer
5992 .update(&mut cx, |buffer, _| {
5993 buffer.wait_for_version(version.clone())
5994 })?
5995 .await?;
5996 let buffer_version = buffer.update(&mut cx, |buffer, _| buffer.version())?;
5997 match envelope
5998 .payload
5999 .strategy
6000 .context("invalid request without the strategy")?
6001 {
6002 proto::multi_lsp_query::Strategy::All(_) => {
6003 // currently, there's only one multiple language servers query strategy,
6004 // so just ensure it's specified correctly
6005 }
6006 }
6007 match envelope.payload.request {
6008 Some(proto::multi_lsp_query::Request::GetHover(get_hover)) => {
6009 let get_hover =
6010 GetHover::from_proto(get_hover, this.clone(), buffer.clone(), cx.clone())
6011 .await?;
6012 let all_hovers = this
6013 .update(&mut cx, |this, cx| {
6014 this.request_multiple_lsp_locally(
6015 &buffer,
6016 Some(get_hover.position),
6017 get_hover,
6018 cx,
6019 )
6020 })?
6021 .await
6022 .into_iter()
6023 .filter_map(|hover| remove_empty_hover_blocks(hover?));
6024 this.update(&mut cx, |project, cx| proto::MultiLspQueryResponse {
6025 responses: all_hovers
6026 .map(|hover| proto::LspResponse {
6027 response: Some(proto::lsp_response::Response::GetHoverResponse(
6028 GetHover::response_to_proto(
6029 Some(hover),
6030 project,
6031 sender_id,
6032 &buffer_version,
6033 cx,
6034 ),
6035 )),
6036 })
6037 .collect(),
6038 })
6039 }
6040 Some(proto::multi_lsp_query::Request::GetCodeActions(get_code_actions)) => {
6041 let get_code_actions = GetCodeActions::from_proto(
6042 get_code_actions,
6043 this.clone(),
6044 buffer.clone(),
6045 cx.clone(),
6046 )
6047 .await?;
6048
6049 let all_actions = this
6050 .update(&mut cx, |project, cx| {
6051 project.request_multiple_lsp_locally(
6052 &buffer,
6053 Some(get_code_actions.range.start),
6054 get_code_actions,
6055 cx,
6056 )
6057 })?
6058 .await
6059 .into_iter();
6060
6061 this.update(&mut cx, |project, cx| proto::MultiLspQueryResponse {
6062 responses: all_actions
6063 .map(|code_actions| proto::LspResponse {
6064 response: Some(proto::lsp_response::Response::GetCodeActionsResponse(
6065 GetCodeActions::response_to_proto(
6066 code_actions,
6067 project,
6068 sender_id,
6069 &buffer_version,
6070 cx,
6071 ),
6072 )),
6073 })
6074 .collect(),
6075 })
6076 }
6077 Some(proto::multi_lsp_query::Request::GetSignatureHelp(get_signature_help)) => {
6078 let get_signature_help = GetSignatureHelp::from_proto(
6079 get_signature_help,
6080 this.clone(),
6081 buffer.clone(),
6082 cx.clone(),
6083 )
6084 .await?;
6085
6086 let all_signatures = this
6087 .update(&mut cx, |project, cx| {
6088 project.request_multiple_lsp_locally(
6089 &buffer,
6090 Some(get_signature_help.position),
6091 get_signature_help,
6092 cx,
6093 )
6094 })?
6095 .await
6096 .into_iter();
6097
6098 this.update(&mut cx, |project, cx| proto::MultiLspQueryResponse {
6099 responses: all_signatures
6100 .map(|signature_help| proto::LspResponse {
6101 response: Some(
6102 proto::lsp_response::Response::GetSignatureHelpResponse(
6103 GetSignatureHelp::response_to_proto(
6104 signature_help,
6105 project,
6106 sender_id,
6107 &buffer_version,
6108 cx,
6109 ),
6110 ),
6111 ),
6112 })
6113 .collect(),
6114 })
6115 }
6116 None => anyhow::bail!("empty multi lsp query request"),
6117 }
6118 }
6119
6120 async fn handle_apply_code_action(
6121 this: Entity<Self>,
6122 envelope: TypedEnvelope<proto::ApplyCodeAction>,
6123 mut cx: AsyncApp,
6124 ) -> Result<proto::ApplyCodeActionResponse> {
6125 let sender_id = envelope.original_sender_id().unwrap_or_default();
6126 let action = Self::deserialize_code_action(
6127 envelope
6128 .payload
6129 .action
6130 .ok_or_else(|| anyhow!("invalid action"))?,
6131 )?;
6132 let apply_code_action = this.update(&mut cx, |this, cx| {
6133 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
6134 let buffer = this.buffer_store.read(cx).get_existing(buffer_id)?;
6135 anyhow::Ok(this.apply_code_action(buffer, action, false, cx))
6136 })??;
6137
6138 let project_transaction = apply_code_action.await?;
6139 let project_transaction = this.update(&mut cx, |this, cx| {
6140 this.buffer_store.update(cx, |buffer_store, cx| {
6141 buffer_store.serialize_project_transaction_for_peer(
6142 project_transaction,
6143 sender_id,
6144 cx,
6145 )
6146 })
6147 })?;
6148 Ok(proto::ApplyCodeActionResponse {
6149 transaction: Some(project_transaction),
6150 })
6151 }
6152
6153 async fn handle_register_buffer_with_language_servers(
6154 this: Entity<Self>,
6155 envelope: TypedEnvelope<proto::RegisterBufferWithLanguageServers>,
6156 mut cx: AsyncApp,
6157 ) -> Result<proto::Ack> {
6158 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
6159 let peer_id = envelope.original_sender_id.unwrap_or(envelope.sender_id);
6160 this.update(&mut cx, |this, cx| {
6161 if let Some((upstream_client, upstream_project_id)) = this.upstream_client() {
6162 return upstream_client.send(proto::RegisterBufferWithLanguageServers {
6163 project_id: upstream_project_id,
6164 buffer_id: buffer_id.to_proto(),
6165 });
6166 }
6167
6168 let Some(buffer) = this.buffer_store().read(cx).get(buffer_id) else {
6169 anyhow::bail!("buffer is not open");
6170 };
6171
6172 let handle = this.register_buffer_with_language_servers(&buffer, false, cx);
6173 this.buffer_store().update(cx, |buffer_store, _| {
6174 buffer_store.register_shared_lsp_handle(peer_id, buffer_id, handle);
6175 });
6176
6177 Ok(())
6178 })??;
6179 Ok(proto::Ack {})
6180 }
6181
6182 async fn handle_rename_project_entry(
6183 this: Entity<Self>,
6184 envelope: TypedEnvelope<proto::RenameProjectEntry>,
6185 mut cx: AsyncApp,
6186 ) -> Result<proto::ProjectEntryResponse> {
6187 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
6188 let (worktree_id, worktree, old_path, is_dir) = this
6189 .update(&mut cx, |this, cx| {
6190 this.worktree_store
6191 .read(cx)
6192 .worktree_and_entry_for_id(entry_id, cx)
6193 .map(|(worktree, entry)| {
6194 (
6195 worktree.read(cx).id(),
6196 worktree,
6197 entry.path.clone(),
6198 entry.is_dir(),
6199 )
6200 })
6201 })?
6202 .ok_or_else(|| anyhow!("worktree not found"))?;
6203 let (old_abs_path, new_abs_path) = {
6204 let root_path = worktree.update(&mut cx, |this, _| this.abs_path())?;
6205 let new_path = PathBuf::from_proto(envelope.payload.new_path.clone());
6206 (root_path.join(&old_path), root_path.join(&new_path))
6207 };
6208
6209 Self::will_rename_entry(
6210 this.downgrade(),
6211 worktree_id,
6212 &old_abs_path,
6213 &new_abs_path,
6214 is_dir,
6215 cx.clone(),
6216 )
6217 .await;
6218 let response = Worktree::handle_rename_entry(worktree, envelope.payload, cx.clone()).await;
6219 this.update(&mut cx, |this, _| {
6220 this.did_rename_entry(worktree_id, &old_abs_path, &new_abs_path, is_dir);
6221 })
6222 .ok();
6223 response
6224 }
6225
6226 async fn handle_update_diagnostic_summary(
6227 this: Entity<Self>,
6228 envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
6229 mut cx: AsyncApp,
6230 ) -> Result<()> {
6231 this.update(&mut cx, |this, cx| {
6232 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
6233 if let Some(message) = envelope.payload.summary {
6234 let project_path = ProjectPath {
6235 worktree_id,
6236 path: Arc::<Path>::from_proto(message.path),
6237 };
6238 let path = project_path.path.clone();
6239 let server_id = LanguageServerId(message.language_server_id as usize);
6240 let summary = DiagnosticSummary {
6241 error_count: message.error_count as usize,
6242 warning_count: message.warning_count as usize,
6243 };
6244
6245 if summary.is_empty() {
6246 if let Some(worktree_summaries) =
6247 this.diagnostic_summaries.get_mut(&worktree_id)
6248 {
6249 if let Some(summaries) = worktree_summaries.get_mut(&path) {
6250 summaries.remove(&server_id);
6251 if summaries.is_empty() {
6252 worktree_summaries.remove(&path);
6253 }
6254 }
6255 }
6256 } else {
6257 this.diagnostic_summaries
6258 .entry(worktree_id)
6259 .or_default()
6260 .entry(path)
6261 .or_default()
6262 .insert(server_id, summary);
6263 }
6264 if let Some((downstream_client, project_id)) = &this.downstream_client {
6265 downstream_client
6266 .send(proto::UpdateDiagnosticSummary {
6267 project_id: *project_id,
6268 worktree_id: worktree_id.to_proto(),
6269 summary: Some(proto::DiagnosticSummary {
6270 path: project_path.path.as_ref().to_proto(),
6271 language_server_id: server_id.0 as u64,
6272 error_count: summary.error_count as u32,
6273 warning_count: summary.warning_count as u32,
6274 }),
6275 })
6276 .log_err();
6277 }
6278 cx.emit(LspStoreEvent::DiagnosticsUpdated {
6279 language_server_id: LanguageServerId(message.language_server_id as usize),
6280 path: project_path,
6281 });
6282 }
6283 Ok(())
6284 })?
6285 }
6286
6287 async fn handle_start_language_server(
6288 this: Entity<Self>,
6289 envelope: TypedEnvelope<proto::StartLanguageServer>,
6290 mut cx: AsyncApp,
6291 ) -> Result<()> {
6292 let server = envelope
6293 .payload
6294 .server
6295 .ok_or_else(|| anyhow!("invalid server"))?;
6296
6297 this.update(&mut cx, |this, cx| {
6298 let server_id = LanguageServerId(server.id as usize);
6299 this.language_server_statuses.insert(
6300 server_id,
6301 LanguageServerStatus {
6302 name: server.name.clone(),
6303 pending_work: Default::default(),
6304 has_pending_diagnostic_updates: false,
6305 progress_tokens: Default::default(),
6306 },
6307 );
6308 cx.emit(LspStoreEvent::LanguageServerAdded(
6309 server_id,
6310 LanguageServerName(server.name.into()),
6311 server.worktree_id.map(WorktreeId::from_proto),
6312 ));
6313 cx.notify();
6314 })?;
6315 Ok(())
6316 }
6317
6318 async fn handle_update_language_server(
6319 this: Entity<Self>,
6320 envelope: TypedEnvelope<proto::UpdateLanguageServer>,
6321 mut cx: AsyncApp,
6322 ) -> Result<()> {
6323 this.update(&mut cx, |this, cx| {
6324 let language_server_id = LanguageServerId(envelope.payload.language_server_id as usize);
6325
6326 match envelope
6327 .payload
6328 .variant
6329 .ok_or_else(|| anyhow!("invalid variant"))?
6330 {
6331 proto::update_language_server::Variant::WorkStart(payload) => {
6332 this.on_lsp_work_start(
6333 language_server_id,
6334 payload.token,
6335 LanguageServerProgress {
6336 title: payload.title,
6337 is_disk_based_diagnostics_progress: false,
6338 is_cancellable: payload.is_cancellable.unwrap_or(false),
6339 message: payload.message,
6340 percentage: payload.percentage.map(|p| p as usize),
6341 last_update_at: cx.background_executor().now(),
6342 },
6343 cx,
6344 );
6345 }
6346
6347 proto::update_language_server::Variant::WorkProgress(payload) => {
6348 this.on_lsp_work_progress(
6349 language_server_id,
6350 payload.token,
6351 LanguageServerProgress {
6352 title: None,
6353 is_disk_based_diagnostics_progress: false,
6354 is_cancellable: payload.is_cancellable.unwrap_or(false),
6355 message: payload.message,
6356 percentage: payload.percentage.map(|p| p as usize),
6357 last_update_at: cx.background_executor().now(),
6358 },
6359 cx,
6360 );
6361 }
6362
6363 proto::update_language_server::Variant::WorkEnd(payload) => {
6364 this.on_lsp_work_end(language_server_id, payload.token, cx);
6365 }
6366
6367 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(_) => {
6368 this.disk_based_diagnostics_started(language_server_id, cx);
6369 }
6370
6371 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(_) => {
6372 this.disk_based_diagnostics_finished(language_server_id, cx)
6373 }
6374 }
6375
6376 Ok(())
6377 })?
6378 }
6379
6380 async fn handle_language_server_log(
6381 this: Entity<Self>,
6382 envelope: TypedEnvelope<proto::LanguageServerLog>,
6383 mut cx: AsyncApp,
6384 ) -> Result<()> {
6385 let language_server_id = LanguageServerId(envelope.payload.language_server_id as usize);
6386 let log_type = envelope
6387 .payload
6388 .log_type
6389 .map(LanguageServerLogType::from_proto)
6390 .context("invalid language server log type")?;
6391
6392 let message = envelope.payload.message;
6393
6394 this.update(&mut cx, |_, cx| {
6395 cx.emit(LspStoreEvent::LanguageServerLog(
6396 language_server_id,
6397 log_type,
6398 message,
6399 ));
6400 })
6401 }
6402
6403 pub fn disk_based_diagnostics_started(
6404 &mut self,
6405 language_server_id: LanguageServerId,
6406 cx: &mut Context<Self>,
6407 ) {
6408 if let Some(language_server_status) =
6409 self.language_server_statuses.get_mut(&language_server_id)
6410 {
6411 language_server_status.has_pending_diagnostic_updates = true;
6412 }
6413
6414 cx.emit(LspStoreEvent::DiskBasedDiagnosticsStarted { language_server_id });
6415 cx.emit(LspStoreEvent::LanguageServerUpdate {
6416 language_server_id,
6417 message: proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(
6418 Default::default(),
6419 ),
6420 })
6421 }
6422
6423 pub fn disk_based_diagnostics_finished(
6424 &mut self,
6425 language_server_id: LanguageServerId,
6426 cx: &mut Context<Self>,
6427 ) {
6428 if let Some(language_server_status) =
6429 self.language_server_statuses.get_mut(&language_server_id)
6430 {
6431 language_server_status.has_pending_diagnostic_updates = false;
6432 }
6433
6434 cx.emit(LspStoreEvent::DiskBasedDiagnosticsFinished { language_server_id });
6435 cx.emit(LspStoreEvent::LanguageServerUpdate {
6436 language_server_id,
6437 message: proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
6438 Default::default(),
6439 ),
6440 })
6441 }
6442
6443 // After saving a buffer using a language server that doesn't provide a disk-based progress token,
6444 // kick off a timer that will reset every time the buffer is saved. If the timer eventually fires,
6445 // simulate disk-based diagnostics being finished so that other pieces of UI (e.g., project
6446 // diagnostics view, diagnostic status bar) can update. We don't emit an event right away because
6447 // the language server might take some time to publish diagnostics.
6448 fn simulate_disk_based_diagnostics_events_if_needed(
6449 &mut self,
6450 language_server_id: LanguageServerId,
6451 cx: &mut Context<Self>,
6452 ) {
6453 const DISK_BASED_DIAGNOSTICS_DEBOUNCE: Duration = Duration::from_secs(1);
6454
6455 let Some(LanguageServerState::Running {
6456 simulate_disk_based_diagnostics_completion,
6457 adapter,
6458 ..
6459 }) = self
6460 .as_local_mut()
6461 .and_then(|local_store| local_store.language_servers.get_mut(&language_server_id))
6462 else {
6463 return;
6464 };
6465
6466 if adapter.disk_based_diagnostics_progress_token.is_some() {
6467 return;
6468 }
6469
6470 let prev_task = simulate_disk_based_diagnostics_completion.replace(cx.spawn(
6471 move |this, mut cx| async move {
6472 cx.background_executor()
6473 .timer(DISK_BASED_DIAGNOSTICS_DEBOUNCE)
6474 .await;
6475
6476 this.update(&mut cx, |this, cx| {
6477 this.disk_based_diagnostics_finished(language_server_id, cx);
6478
6479 if let Some(LanguageServerState::Running {
6480 simulate_disk_based_diagnostics_completion,
6481 ..
6482 }) = this.as_local_mut().and_then(|local_store| {
6483 local_store.language_servers.get_mut(&language_server_id)
6484 }) {
6485 *simulate_disk_based_diagnostics_completion = None;
6486 }
6487 })
6488 .ok();
6489 },
6490 ));
6491
6492 if prev_task.is_none() {
6493 self.disk_based_diagnostics_started(language_server_id, cx);
6494 }
6495 }
6496
6497 pub fn language_server_statuses(
6498 &self,
6499 ) -> impl DoubleEndedIterator<Item = (LanguageServerId, &LanguageServerStatus)> {
6500 self.language_server_statuses
6501 .iter()
6502 .map(|(key, value)| (*key, value))
6503 }
6504
6505 pub(super) fn did_rename_entry(
6506 &self,
6507 worktree_id: WorktreeId,
6508 old_path: &Path,
6509 new_path: &Path,
6510 is_dir: bool,
6511 ) {
6512 maybe!({
6513 let local_store = self.as_local()?;
6514
6515 let old_uri = lsp::Url::from_file_path(old_path).ok().map(String::from)?;
6516 let new_uri = lsp::Url::from_file_path(new_path).ok().map(String::from)?;
6517
6518 for language_server in local_store.language_servers_for_worktree(worktree_id) {
6519 let Some(filter) = local_store
6520 .language_server_paths_watched_for_rename
6521 .get(&language_server.server_id())
6522 else {
6523 continue;
6524 };
6525
6526 if filter.should_send_did_rename(&old_uri, is_dir) {
6527 language_server
6528 .notify::<DidRenameFiles>(&RenameFilesParams {
6529 files: vec![FileRename {
6530 old_uri: old_uri.clone(),
6531 new_uri: new_uri.clone(),
6532 }],
6533 })
6534 .log_err();
6535 }
6536 }
6537 Some(())
6538 });
6539 }
6540
6541 pub(super) fn will_rename_entry(
6542 this: WeakEntity<Self>,
6543 worktree_id: WorktreeId,
6544 old_path: &Path,
6545 new_path: &Path,
6546 is_dir: bool,
6547 cx: AsyncApp,
6548 ) -> Task<()> {
6549 let old_uri = lsp::Url::from_file_path(old_path).ok().map(String::from);
6550 let new_uri = lsp::Url::from_file_path(new_path).ok().map(String::from);
6551 cx.spawn(move |mut cx| async move {
6552 let mut tasks = vec![];
6553 this.update(&mut cx, |this, cx| {
6554 let local_store = this.as_local()?;
6555 let old_uri = old_uri?;
6556 let new_uri = new_uri?;
6557 for language_server in local_store.language_servers_for_worktree(worktree_id) {
6558 let Some(filter) = local_store
6559 .language_server_paths_watched_for_rename
6560 .get(&language_server.server_id())
6561 else {
6562 continue;
6563 };
6564 let Some(adapter) =
6565 this.language_server_adapter_for_id(language_server.server_id())
6566 else {
6567 continue;
6568 };
6569 if filter.should_send_will_rename(&old_uri, is_dir) {
6570 let apply_edit = cx.spawn({
6571 let old_uri = old_uri.clone();
6572 let new_uri = new_uri.clone();
6573 let language_server = language_server.clone();
6574 |this, mut cx| async move {
6575 let edit = language_server
6576 .request::<WillRenameFiles>(RenameFilesParams {
6577 files: vec![FileRename { old_uri, new_uri }],
6578 })
6579 .log_err()
6580 .await
6581 .flatten()?;
6582
6583 LocalLspStore::deserialize_workspace_edit(
6584 this.upgrade()?,
6585 edit,
6586 false,
6587 adapter.clone(),
6588 language_server.clone(),
6589 &mut cx,
6590 )
6591 .await
6592 .ok();
6593 Some(())
6594 }
6595 });
6596 tasks.push(apply_edit);
6597 }
6598 }
6599 Some(())
6600 })
6601 .ok()
6602 .flatten();
6603 for task in tasks {
6604 // Await on tasks sequentially so that the order of application of edits is deterministic
6605 // (at least with regards to the order of registration of language servers)
6606 task.await;
6607 }
6608 })
6609 }
6610
6611 fn lsp_notify_abs_paths_changed(
6612 &mut self,
6613 server_id: LanguageServerId,
6614 changes: Vec<PathEvent>,
6615 ) {
6616 maybe!({
6617 let server = self.language_server_for_id(server_id)?;
6618 let changes = changes
6619 .into_iter()
6620 .filter_map(|event| {
6621 let typ = match event.kind? {
6622 PathEventKind::Created => lsp::FileChangeType::CREATED,
6623 PathEventKind::Removed => lsp::FileChangeType::DELETED,
6624 PathEventKind::Changed => lsp::FileChangeType::CHANGED,
6625 };
6626 Some(lsp::FileEvent {
6627 uri: lsp::Url::from_file_path(&event.path).ok()?,
6628 typ,
6629 })
6630 })
6631 .collect::<Vec<_>>();
6632 if !changes.is_empty() {
6633 server
6634 .notify::<lsp::notification::DidChangeWatchedFiles>(
6635 &lsp::DidChangeWatchedFilesParams { changes },
6636 )
6637 .log_err();
6638 }
6639 Some(())
6640 });
6641 }
6642
6643 pub fn language_server_for_id(&self, id: LanguageServerId) -> Option<Arc<LanguageServer>> {
6644 let local_lsp_store = self.as_local()?;
6645 if let Some(LanguageServerState::Running { server, .. }) =
6646 local_lsp_store.language_servers.get(&id)
6647 {
6648 Some(server.clone())
6649 } else if let Some((_, server)) = local_lsp_store.supplementary_language_servers.get(&id) {
6650 Some(Arc::clone(server))
6651 } else {
6652 None
6653 }
6654 }
6655
6656 fn on_lsp_progress(
6657 &mut self,
6658 progress: lsp::ProgressParams,
6659 language_server_id: LanguageServerId,
6660 disk_based_diagnostics_progress_token: Option<String>,
6661 cx: &mut Context<Self>,
6662 ) {
6663 let token = match progress.token {
6664 lsp::NumberOrString::String(token) => token,
6665 lsp::NumberOrString::Number(token) => {
6666 log::info!("skipping numeric progress token {}", token);
6667 return;
6668 }
6669 };
6670
6671 let lsp::ProgressParamsValue::WorkDone(progress) = progress.value;
6672 let language_server_status =
6673 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
6674 status
6675 } else {
6676 return;
6677 };
6678
6679 if !language_server_status.progress_tokens.contains(&token) {
6680 return;
6681 }
6682
6683 let is_disk_based_diagnostics_progress = disk_based_diagnostics_progress_token
6684 .as_ref()
6685 .map_or(false, |disk_based_token| {
6686 token.starts_with(disk_based_token)
6687 });
6688
6689 match progress {
6690 lsp::WorkDoneProgress::Begin(report) => {
6691 if is_disk_based_diagnostics_progress {
6692 self.disk_based_diagnostics_started(language_server_id, cx);
6693 }
6694 self.on_lsp_work_start(
6695 language_server_id,
6696 token.clone(),
6697 LanguageServerProgress {
6698 title: Some(report.title),
6699 is_disk_based_diagnostics_progress,
6700 is_cancellable: report.cancellable.unwrap_or(false),
6701 message: report.message.clone(),
6702 percentage: report.percentage.map(|p| p as usize),
6703 last_update_at: cx.background_executor().now(),
6704 },
6705 cx,
6706 );
6707 }
6708 lsp::WorkDoneProgress::Report(report) => {
6709 if self.on_lsp_work_progress(
6710 language_server_id,
6711 token.clone(),
6712 LanguageServerProgress {
6713 title: None,
6714 is_disk_based_diagnostics_progress,
6715 is_cancellable: report.cancellable.unwrap_or(false),
6716 message: report.message.clone(),
6717 percentage: report.percentage.map(|p| p as usize),
6718 last_update_at: cx.background_executor().now(),
6719 },
6720 cx,
6721 ) {
6722 cx.emit(LspStoreEvent::LanguageServerUpdate {
6723 language_server_id,
6724 message: proto::update_language_server::Variant::WorkProgress(
6725 proto::LspWorkProgress {
6726 token,
6727 message: report.message,
6728 percentage: report.percentage,
6729 is_cancellable: report.cancellable,
6730 },
6731 ),
6732 })
6733 }
6734 }
6735 lsp::WorkDoneProgress::End(_) => {
6736 language_server_status.progress_tokens.remove(&token);
6737 self.on_lsp_work_end(language_server_id, token.clone(), cx);
6738 if is_disk_based_diagnostics_progress {
6739 self.disk_based_diagnostics_finished(language_server_id, cx);
6740 }
6741 }
6742 }
6743 }
6744
6745 fn on_lsp_work_start(
6746 &mut self,
6747 language_server_id: LanguageServerId,
6748 token: String,
6749 progress: LanguageServerProgress,
6750 cx: &mut Context<Self>,
6751 ) {
6752 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
6753 status.pending_work.insert(token.clone(), progress.clone());
6754 cx.notify();
6755 }
6756 cx.emit(LspStoreEvent::LanguageServerUpdate {
6757 language_server_id,
6758 message: proto::update_language_server::Variant::WorkStart(proto::LspWorkStart {
6759 token,
6760 title: progress.title,
6761 message: progress.message,
6762 percentage: progress.percentage.map(|p| p as u32),
6763 is_cancellable: Some(progress.is_cancellable),
6764 }),
6765 })
6766 }
6767
6768 fn on_lsp_work_progress(
6769 &mut self,
6770 language_server_id: LanguageServerId,
6771 token: String,
6772 progress: LanguageServerProgress,
6773 cx: &mut Context<Self>,
6774 ) -> bool {
6775 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
6776 match status.pending_work.entry(token) {
6777 btree_map::Entry::Vacant(entry) => {
6778 entry.insert(progress);
6779 cx.notify();
6780 return true;
6781 }
6782 btree_map::Entry::Occupied(mut entry) => {
6783 let entry = entry.get_mut();
6784 if (progress.last_update_at - entry.last_update_at)
6785 >= SERVER_PROGRESS_THROTTLE_TIMEOUT
6786 {
6787 entry.last_update_at = progress.last_update_at;
6788 if progress.message.is_some() {
6789 entry.message = progress.message;
6790 }
6791 if progress.percentage.is_some() {
6792 entry.percentage = progress.percentage;
6793 }
6794 if progress.is_cancellable != entry.is_cancellable {
6795 entry.is_cancellable = progress.is_cancellable;
6796 }
6797 cx.notify();
6798 return true;
6799 }
6800 }
6801 }
6802 }
6803
6804 false
6805 }
6806
6807 fn on_lsp_work_end(
6808 &mut self,
6809 language_server_id: LanguageServerId,
6810 token: String,
6811 cx: &mut Context<Self>,
6812 ) {
6813 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
6814 if let Some(work) = status.pending_work.remove(&token) {
6815 if !work.is_disk_based_diagnostics_progress {
6816 cx.emit(LspStoreEvent::RefreshInlayHints);
6817 }
6818 }
6819 cx.notify();
6820 }
6821
6822 cx.emit(LspStoreEvent::LanguageServerUpdate {
6823 language_server_id,
6824 message: proto::update_language_server::Variant::WorkEnd(proto::LspWorkEnd { token }),
6825 })
6826 }
6827
6828 pub async fn handle_resolve_completion_documentation(
6829 this: Entity<Self>,
6830 envelope: TypedEnvelope<proto::ResolveCompletionDocumentation>,
6831 mut cx: AsyncApp,
6832 ) -> Result<proto::ResolveCompletionDocumentationResponse> {
6833 let lsp_completion = serde_json::from_slice(&envelope.payload.lsp_completion)?;
6834
6835 let completion = this
6836 .read_with(&cx, |this, cx| {
6837 let id = LanguageServerId(envelope.payload.language_server_id as usize);
6838 let Some(server) = this.language_server_for_id(id) else {
6839 return Err(anyhow!("No language server {id}"));
6840 };
6841
6842 Ok(cx.background_spawn(async move {
6843 let can_resolve = server
6844 .capabilities()
6845 .completion_provider
6846 .as_ref()
6847 .and_then(|options| options.resolve_provider)
6848 .unwrap_or(false);
6849 if can_resolve {
6850 server
6851 .request::<lsp::request::ResolveCompletionItem>(lsp_completion)
6852 .await
6853 } else {
6854 anyhow::Ok(lsp_completion)
6855 }
6856 }))
6857 })??
6858 .await?;
6859
6860 let mut documentation_is_markdown = false;
6861 let lsp_completion = serde_json::to_string(&completion)?.into_bytes();
6862 let documentation = match completion.documentation {
6863 Some(lsp::Documentation::String(text)) => text,
6864
6865 Some(lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value })) => {
6866 documentation_is_markdown = kind == lsp::MarkupKind::Markdown;
6867 value
6868 }
6869
6870 _ => String::new(),
6871 };
6872
6873 // If we have a new buffer_id, that means we're talking to a new client
6874 // and want to check for new text_edits in the completion too.
6875 let mut old_start = None;
6876 let mut old_end = None;
6877 let mut new_text = String::default();
6878 if let Ok(buffer_id) = BufferId::new(envelope.payload.buffer_id) {
6879 let buffer_snapshot = this.update(&mut cx, |this, cx| {
6880 let buffer = this.buffer_store.read(cx).get_existing(buffer_id)?;
6881 anyhow::Ok(buffer.read(cx).snapshot())
6882 })??;
6883
6884 if let Some(text_edit) = completion.text_edit.as_ref() {
6885 let edit = parse_completion_text_edit(text_edit, &buffer_snapshot);
6886
6887 if let Some((old_range, mut text_edit_new_text)) = edit {
6888 LineEnding::normalize(&mut text_edit_new_text);
6889
6890 new_text = text_edit_new_text;
6891 old_start = Some(serialize_anchor(&old_range.start));
6892 old_end = Some(serialize_anchor(&old_range.end));
6893 }
6894 }
6895 }
6896
6897 Ok(proto::ResolveCompletionDocumentationResponse {
6898 documentation,
6899 documentation_is_markdown,
6900 old_start,
6901 old_end,
6902 new_text,
6903 lsp_completion,
6904 })
6905 }
6906
6907 async fn handle_on_type_formatting(
6908 this: Entity<Self>,
6909 envelope: TypedEnvelope<proto::OnTypeFormatting>,
6910 mut cx: AsyncApp,
6911 ) -> Result<proto::OnTypeFormattingResponse> {
6912 let on_type_formatting = this.update(&mut cx, |this, cx| {
6913 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
6914 let buffer = this.buffer_store.read(cx).get_existing(buffer_id)?;
6915 let position = envelope
6916 .payload
6917 .position
6918 .and_then(deserialize_anchor)
6919 .ok_or_else(|| anyhow!("invalid position"))?;
6920 Ok::<_, anyhow::Error>(this.apply_on_type_formatting(
6921 buffer,
6922 position,
6923 envelope.payload.trigger.clone(),
6924 cx,
6925 ))
6926 })??;
6927
6928 let transaction = on_type_formatting
6929 .await?
6930 .as_ref()
6931 .map(language::proto::serialize_transaction);
6932 Ok(proto::OnTypeFormattingResponse { transaction })
6933 }
6934
6935 async fn handle_refresh_inlay_hints(
6936 this: Entity<Self>,
6937 _: TypedEnvelope<proto::RefreshInlayHints>,
6938 mut cx: AsyncApp,
6939 ) -> Result<proto::Ack> {
6940 this.update(&mut cx, |_, cx| {
6941 cx.emit(LspStoreEvent::RefreshInlayHints);
6942 })?;
6943 Ok(proto::Ack {})
6944 }
6945
6946 async fn handle_inlay_hints(
6947 this: Entity<Self>,
6948 envelope: TypedEnvelope<proto::InlayHints>,
6949 mut cx: AsyncApp,
6950 ) -> Result<proto::InlayHintsResponse> {
6951 let sender_id = envelope.original_sender_id().unwrap_or_default();
6952 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
6953 let buffer = this.update(&mut cx, |this, cx| {
6954 this.buffer_store.read(cx).get_existing(buffer_id)
6955 })??;
6956 buffer
6957 .update(&mut cx, |buffer, _| {
6958 buffer.wait_for_version(deserialize_version(&envelope.payload.version))
6959 })?
6960 .await
6961 .with_context(|| format!("waiting for version for buffer {}", buffer.entity_id()))?;
6962
6963 let start = envelope
6964 .payload
6965 .start
6966 .and_then(deserialize_anchor)
6967 .context("missing range start")?;
6968 let end = envelope
6969 .payload
6970 .end
6971 .and_then(deserialize_anchor)
6972 .context("missing range end")?;
6973 let buffer_hints = this
6974 .update(&mut cx, |lsp_store, cx| {
6975 lsp_store.inlay_hints(buffer.clone(), start..end, cx)
6976 })?
6977 .await
6978 .context("inlay hints fetch")?;
6979
6980 this.update(&mut cx, |project, cx| {
6981 InlayHints::response_to_proto(
6982 buffer_hints,
6983 project,
6984 sender_id,
6985 &buffer.read(cx).version(),
6986 cx,
6987 )
6988 })
6989 }
6990
6991 async fn handle_resolve_inlay_hint(
6992 this: Entity<Self>,
6993 envelope: TypedEnvelope<proto::ResolveInlayHint>,
6994 mut cx: AsyncApp,
6995 ) -> Result<proto::ResolveInlayHintResponse> {
6996 let proto_hint = envelope
6997 .payload
6998 .hint
6999 .expect("incorrect protobuf resolve inlay hint message: missing the inlay hint");
7000 let hint = InlayHints::proto_to_project_hint(proto_hint)
7001 .context("resolved proto inlay hint conversion")?;
7002 let buffer = this.update(&mut cx, |this, cx| {
7003 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
7004 this.buffer_store.read(cx).get_existing(buffer_id)
7005 })??;
7006 let response_hint = this
7007 .update(&mut cx, |this, cx| {
7008 this.resolve_inlay_hint(
7009 hint,
7010 buffer,
7011 LanguageServerId(envelope.payload.language_server_id as usize),
7012 cx,
7013 )
7014 })?
7015 .await
7016 .context("inlay hints fetch")?;
7017 Ok(proto::ResolveInlayHintResponse {
7018 hint: Some(InlayHints::project_to_proto_hint(response_hint)),
7019 })
7020 }
7021
7022 async fn handle_open_buffer_for_symbol(
7023 this: Entity<Self>,
7024 envelope: TypedEnvelope<proto::OpenBufferForSymbol>,
7025 mut cx: AsyncApp,
7026 ) -> Result<proto::OpenBufferForSymbolResponse> {
7027 let peer_id = envelope.original_sender_id().unwrap_or_default();
7028 let symbol = envelope
7029 .payload
7030 .symbol
7031 .ok_or_else(|| anyhow!("invalid symbol"))?;
7032 let symbol = Self::deserialize_symbol(symbol)?;
7033 let symbol = this.update(&mut cx, |this, _| {
7034 let signature = this.symbol_signature(&symbol.path);
7035 if signature == symbol.signature {
7036 Ok(symbol)
7037 } else {
7038 Err(anyhow!("invalid symbol signature"))
7039 }
7040 })??;
7041 let buffer = this
7042 .update(&mut cx, |this, cx| {
7043 this.open_buffer_for_symbol(
7044 &Symbol {
7045 language_server_name: symbol.language_server_name,
7046 source_worktree_id: symbol.source_worktree_id,
7047 source_language_server_id: symbol.source_language_server_id,
7048 path: symbol.path,
7049 name: symbol.name,
7050 kind: symbol.kind,
7051 range: symbol.range,
7052 signature: symbol.signature,
7053 label: CodeLabel {
7054 text: Default::default(),
7055 runs: Default::default(),
7056 filter_range: Default::default(),
7057 },
7058 },
7059 cx,
7060 )
7061 })?
7062 .await?;
7063
7064 this.update(&mut cx, |this, cx| {
7065 let is_private = buffer
7066 .read(cx)
7067 .file()
7068 .map(|f| f.is_private())
7069 .unwrap_or_default();
7070 if is_private {
7071 Err(anyhow!(rpc::ErrorCode::UnsharedItem))
7072 } else {
7073 this.buffer_store
7074 .update(cx, |buffer_store, cx| {
7075 buffer_store.create_buffer_for_peer(&buffer, peer_id, cx)
7076 })
7077 .detach_and_log_err(cx);
7078 let buffer_id = buffer.read(cx).remote_id().to_proto();
7079 Ok(proto::OpenBufferForSymbolResponse { buffer_id })
7080 }
7081 })?
7082 }
7083
7084 fn symbol_signature(&self, project_path: &ProjectPath) -> [u8; 32] {
7085 let mut hasher = Sha256::new();
7086 hasher.update(project_path.worktree_id.to_proto().to_be_bytes());
7087 hasher.update(project_path.path.to_string_lossy().as_bytes());
7088 hasher.update(self.nonce.to_be_bytes());
7089 hasher.finalize().as_slice().try_into().unwrap()
7090 }
7091
7092 pub async fn handle_get_project_symbols(
7093 this: Entity<Self>,
7094 envelope: TypedEnvelope<proto::GetProjectSymbols>,
7095 mut cx: AsyncApp,
7096 ) -> Result<proto::GetProjectSymbolsResponse> {
7097 let symbols = this
7098 .update(&mut cx, |this, cx| {
7099 this.symbols(&envelope.payload.query, cx)
7100 })?
7101 .await?;
7102
7103 Ok(proto::GetProjectSymbolsResponse {
7104 symbols: symbols.iter().map(Self::serialize_symbol).collect(),
7105 })
7106 }
7107
7108 pub async fn handle_restart_language_servers(
7109 this: Entity<Self>,
7110 envelope: TypedEnvelope<proto::RestartLanguageServers>,
7111 mut cx: AsyncApp,
7112 ) -> Result<proto::Ack> {
7113 this.update(&mut cx, |this, cx| {
7114 let buffers = this.buffer_ids_to_buffers(envelope.payload.buffer_ids.into_iter(), cx);
7115 this.restart_language_servers_for_buffers(buffers, cx);
7116 })?;
7117
7118 Ok(proto::Ack {})
7119 }
7120
7121 pub async fn handle_cancel_language_server_work(
7122 this: Entity<Self>,
7123 envelope: TypedEnvelope<proto::CancelLanguageServerWork>,
7124 mut cx: AsyncApp,
7125 ) -> Result<proto::Ack> {
7126 this.update(&mut cx, |this, cx| {
7127 if let Some(work) = envelope.payload.work {
7128 match work {
7129 proto::cancel_language_server_work::Work::Buffers(buffers) => {
7130 let buffers =
7131 this.buffer_ids_to_buffers(buffers.buffer_ids.into_iter(), cx);
7132 this.cancel_language_server_work_for_buffers(buffers, cx);
7133 }
7134 proto::cancel_language_server_work::Work::LanguageServerWork(work) => {
7135 let server_id = LanguageServerId::from_proto(work.language_server_id);
7136 this.cancel_language_server_work(server_id, work.token, cx);
7137 }
7138 }
7139 }
7140 })?;
7141
7142 Ok(proto::Ack {})
7143 }
7144
7145 fn buffer_ids_to_buffers(
7146 &mut self,
7147 buffer_ids: impl Iterator<Item = u64>,
7148 cx: &mut Context<Self>,
7149 ) -> Vec<Entity<Buffer>> {
7150 buffer_ids
7151 .into_iter()
7152 .flat_map(|buffer_id| {
7153 self.buffer_store
7154 .read(cx)
7155 .get(BufferId::new(buffer_id).log_err()?)
7156 })
7157 .collect::<Vec<_>>()
7158 }
7159
7160 async fn handle_apply_additional_edits_for_completion(
7161 this: Entity<Self>,
7162 envelope: TypedEnvelope<proto::ApplyCompletionAdditionalEdits>,
7163 mut cx: AsyncApp,
7164 ) -> Result<proto::ApplyCompletionAdditionalEditsResponse> {
7165 let (buffer, completion) = this.update(&mut cx, |this, cx| {
7166 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
7167 let buffer = this.buffer_store.read(cx).get_existing(buffer_id)?;
7168 let completion = Self::deserialize_completion(
7169 envelope
7170 .payload
7171 .completion
7172 .ok_or_else(|| anyhow!("invalid completion"))?,
7173 )?;
7174 anyhow::Ok((buffer, completion))
7175 })??;
7176
7177 let apply_additional_edits = this.update(&mut cx, |this, cx| {
7178 this.apply_additional_edits_for_completion(
7179 buffer,
7180 Rc::new(RefCell::new(Box::new([Completion {
7181 old_range: completion.old_range,
7182 new_text: completion.new_text,
7183 lsp_completion: completion.lsp_completion,
7184 server_id: completion.server_id,
7185 documentation: None,
7186 label: CodeLabel {
7187 text: Default::default(),
7188 runs: Default::default(),
7189 filter_range: Default::default(),
7190 },
7191 confirm: None,
7192 resolved: completion.resolved,
7193 }]))),
7194 0,
7195 false,
7196 cx,
7197 )
7198 })?;
7199
7200 Ok(proto::ApplyCompletionAdditionalEditsResponse {
7201 transaction: apply_additional_edits
7202 .await?
7203 .as_ref()
7204 .map(language::proto::serialize_transaction),
7205 })
7206 }
7207
7208 pub fn last_formatting_failure(&self) -> Option<&str> {
7209 self.last_formatting_failure.as_deref()
7210 }
7211
7212 pub fn reset_last_formatting_failure(&mut self) {
7213 self.last_formatting_failure = None;
7214 }
7215
7216 pub fn environment_for_buffer(
7217 &self,
7218 buffer: &Entity<Buffer>,
7219 cx: &mut Context<Self>,
7220 ) -> Shared<Task<Option<HashMap<String, String>>>> {
7221 let worktree_id = buffer.read(cx).file().map(|file| file.worktree_id(cx));
7222 let worktree_abs_path = worktree_id.and_then(|worktree_id| {
7223 self.worktree_store
7224 .read(cx)
7225 .worktree_for_id(worktree_id, cx)
7226 .map(|entry| entry.read(cx).abs_path().clone())
7227 });
7228
7229 if let Some(environment) = &self.as_local().map(|local| local.environment.clone()) {
7230 environment.update(cx, |env, cx| {
7231 env.get_environment(worktree_id, worktree_abs_path, cx)
7232 })
7233 } else {
7234 Task::ready(None).shared()
7235 }
7236 }
7237
7238 pub fn format(
7239 &mut self,
7240 buffers: HashSet<Entity<Buffer>>,
7241 target: LspFormatTarget,
7242 push_to_history: bool,
7243 trigger: FormatTrigger,
7244 cx: &mut Context<Self>,
7245 ) -> Task<anyhow::Result<ProjectTransaction>> {
7246 if let Some(_) = self.as_local() {
7247 let buffers = buffers
7248 .into_iter()
7249 .map(|buffer_handle| {
7250 let buffer = buffer_handle.read(cx);
7251 let buffer_abs_path = File::from_dyn(buffer.file())
7252 .and_then(|file| file.as_local().map(|f| f.abs_path(cx)));
7253
7254 (buffer_handle, buffer_abs_path, buffer.remote_id())
7255 })
7256 .collect::<Vec<_>>();
7257
7258 cx.spawn(move |lsp_store, mut cx| async move {
7259 let mut formattable_buffers = Vec::with_capacity(buffers.len());
7260
7261 for (handle, abs_path, id) in buffers {
7262 let env = lsp_store
7263 .update(&mut cx, |lsp_store, cx| {
7264 lsp_store.environment_for_buffer(&handle, cx)
7265 })?
7266 .await;
7267
7268 let ranges = match &target {
7269 LspFormatTarget::Buffers => None,
7270 LspFormatTarget::Ranges(ranges) => {
7271 let Some(ranges) = ranges.get(&id) else {
7272 return Err(anyhow!("No format ranges provided for buffer"));
7273 };
7274 Some(ranges.clone())
7275 }
7276 };
7277
7278 formattable_buffers.push(FormattableBuffer {
7279 handle,
7280 abs_path,
7281 env,
7282 ranges,
7283 });
7284 }
7285
7286 let result = LocalLspStore::format_locally(
7287 lsp_store.clone(),
7288 formattable_buffers,
7289 push_to_history,
7290 trigger,
7291 cx.clone(),
7292 )
7293 .await;
7294 lsp_store.update(&mut cx, |lsp_store, _| {
7295 lsp_store.update_last_formatting_failure(&result);
7296 })?;
7297
7298 result
7299 })
7300 } else if let Some((client, project_id)) = self.upstream_client() {
7301 // Don't support formatting ranges via remote
7302 match target {
7303 LspFormatTarget::Buffers => {}
7304 LspFormatTarget::Ranges(_) => {
7305 return Task::ready(Ok(ProjectTransaction::default()));
7306 }
7307 }
7308
7309 let buffer_store = self.buffer_store();
7310 cx.spawn(move |lsp_store, mut cx| async move {
7311 let result = client
7312 .request(proto::FormatBuffers {
7313 project_id,
7314 trigger: trigger as i32,
7315 buffer_ids: buffers
7316 .iter()
7317 .map(|buffer| {
7318 buffer.update(&mut cx, |buffer, _| buffer.remote_id().into())
7319 })
7320 .collect::<Result<_>>()?,
7321 })
7322 .await
7323 .and_then(|result| result.transaction.context("missing transaction"));
7324
7325 lsp_store.update(&mut cx, |lsp_store, _| {
7326 lsp_store.update_last_formatting_failure(&result);
7327 })?;
7328
7329 let transaction_response = result?;
7330 buffer_store
7331 .update(&mut cx, |buffer_store, cx| {
7332 buffer_store.deserialize_project_transaction(
7333 transaction_response,
7334 push_to_history,
7335 cx,
7336 )
7337 })?
7338 .await
7339 })
7340 } else {
7341 Task::ready(Ok(ProjectTransaction::default()))
7342 }
7343 }
7344
7345 async fn handle_format_buffers(
7346 this: Entity<Self>,
7347 envelope: TypedEnvelope<proto::FormatBuffers>,
7348 mut cx: AsyncApp,
7349 ) -> Result<proto::FormatBuffersResponse> {
7350 let sender_id = envelope.original_sender_id().unwrap_or_default();
7351 let format = this.update(&mut cx, |this, cx| {
7352 let mut buffers = HashSet::default();
7353 for buffer_id in &envelope.payload.buffer_ids {
7354 let buffer_id = BufferId::new(*buffer_id)?;
7355 buffers.insert(this.buffer_store.read(cx).get_existing(buffer_id)?);
7356 }
7357 let trigger = FormatTrigger::from_proto(envelope.payload.trigger);
7358 anyhow::Ok(this.format(buffers, LspFormatTarget::Buffers, false, trigger, cx))
7359 })??;
7360
7361 let project_transaction = format.await?;
7362 let project_transaction = this.update(&mut cx, |this, cx| {
7363 this.buffer_store.update(cx, |buffer_store, cx| {
7364 buffer_store.serialize_project_transaction_for_peer(
7365 project_transaction,
7366 sender_id,
7367 cx,
7368 )
7369 })
7370 })?;
7371 Ok(proto::FormatBuffersResponse {
7372 transaction: Some(project_transaction),
7373 })
7374 }
7375
7376 async fn handle_apply_code_action_kind(
7377 this: Entity<Self>,
7378 envelope: TypedEnvelope<proto::ApplyCodeActionKind>,
7379 mut cx: AsyncApp,
7380 ) -> Result<proto::ApplyCodeActionKindResponse> {
7381 let sender_id = envelope.original_sender_id().unwrap_or_default();
7382 let format = this.update(&mut cx, |this, cx| {
7383 let mut buffers = HashSet::default();
7384 for buffer_id in &envelope.payload.buffer_ids {
7385 let buffer_id = BufferId::new(*buffer_id)?;
7386 buffers.insert(this.buffer_store.read(cx).get_existing(buffer_id)?);
7387 }
7388 let kind = match envelope.payload.kind.as_str() {
7389 "" => Ok(CodeActionKind::EMPTY),
7390 "quickfix" => Ok(CodeActionKind::QUICKFIX),
7391 "refactor" => Ok(CodeActionKind::REFACTOR),
7392 "refactor.extract" => Ok(CodeActionKind::REFACTOR_EXTRACT),
7393 "refactor.inline" => Ok(CodeActionKind::REFACTOR_INLINE),
7394 "refactor.rewrite" => Ok(CodeActionKind::REFACTOR_REWRITE),
7395 "source" => Ok(CodeActionKind::SOURCE),
7396 "source.organizeImports" => Ok(CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
7397 "source.fixAll" => Ok(CodeActionKind::SOURCE_FIX_ALL),
7398 _ => Err(anyhow!("Invalid code action kind")),
7399 }?;
7400 anyhow::Ok(this.apply_code_action_kind(buffers, kind, false, cx))
7401 })??;
7402
7403 let project_transaction = format.await?;
7404 let project_transaction = this.update(&mut cx, |this, cx| {
7405 this.buffer_store.update(cx, |buffer_store, cx| {
7406 buffer_store.serialize_project_transaction_for_peer(
7407 project_transaction,
7408 sender_id,
7409 cx,
7410 )
7411 })
7412 })?;
7413 Ok(proto::ApplyCodeActionKindResponse {
7414 transaction: Some(project_transaction),
7415 })
7416 }
7417
7418 async fn shutdown_language_server(
7419 server_state: Option<LanguageServerState>,
7420 name: LanguageServerName,
7421 cx: AsyncApp,
7422 ) {
7423 let server = match server_state {
7424 Some(LanguageServerState::Starting { startup, .. }) => {
7425 let mut timer = cx
7426 .background_executor()
7427 .timer(SERVER_LAUNCHING_BEFORE_SHUTDOWN_TIMEOUT)
7428 .fuse();
7429
7430 select! {
7431 server = startup.fuse() => server,
7432 _ = timer => {
7433 log::info!(
7434 "timeout waiting for language server {} to finish launching before stopping",
7435 name
7436 );
7437 None
7438 },
7439 }
7440 }
7441
7442 Some(LanguageServerState::Running { server, .. }) => Some(server),
7443
7444 None => None,
7445 };
7446
7447 if let Some(server) = server {
7448 if let Some(shutdown) = server.shutdown() {
7449 shutdown.await;
7450 }
7451 }
7452 }
7453
7454 // Returns a list of all of the worktrees which no longer have a language server and the root path
7455 // for the stopped server
7456 fn stop_local_language_server(
7457 &mut self,
7458 server_id: LanguageServerId,
7459 name: LanguageServerName,
7460 cx: &mut Context<Self>,
7461 ) -> Task<Vec<WorktreeId>> {
7462 let local = match &mut self.mode {
7463 LspStoreMode::Local(local) => local,
7464 _ => {
7465 return Task::ready(Vec::new());
7466 }
7467 };
7468
7469 let mut orphaned_worktrees = vec![];
7470 // Remove this server ID from all entries in the given worktree.
7471 local.language_server_ids.retain(|(worktree, _), ids| {
7472 if !ids.remove(&server_id) {
7473 return true;
7474 }
7475
7476 if ids.is_empty() {
7477 orphaned_worktrees.push(*worktree);
7478 false
7479 } else {
7480 true
7481 }
7482 });
7483 let _ = self.language_server_statuses.remove(&server_id);
7484 log::info!("stopping language server {name}");
7485 self.buffer_store.update(cx, |buffer_store, cx| {
7486 for buffer in buffer_store.buffers() {
7487 buffer.update(cx, |buffer, cx| {
7488 buffer.update_diagnostics(server_id, DiagnosticSet::new([], buffer), cx);
7489 buffer.set_completion_triggers(server_id, Default::default(), cx);
7490 });
7491 }
7492 });
7493
7494 for (worktree_id, summaries) in self.diagnostic_summaries.iter_mut() {
7495 summaries.retain(|path, summaries_by_server_id| {
7496 if summaries_by_server_id.remove(&server_id).is_some() {
7497 if let Some((client, project_id)) = self.downstream_client.clone() {
7498 client
7499 .send(proto::UpdateDiagnosticSummary {
7500 project_id,
7501 worktree_id: worktree_id.to_proto(),
7502 summary: Some(proto::DiagnosticSummary {
7503 path: path.as_ref().to_proto(),
7504 language_server_id: server_id.0 as u64,
7505 error_count: 0,
7506 warning_count: 0,
7507 }),
7508 })
7509 .log_err();
7510 }
7511 !summaries_by_server_id.is_empty()
7512 } else {
7513 true
7514 }
7515 });
7516 }
7517
7518 let local = self.as_local_mut().unwrap();
7519 for diagnostics in local.diagnostics.values_mut() {
7520 diagnostics.retain(|_, diagnostics_by_server_id| {
7521 if let Ok(ix) = diagnostics_by_server_id.binary_search_by_key(&server_id, |e| e.0) {
7522 diagnostics_by_server_id.remove(ix);
7523 !diagnostics_by_server_id.is_empty()
7524 } else {
7525 true
7526 }
7527 });
7528 }
7529 local.language_server_watched_paths.remove(&server_id);
7530 let server_state = local.language_servers.remove(&server_id);
7531 cx.notify();
7532 cx.emit(LspStoreEvent::LanguageServerRemoved(server_id));
7533 cx.spawn(move |_, cx| async move {
7534 Self::shutdown_language_server(server_state, name, cx).await;
7535 orphaned_worktrees
7536 })
7537 }
7538
7539 pub fn restart_language_servers_for_buffers(
7540 &mut self,
7541 buffers: Vec<Entity<Buffer>>,
7542 cx: &mut Context<Self>,
7543 ) {
7544 if let Some((client, project_id)) = self.upstream_client() {
7545 let request = client.request(proto::RestartLanguageServers {
7546 project_id,
7547 buffer_ids: buffers
7548 .into_iter()
7549 .map(|b| b.read(cx).remote_id().to_proto())
7550 .collect(),
7551 });
7552 cx.background_spawn(request).detach_and_log_err(cx);
7553 } else {
7554 let Some(local) = self.as_local_mut() else {
7555 return;
7556 };
7557 let language_servers_to_stop = buffers
7558 .iter()
7559 .flat_map(|buffer| {
7560 buffer.update(cx, |buffer, cx| {
7561 local.language_server_ids_for_buffer(buffer, cx)
7562 })
7563 })
7564 .collect::<BTreeSet<_>>();
7565 local.lsp_tree.update(cx, |this, _| {
7566 this.remove_nodes(&language_servers_to_stop);
7567 });
7568 let tasks = language_servers_to_stop
7569 .into_iter()
7570 .map(|server| {
7571 let name = self
7572 .language_server_statuses
7573 .get(&server)
7574 .map(|state| state.name.as_str().into())
7575 .unwrap_or_else(|| LanguageServerName::from("Unknown"));
7576 self.stop_local_language_server(server, name, cx)
7577 })
7578 .collect::<Vec<_>>();
7579
7580 cx.spawn(|this, mut cx| async move {
7581 cx.background_spawn(futures::future::join_all(tasks)).await;
7582 this.update(&mut cx, |this, cx| {
7583 for buffer in buffers {
7584 this.register_buffer_with_language_servers(&buffer, true, cx);
7585 }
7586 })
7587 .ok()
7588 })
7589 .detach();
7590 }
7591 }
7592
7593 pub fn update_diagnostics(
7594 &mut self,
7595 language_server_id: LanguageServerId,
7596 mut params: lsp::PublishDiagnosticsParams,
7597 disk_based_sources: &[String],
7598 cx: &mut Context<Self>,
7599 ) -> Result<()> {
7600 if !self.mode.is_local() {
7601 anyhow::bail!("called update_diagnostics on remote");
7602 }
7603 let abs_path = params
7604 .uri
7605 .to_file_path()
7606 .map_err(|_| anyhow!("URI is not a file"))?;
7607 let mut diagnostics = Vec::default();
7608 let mut primary_diagnostic_group_ids = HashMap::default();
7609 let mut sources_by_group_id = HashMap::default();
7610 let mut supporting_diagnostics = HashMap::default();
7611
7612 // Ensure that primary diagnostics are always the most severe
7613 params.diagnostics.sort_by_key(|item| item.severity);
7614
7615 for diagnostic in ¶ms.diagnostics {
7616 let source = diagnostic.source.as_ref();
7617 let range = range_from_lsp(diagnostic.range);
7618 let is_supporting = diagnostic
7619 .related_information
7620 .as_ref()
7621 .map_or(false, |infos| {
7622 infos.iter().any(|info| {
7623 primary_diagnostic_group_ids.contains_key(&(
7624 source,
7625 diagnostic.code.clone(),
7626 range_from_lsp(info.location.range),
7627 ))
7628 })
7629 });
7630
7631 let is_unnecessary = diagnostic.tags.as_ref().map_or(false, |tags| {
7632 tags.iter().any(|tag| *tag == DiagnosticTag::UNNECESSARY)
7633 });
7634
7635 if is_supporting {
7636 supporting_diagnostics.insert(
7637 (source, diagnostic.code.clone(), range),
7638 (diagnostic.severity, is_unnecessary),
7639 );
7640 } else {
7641 let group_id = post_inc(&mut self.as_local_mut().unwrap().next_diagnostic_group_id);
7642 let is_disk_based =
7643 source.map_or(false, |source| disk_based_sources.contains(source));
7644
7645 sources_by_group_id.insert(group_id, source);
7646 primary_diagnostic_group_ids
7647 .insert((source, diagnostic.code.clone(), range.clone()), group_id);
7648
7649 diagnostics.push(DiagnosticEntry {
7650 range,
7651 diagnostic: Diagnostic {
7652 source: diagnostic.source.clone(),
7653 code: diagnostic.code.clone(),
7654 severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
7655 message: diagnostic.message.trim().to_string(),
7656 group_id,
7657 is_primary: true,
7658 is_disk_based,
7659 is_unnecessary,
7660 data: diagnostic.data.clone(),
7661 },
7662 });
7663 if let Some(infos) = &diagnostic.related_information {
7664 for info in infos {
7665 if info.location.uri == params.uri && !info.message.is_empty() {
7666 let range = range_from_lsp(info.location.range);
7667 diagnostics.push(DiagnosticEntry {
7668 range,
7669 diagnostic: Diagnostic {
7670 source: diagnostic.source.clone(),
7671 code: diagnostic.code.clone(),
7672 severity: DiagnosticSeverity::INFORMATION,
7673 message: info.message.trim().to_string(),
7674 group_id,
7675 is_primary: false,
7676 is_disk_based,
7677 is_unnecessary: false,
7678 data: diagnostic.data.clone(),
7679 },
7680 });
7681 }
7682 }
7683 }
7684 }
7685 }
7686
7687 for entry in &mut diagnostics {
7688 let diagnostic = &mut entry.diagnostic;
7689 if !diagnostic.is_primary {
7690 let source = *sources_by_group_id.get(&diagnostic.group_id).unwrap();
7691 if let Some(&(severity, is_unnecessary)) = supporting_diagnostics.get(&(
7692 source,
7693 diagnostic.code.clone(),
7694 entry.range.clone(),
7695 )) {
7696 if let Some(severity) = severity {
7697 diagnostic.severity = severity;
7698 }
7699 diagnostic.is_unnecessary = is_unnecessary;
7700 }
7701 }
7702 }
7703
7704 self.update_diagnostic_entries(
7705 language_server_id,
7706 abs_path,
7707 params.version,
7708 diagnostics,
7709 cx,
7710 )?;
7711 Ok(())
7712 }
7713
7714 #[allow(clippy::too_many_arguments)]
7715 fn insert_newly_running_language_server(
7716 &mut self,
7717 adapter: Arc<CachedLspAdapter>,
7718 language_server: Arc<LanguageServer>,
7719 server_id: LanguageServerId,
7720 key: (WorktreeId, LanguageServerName),
7721 workspace_folders: Arc<Mutex<BTreeSet<Url>>>,
7722 cx: &mut Context<Self>,
7723 ) {
7724 let Some(local) = self.as_local_mut() else {
7725 return;
7726 };
7727 // If the language server for this key doesn't match the server id, don't store the
7728 // server. Which will cause it to be dropped, killing the process
7729 if local
7730 .language_server_ids
7731 .get(&key)
7732 .map(|ids| !ids.contains(&server_id))
7733 .unwrap_or(false)
7734 {
7735 return;
7736 }
7737
7738 // Update language_servers collection with Running variant of LanguageServerState
7739 // indicating that the server is up and running and ready
7740 let workspace_folders = workspace_folders.lock().clone();
7741 local.language_servers.insert(
7742 server_id,
7743 LanguageServerState::running(
7744 workspace_folders,
7745 adapter.clone(),
7746 language_server.clone(),
7747 None,
7748 ),
7749 );
7750 if let Some(file_ops_caps) = language_server
7751 .capabilities()
7752 .workspace
7753 .as_ref()
7754 .and_then(|ws| ws.file_operations.as_ref())
7755 {
7756 let did_rename_caps = file_ops_caps.did_rename.as_ref();
7757 let will_rename_caps = file_ops_caps.will_rename.as_ref();
7758 if did_rename_caps.or(will_rename_caps).is_some() {
7759 let watcher = RenamePathsWatchedForServer::default()
7760 .with_did_rename_patterns(did_rename_caps)
7761 .with_will_rename_patterns(will_rename_caps);
7762 local
7763 .language_server_paths_watched_for_rename
7764 .insert(server_id, watcher);
7765 }
7766 }
7767
7768 self.language_server_statuses.insert(
7769 server_id,
7770 LanguageServerStatus {
7771 name: language_server.name().to_string(),
7772 pending_work: Default::default(),
7773 has_pending_diagnostic_updates: false,
7774 progress_tokens: Default::default(),
7775 },
7776 );
7777
7778 cx.emit(LspStoreEvent::LanguageServerAdded(
7779 server_id,
7780 language_server.name(),
7781 Some(key.0),
7782 ));
7783 cx.emit(LspStoreEvent::RefreshInlayHints);
7784
7785 if let Some((downstream_client, project_id)) = self.downstream_client.as_ref() {
7786 downstream_client
7787 .send(proto::StartLanguageServer {
7788 project_id: *project_id,
7789 server: Some(proto::LanguageServer {
7790 id: server_id.0 as u64,
7791 name: language_server.name().to_string(),
7792 worktree_id: Some(key.0.to_proto()),
7793 }),
7794 })
7795 .log_err();
7796 }
7797
7798 // Tell the language server about every open buffer in the worktree that matches the language.
7799 self.buffer_store.clone().update(cx, |buffer_store, cx| {
7800 for buffer_handle in buffer_store.buffers() {
7801 let buffer = buffer_handle.read(cx);
7802 let file = match File::from_dyn(buffer.file()) {
7803 Some(file) => file,
7804 None => continue,
7805 };
7806 let language = match buffer.language() {
7807 Some(language) => language,
7808 None => continue,
7809 };
7810
7811 if file.worktree.read(cx).id() != key.0
7812 || !self
7813 .languages
7814 .lsp_adapters(&language.name())
7815 .iter()
7816 .any(|a| a.name == key.1)
7817 {
7818 continue;
7819 }
7820 // didOpen
7821 let file = match file.as_local() {
7822 Some(file) => file,
7823 None => continue,
7824 };
7825
7826 let local = self.as_local_mut().unwrap();
7827
7828 if local.registered_buffers.contains_key(&buffer.remote_id()) {
7829 let versions = local
7830 .buffer_snapshots
7831 .entry(buffer.remote_id())
7832 .or_default()
7833 .entry(server_id)
7834 .and_modify(|_| {
7835 assert!(
7836 false,
7837 "There should not be an existing snapshot for a newly inserted buffer"
7838 )
7839 })
7840 .or_insert_with(|| {
7841 vec![LspBufferSnapshot {
7842 version: 0,
7843 snapshot: buffer.text_snapshot(),
7844 }]
7845 });
7846
7847 let snapshot = versions.last().unwrap();
7848 let version = snapshot.version;
7849 let initial_snapshot = &snapshot.snapshot;
7850 let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
7851 language_server.register_buffer(
7852 uri,
7853 adapter.language_id(&language.name()),
7854 version,
7855 initial_snapshot.text(),
7856 );
7857 }
7858 buffer_handle.update(cx, |buffer, cx| {
7859 buffer.set_completion_triggers(
7860 server_id,
7861 language_server
7862 .capabilities()
7863 .completion_provider
7864 .as_ref()
7865 .and_then(|provider| {
7866 provider
7867 .trigger_characters
7868 .as_ref()
7869 .map(|characters| characters.iter().cloned().collect())
7870 })
7871 .unwrap_or_default(),
7872 cx,
7873 )
7874 });
7875 }
7876 });
7877
7878 cx.notify();
7879 }
7880
7881 pub fn language_servers_running_disk_based_diagnostics(
7882 &self,
7883 ) -> impl Iterator<Item = LanguageServerId> + '_ {
7884 self.language_server_statuses
7885 .iter()
7886 .filter_map(|(id, status)| {
7887 if status.has_pending_diagnostic_updates {
7888 Some(*id)
7889 } else {
7890 None
7891 }
7892 })
7893 }
7894
7895 pub(crate) fn cancel_language_server_work_for_buffers(
7896 &mut self,
7897 buffers: impl IntoIterator<Item = Entity<Buffer>>,
7898 cx: &mut Context<Self>,
7899 ) {
7900 if let Some((client, project_id)) = self.upstream_client() {
7901 let request = client.request(proto::CancelLanguageServerWork {
7902 project_id,
7903 work: Some(proto::cancel_language_server_work::Work::Buffers(
7904 proto::cancel_language_server_work::Buffers {
7905 buffer_ids: buffers
7906 .into_iter()
7907 .map(|b| b.read(cx).remote_id().to_proto())
7908 .collect(),
7909 },
7910 )),
7911 });
7912 cx.background_spawn(request).detach_and_log_err(cx);
7913 } else if let Some(local) = self.as_local() {
7914 let servers = buffers
7915 .into_iter()
7916 .flat_map(|buffer| {
7917 buffer.update(cx, |buffer, cx| {
7918 local.language_server_ids_for_buffer(buffer, cx).into_iter()
7919 })
7920 })
7921 .collect::<HashSet<_>>();
7922 for server_id in servers {
7923 self.cancel_language_server_work(server_id, None, cx);
7924 }
7925 }
7926 }
7927
7928 pub(crate) fn cancel_language_server_work(
7929 &mut self,
7930 server_id: LanguageServerId,
7931 token_to_cancel: Option<String>,
7932 cx: &mut Context<Self>,
7933 ) {
7934 if let Some(local) = self.as_local() {
7935 let status = self.language_server_statuses.get(&server_id);
7936 let server = local.language_servers.get(&server_id);
7937 if let Some((LanguageServerState::Running { server, .. }, status)) = server.zip(status)
7938 {
7939 for (token, progress) in &status.pending_work {
7940 if let Some(token_to_cancel) = token_to_cancel.as_ref() {
7941 if token != token_to_cancel {
7942 continue;
7943 }
7944 }
7945 if progress.is_cancellable {
7946 server
7947 .notify::<lsp::notification::WorkDoneProgressCancel>(
7948 &WorkDoneProgressCancelParams {
7949 token: lsp::NumberOrString::String(token.clone()),
7950 },
7951 )
7952 .ok();
7953 }
7954 }
7955 }
7956 } else if let Some((client, project_id)) = self.upstream_client() {
7957 let request = client.request(proto::CancelLanguageServerWork {
7958 project_id,
7959 work: Some(
7960 proto::cancel_language_server_work::Work::LanguageServerWork(
7961 proto::cancel_language_server_work::LanguageServerWork {
7962 language_server_id: server_id.to_proto(),
7963 token: token_to_cancel,
7964 },
7965 ),
7966 ),
7967 });
7968 cx.background_spawn(request).detach_and_log_err(cx);
7969 }
7970 }
7971
7972 fn register_supplementary_language_server(
7973 &mut self,
7974 id: LanguageServerId,
7975 name: LanguageServerName,
7976 server: Arc<LanguageServer>,
7977 cx: &mut Context<Self>,
7978 ) {
7979 if let Some(local) = self.as_local_mut() {
7980 local
7981 .supplementary_language_servers
7982 .insert(id, (name.clone(), server));
7983 cx.emit(LspStoreEvent::LanguageServerAdded(id, name, None));
7984 }
7985 }
7986
7987 fn unregister_supplementary_language_server(
7988 &mut self,
7989 id: LanguageServerId,
7990 cx: &mut Context<Self>,
7991 ) {
7992 if let Some(local) = self.as_local_mut() {
7993 local.supplementary_language_servers.remove(&id);
7994 cx.emit(LspStoreEvent::LanguageServerRemoved(id));
7995 }
7996 }
7997
7998 pub(crate) fn supplementary_language_servers(
7999 &self,
8000 ) -> impl '_ + Iterator<Item = (LanguageServerId, LanguageServerName)> {
8001 self.as_local().into_iter().flat_map(|local| {
8002 local
8003 .supplementary_language_servers
8004 .iter()
8005 .map(|(id, (name, _))| (*id, name.clone()))
8006 })
8007 }
8008
8009 pub fn language_server_adapter_for_id(
8010 &self,
8011 id: LanguageServerId,
8012 ) -> Option<Arc<CachedLspAdapter>> {
8013 self.as_local()
8014 .and_then(|local| local.language_servers.get(&id))
8015 .and_then(|language_server_state| match language_server_state {
8016 LanguageServerState::Running { adapter, .. } => Some(adapter.clone()),
8017 _ => None,
8018 })
8019 }
8020
8021 pub(super) fn update_local_worktree_language_servers(
8022 &mut self,
8023 worktree_handle: &Entity<Worktree>,
8024 changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
8025 cx: &mut Context<Self>,
8026 ) {
8027 if changes.is_empty() {
8028 return;
8029 }
8030
8031 let Some(local) = self.as_local() else { return };
8032
8033 local.prettier_store.update(cx, |prettier_store, cx| {
8034 prettier_store.update_prettier_settings(&worktree_handle, changes, cx)
8035 });
8036
8037 let worktree_id = worktree_handle.read(cx).id();
8038 let mut language_server_ids = local
8039 .language_server_ids
8040 .iter()
8041 .flat_map(|((server_worktree, _), server_ids)| {
8042 server_ids
8043 .iter()
8044 .filter_map(|server_id| server_worktree.eq(&worktree_id).then(|| *server_id))
8045 })
8046 .collect::<Vec<_>>();
8047 language_server_ids.sort();
8048 language_server_ids.dedup();
8049
8050 let abs_path = worktree_handle.read(cx).abs_path();
8051 for server_id in &language_server_ids {
8052 if let Some(LanguageServerState::Running { server, .. }) =
8053 local.language_servers.get(server_id)
8054 {
8055 if let Some(watched_paths) = local
8056 .language_server_watched_paths
8057 .get(server_id)
8058 .and_then(|paths| paths.worktree_paths.get(&worktree_id))
8059 {
8060 let params = lsp::DidChangeWatchedFilesParams {
8061 changes: changes
8062 .iter()
8063 .filter_map(|(path, _, change)| {
8064 if !watched_paths.is_match(path) {
8065 return None;
8066 }
8067 let typ = match change {
8068 PathChange::Loaded => return None,
8069 PathChange::Added => lsp::FileChangeType::CREATED,
8070 PathChange::Removed => lsp::FileChangeType::DELETED,
8071 PathChange::Updated => lsp::FileChangeType::CHANGED,
8072 PathChange::AddedOrUpdated => lsp::FileChangeType::CHANGED,
8073 };
8074 Some(lsp::FileEvent {
8075 uri: lsp::Url::from_file_path(abs_path.join(path)).unwrap(),
8076 typ,
8077 })
8078 })
8079 .collect(),
8080 };
8081 if !params.changes.is_empty() {
8082 server
8083 .notify::<lsp::notification::DidChangeWatchedFiles>(¶ms)
8084 .log_err();
8085 }
8086 }
8087 }
8088 }
8089 }
8090
8091 pub fn wait_for_remote_buffer(
8092 &mut self,
8093 id: BufferId,
8094 cx: &mut Context<Self>,
8095 ) -> Task<Result<Entity<Buffer>>> {
8096 self.buffer_store.update(cx, |buffer_store, cx| {
8097 buffer_store.wait_for_remote_buffer(id, cx)
8098 })
8099 }
8100
8101 fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
8102 proto::Symbol {
8103 language_server_name: symbol.language_server_name.0.to_string(),
8104 source_worktree_id: symbol.source_worktree_id.to_proto(),
8105 language_server_id: symbol.source_language_server_id.to_proto(),
8106 worktree_id: symbol.path.worktree_id.to_proto(),
8107 path: symbol.path.path.as_ref().to_proto(),
8108 name: symbol.name.clone(),
8109 kind: unsafe { mem::transmute::<lsp::SymbolKind, i32>(symbol.kind) },
8110 start: Some(proto::PointUtf16 {
8111 row: symbol.range.start.0.row,
8112 column: symbol.range.start.0.column,
8113 }),
8114 end: Some(proto::PointUtf16 {
8115 row: symbol.range.end.0.row,
8116 column: symbol.range.end.0.column,
8117 }),
8118 signature: symbol.signature.to_vec(),
8119 }
8120 }
8121
8122 fn deserialize_symbol(serialized_symbol: proto::Symbol) -> Result<CoreSymbol> {
8123 let source_worktree_id = WorktreeId::from_proto(serialized_symbol.source_worktree_id);
8124 let worktree_id = WorktreeId::from_proto(serialized_symbol.worktree_id);
8125 let kind = unsafe { mem::transmute::<i32, lsp::SymbolKind>(serialized_symbol.kind) };
8126 let path = ProjectPath {
8127 worktree_id,
8128 path: Arc::<Path>::from_proto(serialized_symbol.path),
8129 };
8130
8131 let start = serialized_symbol
8132 .start
8133 .ok_or_else(|| anyhow!("invalid start"))?;
8134 let end = serialized_symbol
8135 .end
8136 .ok_or_else(|| anyhow!("invalid end"))?;
8137 Ok(CoreSymbol {
8138 language_server_name: LanguageServerName(serialized_symbol.language_server_name.into()),
8139 source_worktree_id,
8140 source_language_server_id: LanguageServerId::from_proto(
8141 serialized_symbol.language_server_id,
8142 ),
8143 path,
8144 name: serialized_symbol.name,
8145 range: Unclipped(PointUtf16::new(start.row, start.column))
8146 ..Unclipped(PointUtf16::new(end.row, end.column)),
8147 kind,
8148 signature: serialized_symbol
8149 .signature
8150 .try_into()
8151 .map_err(|_| anyhow!("invalid signature"))?,
8152 })
8153 }
8154
8155 pub(crate) fn serialize_completion(completion: &CoreCompletion) -> proto::Completion {
8156 proto::Completion {
8157 old_start: Some(serialize_anchor(&completion.old_range.start)),
8158 old_end: Some(serialize_anchor(&completion.old_range.end)),
8159 new_text: completion.new_text.clone(),
8160 server_id: completion.server_id.0 as u64,
8161 lsp_completion: serde_json::to_vec(&completion.lsp_completion).unwrap(),
8162 resolved: completion.resolved,
8163 }
8164 }
8165
8166 pub(crate) fn deserialize_completion(completion: proto::Completion) -> Result<CoreCompletion> {
8167 let old_start = completion
8168 .old_start
8169 .and_then(deserialize_anchor)
8170 .ok_or_else(|| anyhow!("invalid old start"))?;
8171 let old_end = completion
8172 .old_end
8173 .and_then(deserialize_anchor)
8174 .ok_or_else(|| anyhow!("invalid old end"))?;
8175 let lsp_completion = serde_json::from_slice(&completion.lsp_completion)?;
8176
8177 Ok(CoreCompletion {
8178 old_range: old_start..old_end,
8179 new_text: completion.new_text,
8180 server_id: LanguageServerId(completion.server_id as usize),
8181 lsp_completion,
8182 resolved: completion.resolved,
8183 })
8184 }
8185
8186 pub(crate) fn serialize_code_action(action: &CodeAction) -> proto::CodeAction {
8187 let (kind, lsp_action) = match &action.lsp_action {
8188 ActionVariant::Action(code_action) => (
8189 proto::code_action::Kind::Action as i32,
8190 serde_json::to_vec(code_action).unwrap(),
8191 ),
8192 ActionVariant::Command(command) => (
8193 proto::code_action::Kind::Command as i32,
8194 serde_json::to_vec(command).unwrap(),
8195 ),
8196 };
8197
8198 proto::CodeAction {
8199 server_id: action.server_id.0 as u64,
8200 start: Some(serialize_anchor(&action.range.start)),
8201 end: Some(serialize_anchor(&action.range.end)),
8202 lsp_action,
8203 kind,
8204 }
8205 }
8206
8207 pub(crate) fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction> {
8208 let start = action
8209 .start
8210 .and_then(deserialize_anchor)
8211 .ok_or_else(|| anyhow!("invalid start"))?;
8212 let end = action
8213 .end
8214 .and_then(deserialize_anchor)
8215 .ok_or_else(|| anyhow!("invalid end"))?;
8216 let lsp_action = match proto::code_action::Kind::from_i32(action.kind) {
8217 Some(proto::code_action::Kind::Action) => {
8218 ActionVariant::Action(serde_json::from_slice(&action.lsp_action)?)
8219 }
8220 Some(proto::code_action::Kind::Command) => {
8221 ActionVariant::Command(serde_json::from_slice(&action.lsp_action)?)
8222 }
8223 None => anyhow::bail!("Unknown action kind {}", action.kind),
8224 };
8225 Ok(CodeAction {
8226 server_id: LanguageServerId(action.server_id as usize),
8227 range: start..end,
8228 lsp_action,
8229 })
8230 }
8231
8232 fn update_last_formatting_failure<T>(&mut self, formatting_result: &anyhow::Result<T>) {
8233 match &formatting_result {
8234 Ok(_) => self.last_formatting_failure = None,
8235 Err(error) => {
8236 let error_string = format!("{error:#}");
8237 log::error!("Formatting failed: {error_string}");
8238 self.last_formatting_failure
8239 .replace(error_string.lines().join(" "));
8240 }
8241 }
8242 }
8243}
8244
8245impl EventEmitter<LspStoreEvent> for LspStore {}
8246
8247fn remove_empty_hover_blocks(mut hover: Hover) -> Option<Hover> {
8248 hover
8249 .contents
8250 .retain(|hover_block| !hover_block.text.trim().is_empty());
8251 if hover.contents.is_empty() {
8252 None
8253 } else {
8254 Some(hover)
8255 }
8256}
8257
8258async fn populate_labels_for_completions(
8259 mut new_completions: Vec<CoreCompletion>,
8260 language: Option<Arc<Language>>,
8261 lsp_adapter: Option<Arc<CachedLspAdapter>>,
8262 completions: &mut Vec<Completion>,
8263) {
8264 let lsp_completions = new_completions
8265 .iter_mut()
8266 .map(|completion| mem::take(&mut completion.lsp_completion))
8267 .collect::<Vec<_>>();
8268
8269 let labels = if let Some((language, lsp_adapter)) = language.as_ref().zip(lsp_adapter) {
8270 lsp_adapter
8271 .labels_for_completions(&lsp_completions, language)
8272 .await
8273 .log_err()
8274 .unwrap_or_default()
8275 } else {
8276 Vec::new()
8277 };
8278
8279 for ((completion, lsp_completion), label) in new_completions
8280 .into_iter()
8281 .zip(lsp_completions)
8282 .zip(labels.into_iter().chain(iter::repeat(None)))
8283 {
8284 let documentation = if let Some(docs) = lsp_completion.documentation.clone() {
8285 Some(docs.into())
8286 } else {
8287 None
8288 };
8289
8290 let mut label = label.unwrap_or_else(|| {
8291 CodeLabel::fallback_for_completion(&lsp_completion, language.as_deref())
8292 });
8293 ensure_uniform_list_compatible_label(&mut label);
8294
8295 completions.push(Completion {
8296 old_range: completion.old_range,
8297 new_text: completion.new_text,
8298 label,
8299 server_id: completion.server_id,
8300 documentation,
8301 lsp_completion,
8302 confirm: None,
8303 resolved: false,
8304 })
8305 }
8306}
8307
8308#[derive(Debug)]
8309pub enum LanguageServerToQuery {
8310 /// Query language servers in order of users preference, up until one capable of handling the request is found.
8311 FirstCapable,
8312 /// Query a specific language server.
8313 Other(LanguageServerId),
8314}
8315
8316#[derive(Default)]
8317struct RenamePathsWatchedForServer {
8318 did_rename: Vec<RenameActionPredicate>,
8319 will_rename: Vec<RenameActionPredicate>,
8320}
8321
8322impl RenamePathsWatchedForServer {
8323 fn with_did_rename_patterns(
8324 mut self,
8325 did_rename: Option<&FileOperationRegistrationOptions>,
8326 ) -> Self {
8327 if let Some(did_rename) = did_rename {
8328 self.did_rename = did_rename
8329 .filters
8330 .iter()
8331 .filter_map(|filter| filter.try_into().log_err())
8332 .collect();
8333 }
8334 self
8335 }
8336 fn with_will_rename_patterns(
8337 mut self,
8338 will_rename: Option<&FileOperationRegistrationOptions>,
8339 ) -> Self {
8340 if let Some(will_rename) = will_rename {
8341 self.will_rename = will_rename
8342 .filters
8343 .iter()
8344 .filter_map(|filter| filter.try_into().log_err())
8345 .collect();
8346 }
8347 self
8348 }
8349
8350 fn should_send_did_rename(&self, path: &str, is_dir: bool) -> bool {
8351 self.did_rename.iter().any(|pred| pred.eval(path, is_dir))
8352 }
8353 fn should_send_will_rename(&self, path: &str, is_dir: bool) -> bool {
8354 self.will_rename.iter().any(|pred| pred.eval(path, is_dir))
8355 }
8356}
8357
8358impl TryFrom<&FileOperationFilter> for RenameActionPredicate {
8359 type Error = globset::Error;
8360 fn try_from(ops: &FileOperationFilter) -> Result<Self, globset::Error> {
8361 Ok(Self {
8362 kind: ops.pattern.matches.clone(),
8363 glob: GlobBuilder::new(&ops.pattern.glob)
8364 .case_insensitive(
8365 ops.pattern
8366 .options
8367 .as_ref()
8368 .map_or(false, |ops| ops.ignore_case.unwrap_or(false)),
8369 )
8370 .build()?
8371 .compile_matcher(),
8372 })
8373 }
8374}
8375struct RenameActionPredicate {
8376 glob: GlobMatcher,
8377 kind: Option<FileOperationPatternKind>,
8378}
8379
8380impl RenameActionPredicate {
8381 // Returns true if language server should be notified
8382 fn eval(&self, path: &str, is_dir: bool) -> bool {
8383 self.kind.as_ref().map_or(true, |kind| {
8384 let expected_kind = if is_dir {
8385 FileOperationPatternKind::Folder
8386 } else {
8387 FileOperationPatternKind::File
8388 };
8389 kind == &expected_kind
8390 }) && self.glob.is_match(path)
8391 }
8392}
8393
8394#[derive(Default)]
8395struct LanguageServerWatchedPaths {
8396 worktree_paths: HashMap<WorktreeId, GlobSet>,
8397 abs_paths: HashMap<Arc<Path>, (GlobSet, Task<()>)>,
8398}
8399
8400#[derive(Default)]
8401struct LanguageServerWatchedPathsBuilder {
8402 worktree_paths: HashMap<WorktreeId, GlobSet>,
8403 abs_paths: HashMap<Arc<Path>, GlobSet>,
8404}
8405
8406impl LanguageServerWatchedPathsBuilder {
8407 fn watch_worktree(&mut self, worktree_id: WorktreeId, glob_set: GlobSet) {
8408 self.worktree_paths.insert(worktree_id, glob_set);
8409 }
8410 fn watch_abs_path(&mut self, path: Arc<Path>, glob_set: GlobSet) {
8411 self.abs_paths.insert(path, glob_set);
8412 }
8413 fn build(
8414 self,
8415 fs: Arc<dyn Fs>,
8416 language_server_id: LanguageServerId,
8417 cx: &mut Context<LspStore>,
8418 ) -> LanguageServerWatchedPaths {
8419 let project = cx.weak_entity();
8420
8421 const LSP_ABS_PATH_OBSERVE: Duration = Duration::from_millis(100);
8422 let abs_paths = self
8423 .abs_paths
8424 .into_iter()
8425 .map(|(abs_path, globset)| {
8426 let task = cx.spawn({
8427 let abs_path = abs_path.clone();
8428 let fs = fs.clone();
8429
8430 let lsp_store = project.clone();
8431 |_, mut cx| async move {
8432 maybe!(async move {
8433 let mut push_updates = fs.watch(&abs_path, LSP_ABS_PATH_OBSERVE).await;
8434 while let Some(update) = push_updates.0.next().await {
8435 let action = lsp_store
8436 .update(&mut cx, |this, _| {
8437 let Some(local) = this.as_local() else {
8438 return ControlFlow::Break(());
8439 };
8440 let Some(watcher) = local
8441 .language_server_watched_paths
8442 .get(&language_server_id)
8443 else {
8444 return ControlFlow::Break(());
8445 };
8446 let (globs, _) = watcher.abs_paths.get(&abs_path).expect(
8447 "Watched abs path is not registered with a watcher",
8448 );
8449 let matching_entries = update
8450 .into_iter()
8451 .filter(|event| globs.is_match(&event.path))
8452 .collect::<Vec<_>>();
8453 this.lsp_notify_abs_paths_changed(
8454 language_server_id,
8455 matching_entries,
8456 );
8457 ControlFlow::Continue(())
8458 })
8459 .ok()?;
8460
8461 if action.is_break() {
8462 break;
8463 }
8464 }
8465 Some(())
8466 })
8467 .await;
8468 }
8469 });
8470 (abs_path, (globset, task))
8471 })
8472 .collect();
8473 LanguageServerWatchedPaths {
8474 worktree_paths: self.worktree_paths,
8475 abs_paths,
8476 }
8477 }
8478}
8479
8480struct LspBufferSnapshot {
8481 version: i32,
8482 snapshot: TextBufferSnapshot,
8483}
8484
8485/// A prompt requested by LSP server.
8486#[derive(Clone, Debug)]
8487pub struct LanguageServerPromptRequest {
8488 pub level: PromptLevel,
8489 pub message: String,
8490 pub actions: Vec<MessageActionItem>,
8491 pub lsp_name: String,
8492 pub(crate) response_channel: Sender<MessageActionItem>,
8493}
8494
8495impl LanguageServerPromptRequest {
8496 pub async fn respond(self, index: usize) -> Option<()> {
8497 if let Some(response) = self.actions.into_iter().nth(index) {
8498 self.response_channel.send(response).await.ok()
8499 } else {
8500 None
8501 }
8502 }
8503}
8504impl PartialEq for LanguageServerPromptRequest {
8505 fn eq(&self, other: &Self) -> bool {
8506 self.message == other.message && self.actions == other.actions
8507 }
8508}
8509
8510#[derive(Clone, Debug, PartialEq)]
8511pub enum LanguageServerLogType {
8512 Log(MessageType),
8513 Trace(Option<String>),
8514}
8515
8516impl LanguageServerLogType {
8517 pub fn to_proto(&self) -> proto::language_server_log::LogType {
8518 match self {
8519 Self::Log(log_type) => {
8520 let message_type = match *log_type {
8521 MessageType::ERROR => 1,
8522 MessageType::WARNING => 2,
8523 MessageType::INFO => 3,
8524 MessageType::LOG => 4,
8525 other => {
8526 log::warn!("Unknown lsp log message type: {:?}", other);
8527 4
8528 }
8529 };
8530 proto::language_server_log::LogType::LogMessageType(message_type)
8531 }
8532 Self::Trace(message) => {
8533 proto::language_server_log::LogType::LogTrace(proto::LspLogTrace {
8534 message: message.clone(),
8535 })
8536 }
8537 }
8538 }
8539
8540 pub fn from_proto(log_type: proto::language_server_log::LogType) -> Self {
8541 match log_type {
8542 proto::language_server_log::LogType::LogMessageType(message_type) => {
8543 Self::Log(match message_type {
8544 1 => MessageType::ERROR,
8545 2 => MessageType::WARNING,
8546 3 => MessageType::INFO,
8547 4 => MessageType::LOG,
8548 _ => MessageType::LOG,
8549 })
8550 }
8551 proto::language_server_log::LogType::LogTrace(trace) => Self::Trace(trace.message),
8552 }
8553 }
8554}
8555
8556pub enum LanguageServerState {
8557 Starting {
8558 startup: Task<Option<Arc<LanguageServer>>>,
8559 /// List of language servers that will be added to the workspace once it's initialization completes.
8560 pending_workspace_folders: Arc<Mutex<BTreeSet<Url>>>,
8561 },
8562
8563 Running {
8564 adapter: Arc<CachedLspAdapter>,
8565 server: Arc<LanguageServer>,
8566 simulate_disk_based_diagnostics_completion: Option<Task<()>>,
8567 },
8568}
8569
8570impl LanguageServerState {
8571 fn add_workspace_folder(&self, uri: Url) {
8572 match self {
8573 LanguageServerState::Starting {
8574 pending_workspace_folders,
8575 ..
8576 } => {
8577 pending_workspace_folders.lock().insert(uri);
8578 }
8579 LanguageServerState::Running { server, .. } => {
8580 server.add_workspace_folder(uri);
8581 }
8582 }
8583 }
8584 fn _remove_workspace_folder(&self, uri: Url) {
8585 match self {
8586 LanguageServerState::Starting {
8587 pending_workspace_folders,
8588 ..
8589 } => {
8590 pending_workspace_folders.lock().remove(&uri);
8591 }
8592 LanguageServerState::Running { server, .. } => server.remove_workspace_folder(uri),
8593 }
8594 }
8595 fn running(
8596 workspace_folders: BTreeSet<Url>,
8597 adapter: Arc<CachedLspAdapter>,
8598 server: Arc<LanguageServer>,
8599 simulate_disk_based_diagnostics_completion: Option<Task<()>>,
8600 ) -> Self {
8601 server.set_workspace_folders(workspace_folders);
8602 Self::Running {
8603 adapter,
8604 server,
8605 simulate_disk_based_diagnostics_completion,
8606 }
8607 }
8608}
8609
8610impl std::fmt::Debug for LanguageServerState {
8611 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
8612 match self {
8613 LanguageServerState::Starting { .. } => {
8614 f.debug_struct("LanguageServerState::Starting").finish()
8615 }
8616 LanguageServerState::Running { .. } => {
8617 f.debug_struct("LanguageServerState::Running").finish()
8618 }
8619 }
8620 }
8621}
8622
8623#[derive(Clone, Debug, Serialize)]
8624pub struct LanguageServerProgress {
8625 pub is_disk_based_diagnostics_progress: bool,
8626 pub is_cancellable: bool,
8627 pub title: Option<String>,
8628 pub message: Option<String>,
8629 pub percentage: Option<usize>,
8630 #[serde(skip_serializing)]
8631 pub last_update_at: Instant,
8632}
8633
8634#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize)]
8635pub struct DiagnosticSummary {
8636 pub error_count: usize,
8637 pub warning_count: usize,
8638}
8639
8640impl DiagnosticSummary {
8641 pub fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
8642 let mut this = Self {
8643 error_count: 0,
8644 warning_count: 0,
8645 };
8646
8647 for entry in diagnostics {
8648 if entry.diagnostic.is_primary {
8649 match entry.diagnostic.severity {
8650 DiagnosticSeverity::ERROR => this.error_count += 1,
8651 DiagnosticSeverity::WARNING => this.warning_count += 1,
8652 _ => {}
8653 }
8654 }
8655 }
8656
8657 this
8658 }
8659
8660 pub fn is_empty(&self) -> bool {
8661 self.error_count == 0 && self.warning_count == 0
8662 }
8663
8664 pub fn to_proto(
8665 &self,
8666 language_server_id: LanguageServerId,
8667 path: &Path,
8668 ) -> proto::DiagnosticSummary {
8669 proto::DiagnosticSummary {
8670 path: path.to_proto(),
8671 language_server_id: language_server_id.0 as u64,
8672 error_count: self.error_count as u32,
8673 warning_count: self.warning_count as u32,
8674 }
8675 }
8676}
8677
8678#[derive(Clone, Debug)]
8679pub enum CompletionDocumentation {
8680 /// There is no documentation for this completion.
8681 Undocumented,
8682 /// A single line of documentation.
8683 SingleLine(SharedString),
8684 /// Multiple lines of plain text documentation.
8685 MultiLinePlainText(SharedString),
8686 /// Markdown documentation.
8687 MultiLineMarkdown(SharedString),
8688}
8689
8690impl From<lsp::Documentation> for CompletionDocumentation {
8691 fn from(docs: lsp::Documentation) -> Self {
8692 match docs {
8693 lsp::Documentation::String(text) => {
8694 if text.lines().count() <= 1 {
8695 CompletionDocumentation::SingleLine(text.into())
8696 } else {
8697 CompletionDocumentation::MultiLinePlainText(text.into())
8698 }
8699 }
8700
8701 lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value }) => match kind {
8702 lsp::MarkupKind::PlainText => {
8703 if value.lines().count() <= 1 {
8704 CompletionDocumentation::SingleLine(value.into())
8705 } else {
8706 CompletionDocumentation::MultiLinePlainText(value.into())
8707 }
8708 }
8709
8710 lsp::MarkupKind::Markdown => {
8711 CompletionDocumentation::MultiLineMarkdown(value.into())
8712 }
8713 },
8714 }
8715 }
8716}
8717
8718fn glob_literal_prefix(glob: &Path) -> PathBuf {
8719 glob.components()
8720 .take_while(|component| match component {
8721 path::Component::Normal(part) => !part.to_string_lossy().contains(['*', '?', '{', '}']),
8722 _ => true,
8723 })
8724 .collect()
8725}
8726
8727pub struct SshLspAdapter {
8728 name: LanguageServerName,
8729 binary: LanguageServerBinary,
8730 initialization_options: Option<String>,
8731 code_action_kinds: Option<Vec<CodeActionKind>>,
8732}
8733
8734impl SshLspAdapter {
8735 pub fn new(
8736 name: LanguageServerName,
8737 binary: LanguageServerBinary,
8738 initialization_options: Option<String>,
8739 code_action_kinds: Option<String>,
8740 ) -> Self {
8741 Self {
8742 name,
8743 binary,
8744 initialization_options,
8745 code_action_kinds: code_action_kinds
8746 .as_ref()
8747 .and_then(|c| serde_json::from_str(c).ok()),
8748 }
8749 }
8750}
8751
8752#[async_trait(?Send)]
8753impl LspAdapter for SshLspAdapter {
8754 fn name(&self) -> LanguageServerName {
8755 self.name.clone()
8756 }
8757
8758 async fn initialization_options(
8759 self: Arc<Self>,
8760 _: &dyn Fs,
8761 _: &Arc<dyn LspAdapterDelegate>,
8762 ) -> Result<Option<serde_json::Value>> {
8763 let Some(options) = &self.initialization_options else {
8764 return Ok(None);
8765 };
8766 let result = serde_json::from_str(options)?;
8767 Ok(result)
8768 }
8769
8770 fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
8771 self.code_action_kinds.clone()
8772 }
8773
8774 async fn check_if_user_installed(
8775 &self,
8776 _: &dyn LspAdapterDelegate,
8777 _: Arc<dyn LanguageToolchainStore>,
8778 _: &AsyncApp,
8779 ) -> Option<LanguageServerBinary> {
8780 Some(self.binary.clone())
8781 }
8782
8783 async fn cached_server_binary(
8784 &self,
8785 _: PathBuf,
8786 _: &dyn LspAdapterDelegate,
8787 ) -> Option<LanguageServerBinary> {
8788 None
8789 }
8790
8791 async fn fetch_latest_server_version(
8792 &self,
8793 _: &dyn LspAdapterDelegate,
8794 ) -> Result<Box<dyn 'static + Send + Any>> {
8795 anyhow::bail!("SshLspAdapter does not support fetch_latest_server_version")
8796 }
8797
8798 async fn fetch_server_binary(
8799 &self,
8800 _: Box<dyn 'static + Send + Any>,
8801 _: PathBuf,
8802 _: &dyn LspAdapterDelegate,
8803 ) -> Result<LanguageServerBinary> {
8804 anyhow::bail!("SshLspAdapter does not support fetch_server_binary")
8805 }
8806}
8807
8808pub fn language_server_settings<'a>(
8809 delegate: &'a dyn LspAdapterDelegate,
8810 language: &LanguageServerName,
8811 cx: &'a App,
8812) -> Option<&'a LspSettings> {
8813 language_server_settings_for(
8814 SettingsLocation {
8815 worktree_id: delegate.worktree_id(),
8816 path: delegate.worktree_root_path(),
8817 },
8818 language,
8819 cx,
8820 )
8821}
8822
8823pub(crate) fn language_server_settings_for<'a>(
8824 location: SettingsLocation<'a>,
8825 language: &LanguageServerName,
8826 cx: &'a App,
8827) -> Option<&'a LspSettings> {
8828 ProjectSettings::get(Some(location), cx).lsp.get(language)
8829}
8830
8831pub struct LocalLspAdapterDelegate {
8832 lsp_store: WeakEntity<LspStore>,
8833 worktree: worktree::Snapshot,
8834 fs: Arc<dyn Fs>,
8835 http_client: Arc<dyn HttpClient>,
8836 language_registry: Arc<LanguageRegistry>,
8837 load_shell_env_task: Shared<Task<Option<HashMap<String, String>>>>,
8838}
8839
8840impl LocalLspAdapterDelegate {
8841 pub fn new(
8842 language_registry: Arc<LanguageRegistry>,
8843 environment: &Entity<ProjectEnvironment>,
8844 lsp_store: WeakEntity<LspStore>,
8845 worktree: &Entity<Worktree>,
8846 http_client: Arc<dyn HttpClient>,
8847 fs: Arc<dyn Fs>,
8848 cx: &mut App,
8849 ) -> Arc<Self> {
8850 let (worktree_id, worktree_abs_path) = {
8851 let worktree = worktree.read(cx);
8852 (worktree.id(), worktree.abs_path())
8853 };
8854
8855 let load_shell_env_task = environment.update(cx, |env, cx| {
8856 env.get_environment(Some(worktree_id), Some(worktree_abs_path), cx)
8857 });
8858
8859 Arc::new(Self {
8860 lsp_store,
8861 worktree: worktree.read(cx).snapshot(),
8862 fs,
8863 http_client,
8864 language_registry,
8865 load_shell_env_task,
8866 })
8867 }
8868
8869 fn from_local_lsp(
8870 local: &LocalLspStore,
8871 worktree: &Entity<Worktree>,
8872 cx: &mut App,
8873 ) -> Arc<Self> {
8874 Self::new(
8875 local.languages.clone(),
8876 &local.environment,
8877 local.weak.clone(),
8878 worktree,
8879 local.http_client.clone(),
8880 local.fs.clone(),
8881 cx,
8882 )
8883 }
8884}
8885
8886#[async_trait]
8887impl LspAdapterDelegate for LocalLspAdapterDelegate {
8888 fn show_notification(&self, message: &str, cx: &mut App) {
8889 self.lsp_store
8890 .update(cx, |_, cx| {
8891 cx.emit(LspStoreEvent::Notification(message.to_owned()))
8892 })
8893 .ok();
8894 }
8895
8896 fn http_client(&self) -> Arc<dyn HttpClient> {
8897 self.http_client.clone()
8898 }
8899
8900 fn worktree_id(&self) -> WorktreeId {
8901 self.worktree.id()
8902 }
8903
8904 fn exists(&self, path: &Path, is_dir: Option<bool>) -> bool {
8905 self.worktree.entry_for_path(path).map_or(false, |entry| {
8906 is_dir.map_or(true, |is_required_to_be_dir| {
8907 is_required_to_be_dir == entry.is_dir()
8908 })
8909 })
8910 }
8911
8912 fn worktree_root_path(&self) -> &Path {
8913 self.worktree.abs_path().as_ref()
8914 }
8915
8916 async fn shell_env(&self) -> HashMap<String, String> {
8917 let task = self.load_shell_env_task.clone();
8918 task.await.unwrap_or_default()
8919 }
8920
8921 async fn npm_package_installed_version(
8922 &self,
8923 package_name: &str,
8924 ) -> Result<Option<(PathBuf, String)>> {
8925 let local_package_directory = self.worktree_root_path();
8926 let node_modules_directory = local_package_directory.join("node_modules");
8927
8928 if let Some(version) =
8929 read_package_installed_version(node_modules_directory.clone(), package_name).await?
8930 {
8931 return Ok(Some((node_modules_directory, version)));
8932 }
8933 let Some(npm) = self.which("npm".as_ref()).await else {
8934 log::warn!(
8935 "Failed to find npm executable for {:?}",
8936 local_package_directory
8937 );
8938 return Ok(None);
8939 };
8940
8941 let env = self.shell_env().await;
8942 let output = util::command::new_smol_command(&npm)
8943 .args(["root", "-g"])
8944 .envs(env)
8945 .current_dir(local_package_directory)
8946 .output()
8947 .await?;
8948 let global_node_modules =
8949 PathBuf::from(String::from_utf8_lossy(&output.stdout).to_string());
8950
8951 if let Some(version) =
8952 read_package_installed_version(global_node_modules.clone(), package_name).await?
8953 {
8954 return Ok(Some((global_node_modules, version)));
8955 }
8956 return Ok(None);
8957 }
8958
8959 #[cfg(not(target_os = "windows"))]
8960 async fn which(&self, command: &OsStr) -> Option<PathBuf> {
8961 let worktree_abs_path = self.worktree.abs_path();
8962 let shell_path = self.shell_env().await.get("PATH").cloned();
8963 which::which_in(command, shell_path.as_ref(), worktree_abs_path).ok()
8964 }
8965
8966 #[cfg(target_os = "windows")]
8967 async fn which(&self, command: &OsStr) -> Option<PathBuf> {
8968 // todo(windows) Getting the shell env variables in a current directory on Windows is more complicated than other platforms
8969 // there isn't a 'default shell' necessarily. The closest would be the default profile on the windows terminal
8970 // SEE: https://learn.microsoft.com/en-us/windows/terminal/customize-settings/startup
8971 which::which(command).ok()
8972 }
8973
8974 async fn try_exec(&self, command: LanguageServerBinary) -> Result<()> {
8975 let working_dir = self.worktree_root_path();
8976 let output = util::command::new_smol_command(&command.path)
8977 .args(command.arguments)
8978 .envs(command.env.clone().unwrap_or_default())
8979 .current_dir(working_dir)
8980 .output()
8981 .await?;
8982
8983 if output.status.success() {
8984 return Ok(());
8985 }
8986 Err(anyhow!(
8987 "{}, stdout: {:?}, stderr: {:?}",
8988 output.status,
8989 String::from_utf8_lossy(&output.stdout),
8990 String::from_utf8_lossy(&output.stderr)
8991 ))
8992 }
8993
8994 fn update_status(
8995 &self,
8996 server_name: LanguageServerName,
8997 status: language::LanguageServerBinaryStatus,
8998 ) {
8999 self.language_registry
9000 .update_lsp_status(server_name, status);
9001 }
9002
9003 async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>> {
9004 let dir = self.language_registry.language_server_download_dir(name)?;
9005
9006 if !dir.exists() {
9007 smol::fs::create_dir_all(&dir)
9008 .await
9009 .context("failed to create container directory")
9010 .log_err()?;
9011 }
9012
9013 Some(dir)
9014 }
9015
9016 async fn read_text_file(&self, path: PathBuf) -> Result<String> {
9017 let entry = self
9018 .worktree
9019 .entry_for_path(&path)
9020 .with_context(|| format!("no worktree entry for path {path:?}"))?;
9021 let abs_path = self
9022 .worktree
9023 .absolutize(&entry.path)
9024 .with_context(|| format!("cannot absolutize path {path:?}"))?;
9025
9026 self.fs.load(&abs_path).await
9027 }
9028}
9029
9030async fn populate_labels_for_symbols(
9031 symbols: Vec<CoreSymbol>,
9032 language_registry: &Arc<LanguageRegistry>,
9033 lsp_adapter: Option<Arc<CachedLspAdapter>>,
9034 output: &mut Vec<Symbol>,
9035) {
9036 #[allow(clippy::mutable_key_type)]
9037 let mut symbols_by_language = HashMap::<Option<Arc<Language>>, Vec<CoreSymbol>>::default();
9038
9039 let mut unknown_paths = BTreeSet::new();
9040 for symbol in symbols {
9041 let language = language_registry
9042 .language_for_file_path(&symbol.path.path)
9043 .await
9044 .ok()
9045 .or_else(|| {
9046 unknown_paths.insert(symbol.path.path.clone());
9047 None
9048 });
9049 symbols_by_language
9050 .entry(language)
9051 .or_default()
9052 .push(symbol);
9053 }
9054
9055 for unknown_path in unknown_paths {
9056 log::info!(
9057 "no language found for symbol path {}",
9058 unknown_path.display()
9059 );
9060 }
9061
9062 let mut label_params = Vec::new();
9063 for (language, mut symbols) in symbols_by_language {
9064 label_params.clear();
9065 label_params.extend(
9066 symbols
9067 .iter_mut()
9068 .map(|symbol| (mem::take(&mut symbol.name), symbol.kind)),
9069 );
9070
9071 let mut labels = Vec::new();
9072 if let Some(language) = language {
9073 let lsp_adapter = lsp_adapter.clone().or_else(|| {
9074 language_registry
9075 .lsp_adapters(&language.name())
9076 .first()
9077 .cloned()
9078 });
9079 if let Some(lsp_adapter) = lsp_adapter {
9080 labels = lsp_adapter
9081 .labels_for_symbols(&label_params, &language)
9082 .await
9083 .log_err()
9084 .unwrap_or_default();
9085 }
9086 }
9087
9088 for ((symbol, (name, _)), label) in symbols
9089 .into_iter()
9090 .zip(label_params.drain(..))
9091 .zip(labels.into_iter().chain(iter::repeat(None)))
9092 {
9093 output.push(Symbol {
9094 language_server_name: symbol.language_server_name,
9095 source_worktree_id: symbol.source_worktree_id,
9096 source_language_server_id: symbol.source_language_server_id,
9097 path: symbol.path,
9098 label: label.unwrap_or_else(|| CodeLabel::plain(name.clone(), None)),
9099 name,
9100 kind: symbol.kind,
9101 range: symbol.range,
9102 signature: symbol.signature,
9103 });
9104 }
9105 }
9106}
9107
9108fn include_text(server: &lsp::LanguageServer) -> Option<bool> {
9109 match server.capabilities().text_document_sync.as_ref()? {
9110 lsp::TextDocumentSyncCapability::Kind(kind) => match *kind {
9111 lsp::TextDocumentSyncKind::NONE => None,
9112 lsp::TextDocumentSyncKind::FULL => Some(true),
9113 lsp::TextDocumentSyncKind::INCREMENTAL => Some(false),
9114 _ => None,
9115 },
9116 lsp::TextDocumentSyncCapability::Options(options) => match options.save.as_ref()? {
9117 lsp::TextDocumentSyncSaveOptions::Supported(supported) => {
9118 if *supported {
9119 Some(true)
9120 } else {
9121 None
9122 }
9123 }
9124 lsp::TextDocumentSyncSaveOptions::SaveOptions(save_options) => {
9125 Some(save_options.include_text.unwrap_or(false))
9126 }
9127 },
9128 }
9129}
9130
9131/// Completion items are displayed in a `UniformList`.
9132/// Usually, those items are single-line strings, but in LSP responses,
9133/// completion items `label`, `detail` and `label_details.description` may contain newlines or long spaces.
9134/// Many language plugins construct these items by joining these parts together, and we may use `CodeLabel::fallback_for_completion` that uses `label` at least.
9135/// All that may lead to a newline being inserted into resulting `CodeLabel.text`, which will force `UniformList` to bloat each entry to occupy more space,
9136/// breaking the completions menu presentation.
9137///
9138/// Sanitize the text to ensure there are no newlines, or, if there are some, remove them and also remove long space sequences if there were newlines.
9139fn ensure_uniform_list_compatible_label(label: &mut CodeLabel) {
9140 let mut new_text = String::with_capacity(label.text.len());
9141 let mut offset_map = vec![0; label.text.len() + 1];
9142 let mut last_char_was_space = false;
9143 let mut new_idx = 0;
9144 let mut chars = label.text.char_indices().fuse();
9145 let mut newlines_removed = false;
9146
9147 while let Some((idx, c)) = chars.next() {
9148 offset_map[idx] = new_idx;
9149
9150 match c {
9151 '\n' if last_char_was_space => {
9152 newlines_removed = true;
9153 }
9154 '\t' | ' ' if last_char_was_space => {}
9155 '\n' if !last_char_was_space => {
9156 new_text.push(' ');
9157 new_idx += 1;
9158 last_char_was_space = true;
9159 newlines_removed = true;
9160 }
9161 ' ' | '\t' => {
9162 new_text.push(' ');
9163 new_idx += 1;
9164 last_char_was_space = true;
9165 }
9166 _ => {
9167 new_text.push(c);
9168 new_idx += c.len_utf8();
9169 last_char_was_space = false;
9170 }
9171 }
9172 }
9173 offset_map[label.text.len()] = new_idx;
9174
9175 // Only modify the label if newlines were removed.
9176 if !newlines_removed {
9177 return;
9178 }
9179
9180 let last_index = new_idx;
9181 let mut run_ranges_errors = Vec::new();
9182 label.runs.retain_mut(|(range, _)| {
9183 match offset_map.get(range.start) {
9184 Some(&start) => range.start = start,
9185 None => {
9186 run_ranges_errors.push(range.clone());
9187 return false;
9188 }
9189 }
9190
9191 match offset_map.get(range.end) {
9192 Some(&end) => range.end = end,
9193 None => {
9194 run_ranges_errors.push(range.clone());
9195 range.end = last_index;
9196 }
9197 }
9198 true
9199 });
9200 if !run_ranges_errors.is_empty() {
9201 log::error!(
9202 "Completion label has errors in its run ranges: {run_ranges_errors:?}, label text: {}",
9203 label.text
9204 );
9205 }
9206
9207 let mut wrong_filter_range = None;
9208 if label.filter_range == (0..label.text.len()) {
9209 label.filter_range = 0..new_text.len();
9210 } else {
9211 let mut original_filter_range = Some(label.filter_range.clone());
9212 match offset_map.get(label.filter_range.start) {
9213 Some(&start) => label.filter_range.start = start,
9214 None => {
9215 wrong_filter_range = original_filter_range.take();
9216 label.filter_range.start = last_index;
9217 }
9218 }
9219
9220 match offset_map.get(label.filter_range.end) {
9221 Some(&end) => label.filter_range.end = end,
9222 None => {
9223 wrong_filter_range = original_filter_range.take();
9224 label.filter_range.end = last_index;
9225 }
9226 }
9227 }
9228 if let Some(wrong_filter_range) = wrong_filter_range {
9229 log::error!(
9230 "Completion label has an invalid filter range: {wrong_filter_range:?}, label text: {}",
9231 label.text
9232 );
9233 }
9234
9235 label.text = new_text;
9236}
9237
9238#[cfg(test)]
9239mod tests {
9240 use language::HighlightId;
9241
9242 use super::*;
9243
9244 #[test]
9245 fn test_glob_literal_prefix() {
9246 assert_eq!(glob_literal_prefix(Path::new("**/*.js")), Path::new(""));
9247 assert_eq!(
9248 glob_literal_prefix(Path::new("node_modules/**/*.js")),
9249 Path::new("node_modules")
9250 );
9251 assert_eq!(
9252 glob_literal_prefix(Path::new("foo/{bar,baz}.js")),
9253 Path::new("foo")
9254 );
9255 assert_eq!(
9256 glob_literal_prefix(Path::new("foo/bar/baz.js")),
9257 Path::new("foo/bar/baz.js")
9258 );
9259
9260 #[cfg(target_os = "windows")]
9261 {
9262 assert_eq!(glob_literal_prefix(Path::new("**\\*.js")), Path::new(""));
9263 assert_eq!(
9264 glob_literal_prefix(Path::new("node_modules\\**/*.js")),
9265 Path::new("node_modules")
9266 );
9267 assert_eq!(
9268 glob_literal_prefix(Path::new("foo/{bar,baz}.js")),
9269 Path::new("foo")
9270 );
9271 assert_eq!(
9272 glob_literal_prefix(Path::new("foo\\bar\\baz.js")),
9273 Path::new("foo/bar/baz.js")
9274 );
9275 }
9276 }
9277
9278 #[test]
9279 fn test_multi_len_chars_normalization() {
9280 let mut label = CodeLabel {
9281 text: "myElˇ (parameter) myElˇ: {\n foo: string;\n}".to_string(),
9282 runs: vec![(0..6, HighlightId(1))],
9283 filter_range: 0..6,
9284 };
9285 ensure_uniform_list_compatible_label(&mut label);
9286 assert_eq!(
9287 label,
9288 CodeLabel {
9289 text: "myElˇ (parameter) myElˇ: { foo: string; }".to_string(),
9290 runs: vec![(0..6, HighlightId(1))],
9291 filter_range: 0..6,
9292 }
9293 );
9294 }
9295}