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