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_line_ending, deserialize_version, serialize_anchor,
44 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, RopeFingerprint};
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<Vec<Hover>>> {
5193 let request_task = self.request_lsp(
5194 buffer.clone(),
5195 LanguageServerToQuery::Primary,
5196 GetHover { position },
5197 cx,
5198 );
5199 cx.spawn(|_, _| async move { request_task.await.map(|hover| hover.into_iter().collect()) })
5200 }
5201
5202 pub fn hover<T: ToPointUtf16>(
5203 &self,
5204 buffer: &Model<Buffer>,
5205 position: T,
5206 cx: &mut ModelContext<Self>,
5207 ) -> Task<Result<Vec<Hover>>> {
5208 let position = position.to_point_utf16(buffer.read(cx));
5209 self.hover_impl(buffer, position, cx)
5210 }
5211
5212 #[inline(never)]
5213 fn completions_impl(
5214 &self,
5215 buffer: &Model<Buffer>,
5216 position: PointUtf16,
5217 cx: &mut ModelContext<Self>,
5218 ) -> Task<Result<Vec<Completion>>> {
5219 if self.is_local() {
5220 let snapshot = buffer.read(cx).snapshot();
5221 let offset = position.to_offset(&snapshot);
5222 let scope = snapshot.language_scope_at(offset);
5223
5224 let server_ids: Vec<_> = self
5225 .language_servers_for_buffer(buffer.read(cx), cx)
5226 .filter(|(_, server)| server.capabilities().completion_provider.is_some())
5227 .filter(|(adapter, _)| {
5228 scope
5229 .as_ref()
5230 .map(|scope| scope.language_allowed(&adapter.name))
5231 .unwrap_or(true)
5232 })
5233 .map(|(_, server)| server.server_id())
5234 .collect();
5235
5236 let buffer = buffer.clone();
5237 cx.spawn(move |this, mut cx| async move {
5238 let mut tasks = Vec::with_capacity(server_ids.len());
5239 this.update(&mut cx, |this, cx| {
5240 for server_id in server_ids {
5241 tasks.push(this.request_lsp(
5242 buffer.clone(),
5243 LanguageServerToQuery::Other(server_id),
5244 GetCompletions { position },
5245 cx,
5246 ));
5247 }
5248 })?;
5249
5250 let mut completions = Vec::new();
5251 for task in tasks {
5252 if let Ok(new_completions) = task.await {
5253 completions.extend_from_slice(&new_completions);
5254 }
5255 }
5256
5257 Ok(completions)
5258 })
5259 } else if let Some(project_id) = self.remote_id() {
5260 self.send_lsp_proto_request(buffer.clone(), project_id, GetCompletions { position }, cx)
5261 } else {
5262 Task::ready(Ok(Default::default()))
5263 }
5264 }
5265 pub fn completions<T: ToOffset + ToPointUtf16>(
5266 &self,
5267 buffer: &Model<Buffer>,
5268 position: T,
5269 cx: &mut ModelContext<Self>,
5270 ) -> Task<Result<Vec<Completion>>> {
5271 let position = position.to_point_utf16(buffer.read(cx));
5272 self.completions_impl(buffer, position, cx)
5273 }
5274
5275 pub fn resolve_completions(
5276 &self,
5277 completion_indices: Vec<usize>,
5278 completions: Arc<RwLock<Box<[Completion]>>>,
5279 cx: &mut ModelContext<Self>,
5280 ) -> Task<Result<bool>> {
5281 let client = self.client();
5282 let language_registry = self.languages().clone();
5283
5284 let is_remote = self.is_remote();
5285 let project_id = self.remote_id();
5286
5287 cx.spawn(move |this, mut cx| async move {
5288 let mut did_resolve = false;
5289 if is_remote {
5290 let project_id =
5291 project_id.ok_or_else(|| anyhow!("Remote project without remote_id"))?;
5292
5293 for completion_index in completion_indices {
5294 let (server_id, completion) = {
5295 let completions_guard = completions.read();
5296 let completion = &completions_guard[completion_index];
5297 if completion.documentation.is_some() {
5298 continue;
5299 }
5300
5301 did_resolve = true;
5302 let server_id = completion.server_id;
5303 let completion = completion.lsp_completion.clone();
5304
5305 (server_id, completion)
5306 };
5307
5308 Self::resolve_completion_documentation_remote(
5309 project_id,
5310 server_id,
5311 completions.clone(),
5312 completion_index,
5313 completion,
5314 client.clone(),
5315 language_registry.clone(),
5316 )
5317 .await;
5318 }
5319 } else {
5320 for completion_index in completion_indices {
5321 let (server_id, completion) = {
5322 let completions_guard = completions.read();
5323 let completion = &completions_guard[completion_index];
5324 if completion.documentation.is_some() {
5325 continue;
5326 }
5327
5328 let server_id = completion.server_id;
5329 let completion = completion.lsp_completion.clone();
5330
5331 (server_id, completion)
5332 };
5333
5334 let server = this
5335 .read_with(&mut cx, |project, _| {
5336 project.language_server_for_id(server_id)
5337 })
5338 .ok()
5339 .flatten();
5340 let Some(server) = server else {
5341 continue;
5342 };
5343
5344 did_resolve = true;
5345 Self::resolve_completion_documentation_local(
5346 server,
5347 completions.clone(),
5348 completion_index,
5349 completion,
5350 language_registry.clone(),
5351 )
5352 .await;
5353 }
5354 }
5355
5356 Ok(did_resolve)
5357 })
5358 }
5359
5360 async fn resolve_completion_documentation_local(
5361 server: Arc<lsp::LanguageServer>,
5362 completions: Arc<RwLock<Box<[Completion]>>>,
5363 completion_index: usize,
5364 completion: lsp::CompletionItem,
5365 language_registry: Arc<LanguageRegistry>,
5366 ) {
5367 let can_resolve = server
5368 .capabilities()
5369 .completion_provider
5370 .as_ref()
5371 .and_then(|options| options.resolve_provider)
5372 .unwrap_or(false);
5373 if !can_resolve {
5374 return;
5375 }
5376
5377 let request = server.request::<lsp::request::ResolveCompletionItem>(completion);
5378 let Some(completion_item) = request.await.log_err() else {
5379 return;
5380 };
5381
5382 if let Some(lsp_documentation) = completion_item.documentation {
5383 let documentation = language::prepare_completion_documentation(
5384 &lsp_documentation,
5385 &language_registry,
5386 None, // TODO: Try to reasonably work out which language the completion is for
5387 )
5388 .await;
5389
5390 let mut completions = completions.write();
5391 let completion = &mut completions[completion_index];
5392 completion.documentation = Some(documentation);
5393 } else {
5394 let mut completions = completions.write();
5395 let completion = &mut completions[completion_index];
5396 completion.documentation = Some(Documentation::Undocumented);
5397 }
5398 }
5399
5400 async fn resolve_completion_documentation_remote(
5401 project_id: u64,
5402 server_id: LanguageServerId,
5403 completions: Arc<RwLock<Box<[Completion]>>>,
5404 completion_index: usize,
5405 completion: lsp::CompletionItem,
5406 client: Arc<Client>,
5407 language_registry: Arc<LanguageRegistry>,
5408 ) {
5409 let request = proto::ResolveCompletionDocumentation {
5410 project_id,
5411 language_server_id: server_id.0 as u64,
5412 lsp_completion: serde_json::to_string(&completion).unwrap().into_bytes(),
5413 };
5414
5415 let Some(response) = client
5416 .request(request)
5417 .await
5418 .context("completion documentation resolve proto request")
5419 .log_err()
5420 else {
5421 return;
5422 };
5423
5424 if response.text.is_empty() {
5425 let mut completions = completions.write();
5426 let completion = &mut completions[completion_index];
5427 completion.documentation = Some(Documentation::Undocumented);
5428 }
5429
5430 let documentation = if response.is_markdown {
5431 Documentation::MultiLineMarkdown(
5432 markdown::parse_markdown(&response.text, &language_registry, None).await,
5433 )
5434 } else if response.text.lines().count() <= 1 {
5435 Documentation::SingleLine(response.text)
5436 } else {
5437 Documentation::MultiLinePlainText(response.text)
5438 };
5439
5440 let mut completions = completions.write();
5441 let completion = &mut completions[completion_index];
5442 completion.documentation = Some(documentation);
5443 }
5444
5445 pub fn apply_additional_edits_for_completion(
5446 &self,
5447 buffer_handle: Model<Buffer>,
5448 completion: Completion,
5449 push_to_history: bool,
5450 cx: &mut ModelContext<Self>,
5451 ) -> Task<Result<Option<Transaction>>> {
5452 let buffer = buffer_handle.read(cx);
5453 let buffer_id = buffer.remote_id();
5454
5455 if self.is_local() {
5456 let server_id = completion.server_id;
5457 let lang_server = match self.language_server_for_buffer(buffer, server_id, cx) {
5458 Some((_, server)) => server.clone(),
5459 _ => return Task::ready(Ok(Default::default())),
5460 };
5461
5462 cx.spawn(move |this, mut cx| async move {
5463 let can_resolve = lang_server
5464 .capabilities()
5465 .completion_provider
5466 .as_ref()
5467 .and_then(|options| options.resolve_provider)
5468 .unwrap_or(false);
5469 let additional_text_edits = if can_resolve {
5470 lang_server
5471 .request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion)
5472 .await?
5473 .additional_text_edits
5474 } else {
5475 completion.lsp_completion.additional_text_edits
5476 };
5477 if let Some(edits) = additional_text_edits {
5478 let edits = this
5479 .update(&mut cx, |this, cx| {
5480 this.edits_from_lsp(
5481 &buffer_handle,
5482 edits,
5483 lang_server.server_id(),
5484 None,
5485 cx,
5486 )
5487 })?
5488 .await?;
5489
5490 buffer_handle.update(&mut cx, |buffer, cx| {
5491 buffer.finalize_last_transaction();
5492 buffer.start_transaction();
5493
5494 for (range, text) in edits {
5495 let primary = &completion.old_range;
5496 let start_within = primary.start.cmp(&range.start, buffer).is_le()
5497 && primary.end.cmp(&range.start, buffer).is_ge();
5498 let end_within = range.start.cmp(&primary.end, buffer).is_le()
5499 && range.end.cmp(&primary.end, buffer).is_ge();
5500
5501 //Skip additional edits which overlap with the primary completion edit
5502 //https://github.com/zed-industries/zed/pull/1871
5503 if !start_within && !end_within {
5504 buffer.edit([(range, text)], None, cx);
5505 }
5506 }
5507
5508 let transaction = if buffer.end_transaction(cx).is_some() {
5509 let transaction = buffer.finalize_last_transaction().unwrap().clone();
5510 if !push_to_history {
5511 buffer.forget_transaction(transaction.id);
5512 }
5513 Some(transaction)
5514 } else {
5515 None
5516 };
5517 Ok(transaction)
5518 })?
5519 } else {
5520 Ok(None)
5521 }
5522 })
5523 } else if let Some(project_id) = self.remote_id() {
5524 let client = self.client.clone();
5525 cx.spawn(move |_, mut cx| async move {
5526 let response = client
5527 .request(proto::ApplyCompletionAdditionalEdits {
5528 project_id,
5529 buffer_id: buffer_id.into(),
5530 completion: Some(language::proto::serialize_completion(&completion)),
5531 })
5532 .await?;
5533
5534 if let Some(transaction) = response.transaction {
5535 let transaction = language::proto::deserialize_transaction(transaction)?;
5536 buffer_handle
5537 .update(&mut cx, |buffer, _| {
5538 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
5539 })?
5540 .await?;
5541 if push_to_history {
5542 buffer_handle.update(&mut cx, |buffer, _| {
5543 buffer.push_transaction(transaction.clone(), Instant::now());
5544 })?;
5545 }
5546 Ok(Some(transaction))
5547 } else {
5548 Ok(None)
5549 }
5550 })
5551 } else {
5552 Task::ready(Err(anyhow!("project does not have a remote id")))
5553 }
5554 }
5555
5556 fn code_actions_impl(
5557 &self,
5558 buffer_handle: &Model<Buffer>,
5559 range: Range<Anchor>,
5560 cx: &mut ModelContext<Self>,
5561 ) -> Task<Result<Vec<CodeAction>>> {
5562 self.request_lsp(
5563 buffer_handle.clone(),
5564 LanguageServerToQuery::Primary,
5565 GetCodeActions { range, kinds: None },
5566 cx,
5567 )
5568 }
5569
5570 pub fn code_actions<T: Clone + ToOffset>(
5571 &self,
5572 buffer_handle: &Model<Buffer>,
5573 range: Range<T>,
5574 cx: &mut ModelContext<Self>,
5575 ) -> Task<Result<Vec<CodeAction>>> {
5576 let buffer = buffer_handle.read(cx);
5577 let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
5578 self.code_actions_impl(buffer_handle, range, cx)
5579 }
5580
5581 pub fn apply_code_action(
5582 &self,
5583 buffer_handle: Model<Buffer>,
5584 mut action: CodeAction,
5585 push_to_history: bool,
5586 cx: &mut ModelContext<Self>,
5587 ) -> Task<Result<ProjectTransaction>> {
5588 if self.is_local() {
5589 let buffer = buffer_handle.read(cx);
5590 let (lsp_adapter, lang_server) = if let Some((adapter, server)) =
5591 self.language_server_for_buffer(buffer, action.server_id, cx)
5592 {
5593 (adapter.clone(), server.clone())
5594 } else {
5595 return Task::ready(Ok(Default::default()));
5596 };
5597 cx.spawn(move |this, mut cx| async move {
5598 Self::try_resolve_code_action(&lang_server, &mut action)
5599 .await
5600 .context("resolving a code action")?;
5601 if let Some(edit) = action.lsp_action.edit {
5602 if edit.changes.is_some() || edit.document_changes.is_some() {
5603 return Self::deserialize_workspace_edit(
5604 this.upgrade().ok_or_else(|| anyhow!("no app present"))?,
5605 edit,
5606 push_to_history,
5607 lsp_adapter.clone(),
5608 lang_server.clone(),
5609 &mut cx,
5610 )
5611 .await;
5612 }
5613 }
5614
5615 if let Some(command) = action.lsp_action.command {
5616 this.update(&mut cx, |this, _| {
5617 this.last_workspace_edits_by_language_server
5618 .remove(&lang_server.server_id());
5619 })?;
5620
5621 let result = lang_server
5622 .request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
5623 command: command.command,
5624 arguments: command.arguments.unwrap_or_default(),
5625 ..Default::default()
5626 })
5627 .await;
5628
5629 if let Err(err) = result {
5630 // TODO: LSP ERROR
5631 return Err(err);
5632 }
5633
5634 return this.update(&mut cx, |this, _| {
5635 this.last_workspace_edits_by_language_server
5636 .remove(&lang_server.server_id())
5637 .unwrap_or_default()
5638 });
5639 }
5640
5641 Ok(ProjectTransaction::default())
5642 })
5643 } else if let Some(project_id) = self.remote_id() {
5644 let client = self.client.clone();
5645 let request = proto::ApplyCodeAction {
5646 project_id,
5647 buffer_id: buffer_handle.read(cx).remote_id().into(),
5648 action: Some(language::proto::serialize_code_action(&action)),
5649 };
5650 cx.spawn(move |this, mut cx| async move {
5651 let response = client
5652 .request(request)
5653 .await?
5654 .transaction
5655 .ok_or_else(|| anyhow!("missing transaction"))?;
5656 this.update(&mut cx, |this, cx| {
5657 this.deserialize_project_transaction(response, push_to_history, cx)
5658 })?
5659 .await
5660 })
5661 } else {
5662 Task::ready(Err(anyhow!("project does not have a remote id")))
5663 }
5664 }
5665
5666 fn apply_on_type_formatting(
5667 &self,
5668 buffer: Model<Buffer>,
5669 position: Anchor,
5670 trigger: String,
5671 cx: &mut ModelContext<Self>,
5672 ) -> Task<Result<Option<Transaction>>> {
5673 if self.is_local() {
5674 cx.spawn(move |this, mut cx| async move {
5675 // Do not allow multiple concurrent formatting requests for the
5676 // same buffer.
5677 this.update(&mut cx, |this, cx| {
5678 this.buffers_being_formatted
5679 .insert(buffer.read(cx).remote_id())
5680 })?;
5681
5682 let _cleanup = defer({
5683 let this = this.clone();
5684 let mut cx = cx.clone();
5685 let closure_buffer = buffer.clone();
5686 move || {
5687 this.update(&mut cx, |this, cx| {
5688 this.buffers_being_formatted
5689 .remove(&closure_buffer.read(cx).remote_id());
5690 })
5691 .ok();
5692 }
5693 });
5694
5695 buffer
5696 .update(&mut cx, |buffer, _| {
5697 buffer.wait_for_edits(Some(position.timestamp))
5698 })?
5699 .await?;
5700 this.update(&mut cx, |this, cx| {
5701 let position = position.to_point_utf16(buffer.read(cx));
5702 this.on_type_format(buffer, position, trigger, false, cx)
5703 })?
5704 .await
5705 })
5706 } else if let Some(project_id) = self.remote_id() {
5707 let client = self.client.clone();
5708 let request = proto::OnTypeFormatting {
5709 project_id,
5710 buffer_id: buffer.read(cx).remote_id().into(),
5711 position: Some(serialize_anchor(&position)),
5712 trigger,
5713 version: serialize_version(&buffer.read(cx).version()),
5714 };
5715 cx.spawn(move |_, _| async move {
5716 client
5717 .request(request)
5718 .await?
5719 .transaction
5720 .map(language::proto::deserialize_transaction)
5721 .transpose()
5722 })
5723 } else {
5724 Task::ready(Err(anyhow!("project does not have a remote id")))
5725 }
5726 }
5727
5728 async fn deserialize_edits(
5729 this: Model<Self>,
5730 buffer_to_edit: Model<Buffer>,
5731 edits: Vec<lsp::TextEdit>,
5732 push_to_history: bool,
5733 _: Arc<CachedLspAdapter>,
5734 language_server: Arc<LanguageServer>,
5735 cx: &mut AsyncAppContext,
5736 ) -> Result<Option<Transaction>> {
5737 let edits = this
5738 .update(cx, |this, cx| {
5739 this.edits_from_lsp(
5740 &buffer_to_edit,
5741 edits,
5742 language_server.server_id(),
5743 None,
5744 cx,
5745 )
5746 })?
5747 .await?;
5748
5749 let transaction = buffer_to_edit.update(cx, |buffer, cx| {
5750 buffer.finalize_last_transaction();
5751 buffer.start_transaction();
5752 for (range, text) in edits {
5753 buffer.edit([(range, text)], None, cx);
5754 }
5755
5756 if buffer.end_transaction(cx).is_some() {
5757 let transaction = buffer.finalize_last_transaction().unwrap().clone();
5758 if !push_to_history {
5759 buffer.forget_transaction(transaction.id);
5760 }
5761 Some(transaction)
5762 } else {
5763 None
5764 }
5765 })?;
5766
5767 Ok(transaction)
5768 }
5769
5770 async fn deserialize_workspace_edit(
5771 this: Model<Self>,
5772 edit: lsp::WorkspaceEdit,
5773 push_to_history: bool,
5774 lsp_adapter: Arc<CachedLspAdapter>,
5775 language_server: Arc<LanguageServer>,
5776 cx: &mut AsyncAppContext,
5777 ) -> Result<ProjectTransaction> {
5778 let fs = this.update(cx, |this, _| this.fs.clone())?;
5779 let mut operations = Vec::new();
5780 if let Some(document_changes) = edit.document_changes {
5781 match document_changes {
5782 lsp::DocumentChanges::Edits(edits) => {
5783 operations.extend(edits.into_iter().map(lsp::DocumentChangeOperation::Edit))
5784 }
5785 lsp::DocumentChanges::Operations(ops) => operations = ops,
5786 }
5787 } else if let Some(changes) = edit.changes {
5788 operations.extend(changes.into_iter().map(|(uri, edits)| {
5789 lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
5790 text_document: lsp::OptionalVersionedTextDocumentIdentifier {
5791 uri,
5792 version: None,
5793 },
5794 edits: edits.into_iter().map(OneOf::Left).collect(),
5795 })
5796 }));
5797 }
5798
5799 let mut project_transaction = ProjectTransaction::default();
5800 for operation in operations {
5801 match operation {
5802 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(op)) => {
5803 let abs_path = op
5804 .uri
5805 .to_file_path()
5806 .map_err(|_| anyhow!("can't convert URI to path"))?;
5807
5808 if let Some(parent_path) = abs_path.parent() {
5809 fs.create_dir(parent_path).await?;
5810 }
5811 if abs_path.ends_with("/") {
5812 fs.create_dir(&abs_path).await?;
5813 } else {
5814 fs.create_file(
5815 &abs_path,
5816 op.options
5817 .map(|options| fs::CreateOptions {
5818 overwrite: options.overwrite.unwrap_or(false),
5819 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
5820 })
5821 .unwrap_or_default(),
5822 )
5823 .await?;
5824 }
5825 }
5826
5827 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Rename(op)) => {
5828 let source_abs_path = op
5829 .old_uri
5830 .to_file_path()
5831 .map_err(|_| anyhow!("can't convert URI to path"))?;
5832 let target_abs_path = op
5833 .new_uri
5834 .to_file_path()
5835 .map_err(|_| anyhow!("can't convert URI to path"))?;
5836 fs.rename(
5837 &source_abs_path,
5838 &target_abs_path,
5839 op.options
5840 .map(|options| fs::RenameOptions {
5841 overwrite: options.overwrite.unwrap_or(false),
5842 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
5843 })
5844 .unwrap_or_default(),
5845 )
5846 .await?;
5847 }
5848
5849 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Delete(op)) => {
5850 let abs_path = op
5851 .uri
5852 .to_file_path()
5853 .map_err(|_| anyhow!("can't convert URI to path"))?;
5854 let options = op
5855 .options
5856 .map(|options| fs::RemoveOptions {
5857 recursive: options.recursive.unwrap_or(false),
5858 ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false),
5859 })
5860 .unwrap_or_default();
5861 if abs_path.ends_with("/") {
5862 fs.remove_dir(&abs_path, options).await?;
5863 } else {
5864 fs.remove_file(&abs_path, options).await?;
5865 }
5866 }
5867
5868 lsp::DocumentChangeOperation::Edit(op) => {
5869 let buffer_to_edit = this
5870 .update(cx, |this, cx| {
5871 this.open_local_buffer_via_lsp(
5872 op.text_document.uri,
5873 language_server.server_id(),
5874 lsp_adapter.name.clone(),
5875 cx,
5876 )
5877 })?
5878 .await?;
5879
5880 let edits = this
5881 .update(cx, |this, cx| {
5882 let edits = op.edits.into_iter().map(|edit| match edit {
5883 OneOf::Left(edit) => edit,
5884 OneOf::Right(edit) => edit.text_edit,
5885 });
5886 this.edits_from_lsp(
5887 &buffer_to_edit,
5888 edits,
5889 language_server.server_id(),
5890 op.text_document.version,
5891 cx,
5892 )
5893 })?
5894 .await?;
5895
5896 let transaction = buffer_to_edit.update(cx, |buffer, cx| {
5897 buffer.finalize_last_transaction();
5898 buffer.start_transaction();
5899 for (range, text) in edits {
5900 buffer.edit([(range, text)], None, cx);
5901 }
5902 let transaction = if buffer.end_transaction(cx).is_some() {
5903 let transaction = buffer.finalize_last_transaction().unwrap().clone();
5904 if !push_to_history {
5905 buffer.forget_transaction(transaction.id);
5906 }
5907 Some(transaction)
5908 } else {
5909 None
5910 };
5911
5912 transaction
5913 })?;
5914 if let Some(transaction) = transaction {
5915 project_transaction.0.insert(buffer_to_edit, transaction);
5916 }
5917 }
5918 }
5919 }
5920
5921 Ok(project_transaction)
5922 }
5923
5924 fn prepare_rename_impl(
5925 &self,
5926 buffer: Model<Buffer>,
5927 position: PointUtf16,
5928 cx: &mut ModelContext<Self>,
5929 ) -> Task<Result<Option<Range<Anchor>>>> {
5930 self.request_lsp(
5931 buffer,
5932 LanguageServerToQuery::Primary,
5933 PrepareRename { position },
5934 cx,
5935 )
5936 }
5937 pub fn prepare_rename<T: ToPointUtf16>(
5938 &self,
5939 buffer: Model<Buffer>,
5940 position: T,
5941 cx: &mut ModelContext<Self>,
5942 ) -> Task<Result<Option<Range<Anchor>>>> {
5943 let position = position.to_point_utf16(buffer.read(cx));
5944 self.prepare_rename_impl(buffer, position, cx)
5945 }
5946
5947 fn perform_rename_impl(
5948 &self,
5949 buffer: Model<Buffer>,
5950 position: PointUtf16,
5951 new_name: String,
5952 push_to_history: bool,
5953 cx: &mut ModelContext<Self>,
5954 ) -> Task<Result<ProjectTransaction>> {
5955 let position = position.to_point_utf16(buffer.read(cx));
5956 self.request_lsp(
5957 buffer,
5958 LanguageServerToQuery::Primary,
5959 PerformRename {
5960 position,
5961 new_name,
5962 push_to_history,
5963 },
5964 cx,
5965 )
5966 }
5967 pub fn perform_rename<T: ToPointUtf16>(
5968 &self,
5969 buffer: Model<Buffer>,
5970 position: T,
5971 new_name: String,
5972 push_to_history: bool,
5973 cx: &mut ModelContext<Self>,
5974 ) -> Task<Result<ProjectTransaction>> {
5975 let position = position.to_point_utf16(buffer.read(cx));
5976 self.perform_rename_impl(buffer, position, new_name, push_to_history, cx)
5977 }
5978
5979 pub fn on_type_format_impl(
5980 &self,
5981 buffer: Model<Buffer>,
5982 position: PointUtf16,
5983 trigger: String,
5984 push_to_history: bool,
5985 cx: &mut ModelContext<Self>,
5986 ) -> Task<Result<Option<Transaction>>> {
5987 let tab_size = buffer.update(cx, |buffer, cx| {
5988 language_settings(buffer.language_at(position).as_ref(), buffer.file(), cx).tab_size
5989 });
5990 self.request_lsp(
5991 buffer.clone(),
5992 LanguageServerToQuery::Primary,
5993 OnTypeFormatting {
5994 position,
5995 trigger,
5996 options: lsp_command::lsp_formatting_options(tab_size.get()).into(),
5997 push_to_history,
5998 },
5999 cx,
6000 )
6001 }
6002
6003 pub fn on_type_format<T: ToPointUtf16>(
6004 &self,
6005 buffer: Model<Buffer>,
6006 position: T,
6007 trigger: String,
6008 push_to_history: bool,
6009 cx: &mut ModelContext<Self>,
6010 ) -> Task<Result<Option<Transaction>>> {
6011 let position = position.to_point_utf16(buffer.read(cx));
6012 self.on_type_format_impl(buffer, position, trigger, push_to_history, cx)
6013 }
6014
6015 pub fn inlay_hints<T: ToOffset>(
6016 &self,
6017 buffer_handle: Model<Buffer>,
6018 range: Range<T>,
6019 cx: &mut ModelContext<Self>,
6020 ) -> Task<anyhow::Result<Vec<InlayHint>>> {
6021 let buffer = buffer_handle.read(cx);
6022 let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
6023 self.inlay_hints_impl(buffer_handle, range, cx)
6024 }
6025 fn inlay_hints_impl(
6026 &self,
6027 buffer_handle: Model<Buffer>,
6028 range: Range<Anchor>,
6029 cx: &mut ModelContext<Self>,
6030 ) -> Task<anyhow::Result<Vec<InlayHint>>> {
6031 let buffer = buffer_handle.read(cx);
6032 let range_start = range.start;
6033 let range_end = range.end;
6034 let buffer_id = buffer.remote_id().into();
6035 let lsp_request = InlayHints { range };
6036
6037 if self.is_local() {
6038 let lsp_request_task = self.request_lsp(
6039 buffer_handle.clone(),
6040 LanguageServerToQuery::Primary,
6041 lsp_request,
6042 cx,
6043 );
6044 cx.spawn(move |_, mut cx| async move {
6045 buffer_handle
6046 .update(&mut cx, |buffer, _| {
6047 buffer.wait_for_edits(vec![range_start.timestamp, range_end.timestamp])
6048 })?
6049 .await
6050 .context("waiting for inlay hint request range edits")?;
6051 lsp_request_task.await.context("inlay hints LSP request")
6052 })
6053 } else if let Some(project_id) = self.remote_id() {
6054 let client = self.client.clone();
6055 let request = proto::InlayHints {
6056 project_id,
6057 buffer_id,
6058 start: Some(serialize_anchor(&range_start)),
6059 end: Some(serialize_anchor(&range_end)),
6060 version: serialize_version(&buffer_handle.read(cx).version()),
6061 };
6062 cx.spawn(move |project, cx| async move {
6063 let response = client
6064 .request(request)
6065 .await
6066 .context("inlay hints proto request")?;
6067 LspCommand::response_from_proto(
6068 lsp_request,
6069 response,
6070 project.upgrade().ok_or_else(|| anyhow!("No project"))?,
6071 buffer_handle.clone(),
6072 cx.clone(),
6073 )
6074 .await
6075 .context("inlay hints proto response conversion")
6076 })
6077 } else {
6078 Task::ready(Err(anyhow!("project does not have a remote id")))
6079 }
6080 }
6081
6082 pub fn resolve_inlay_hint(
6083 &self,
6084 hint: InlayHint,
6085 buffer_handle: Model<Buffer>,
6086 server_id: LanguageServerId,
6087 cx: &mut ModelContext<Self>,
6088 ) -> Task<anyhow::Result<InlayHint>> {
6089 if self.is_local() {
6090 let buffer = buffer_handle.read(cx);
6091 let (_, lang_server) = if let Some((adapter, server)) =
6092 self.language_server_for_buffer(buffer, server_id, cx)
6093 {
6094 (adapter.clone(), server.clone())
6095 } else {
6096 return Task::ready(Ok(hint));
6097 };
6098 if !InlayHints::can_resolve_inlays(lang_server.capabilities()) {
6099 return Task::ready(Ok(hint));
6100 }
6101
6102 let buffer_snapshot = buffer.snapshot();
6103 cx.spawn(move |_, mut cx| async move {
6104 let resolve_task = lang_server.request::<lsp::request::InlayHintResolveRequest>(
6105 InlayHints::project_to_lsp_hint(hint, &buffer_snapshot),
6106 );
6107 let resolved_hint = resolve_task
6108 .await
6109 .context("inlay hint resolve LSP request")?;
6110 let resolved_hint = InlayHints::lsp_to_project_hint(
6111 resolved_hint,
6112 &buffer_handle,
6113 server_id,
6114 ResolveState::Resolved,
6115 false,
6116 &mut cx,
6117 )
6118 .await?;
6119 Ok(resolved_hint)
6120 })
6121 } else if let Some(project_id) = self.remote_id() {
6122 let client = self.client.clone();
6123 let request = proto::ResolveInlayHint {
6124 project_id,
6125 buffer_id: buffer_handle.read(cx).remote_id().into(),
6126 language_server_id: server_id.0 as u64,
6127 hint: Some(InlayHints::project_to_proto_hint(hint.clone())),
6128 };
6129 cx.spawn(move |_, _| async move {
6130 let response = client
6131 .request(request)
6132 .await
6133 .context("inlay hints proto request")?;
6134 match response.hint {
6135 Some(resolved_hint) => InlayHints::proto_to_project_hint(resolved_hint)
6136 .context("inlay hints proto resolve response conversion"),
6137 None => Ok(hint),
6138 }
6139 })
6140 } else {
6141 Task::ready(Err(anyhow!("project does not have a remote id")))
6142 }
6143 }
6144
6145 #[allow(clippy::type_complexity)]
6146 pub fn search(
6147 &self,
6148 query: SearchQuery,
6149 cx: &mut ModelContext<Self>,
6150 ) -> Receiver<SearchResult> {
6151 if self.is_local() {
6152 self.search_local(query, cx)
6153 } else if let Some(project_id) = self.remote_id() {
6154 let (tx, rx) = smol::channel::unbounded();
6155 let request = self.client.request(query.to_proto(project_id));
6156 cx.spawn(move |this, mut cx| async move {
6157 let response = request.await?;
6158 let mut result = HashMap::default();
6159 for location in response.locations {
6160 let buffer_id = BufferId::new(location.buffer_id)?;
6161 let target_buffer = this
6162 .update(&mut cx, |this, cx| {
6163 this.wait_for_remote_buffer(buffer_id, cx)
6164 })?
6165 .await?;
6166 let start = location
6167 .start
6168 .and_then(deserialize_anchor)
6169 .ok_or_else(|| anyhow!("missing target start"))?;
6170 let end = location
6171 .end
6172 .and_then(deserialize_anchor)
6173 .ok_or_else(|| anyhow!("missing target end"))?;
6174 result
6175 .entry(target_buffer)
6176 .or_insert(Vec::new())
6177 .push(start..end)
6178 }
6179 for (buffer, ranges) in result {
6180 let _ = tx.send(SearchResult::Buffer { buffer, ranges }).await;
6181 }
6182
6183 if response.limit_reached {
6184 let _ = tx.send(SearchResult::LimitReached).await;
6185 }
6186
6187 Result::<(), anyhow::Error>::Ok(())
6188 })
6189 .detach_and_log_err(cx);
6190 rx
6191 } else {
6192 unimplemented!();
6193 }
6194 }
6195
6196 pub fn search_local(
6197 &self,
6198 query: SearchQuery,
6199 cx: &mut ModelContext<Self>,
6200 ) -> Receiver<SearchResult> {
6201 // Local search is split into several phases.
6202 // TL;DR is that we do 2 passes; initial pass to pick files which contain at least one match
6203 // and the second phase that finds positions of all the matches found in the candidate files.
6204 // The Receiver obtained from this function returns matches sorted by buffer path. Files without a buffer path are reported first.
6205 //
6206 // It gets a bit hairy though, because we must account for files that do not have a persistent representation
6207 // on FS. Namely, if you have an untitled buffer or unsaved changes in a buffer, we want to scan that too.
6208 //
6209 // 1. We initialize a queue of match candidates and feed all opened buffers into it (== unsaved files / untitled buffers).
6210 // 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
6211 // of FS version for that file altogether - after all, what we have in memory is more up-to-date than what's in FS.
6212 // 2. At this point, we have a list of all potentially matching buffers/files.
6213 // We sort that list by buffer path - this list is retained for later use.
6214 // We ensure that all buffers are now opened and available in project.
6215 // 3. We run a scan over all the candidate buffers on multiple background threads.
6216 // We cannot assume that there will even be a match - while at least one match
6217 // is guaranteed for files obtained from FS, the buffers we got from memory (unsaved files/unnamed buffers) might not have a match at all.
6218 // There is also an auxiliary background thread responsible for result gathering.
6219 // 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),
6220 // it keeps it around. It reports matches in sorted order, though it accepts them in unsorted order as well.
6221 // 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
6222 // entry - which might already be available thanks to out-of-order processing.
6223 //
6224 // We could also report matches fully out-of-order, without maintaining a sorted list of matching paths.
6225 // 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.
6226 // 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
6227 // in face of constantly updating list of sorted matches.
6228 // Meanwhile, this implementation offers index stability, since the matches are already reported in a sorted order.
6229 let snapshots = self
6230 .visible_worktrees(cx)
6231 .filter_map(|tree| {
6232 let tree = tree.read(cx).as_local()?;
6233 Some(tree.snapshot())
6234 })
6235 .collect::<Vec<_>>();
6236 let include_root = snapshots.len() > 1;
6237
6238 let background = cx.background_executor().clone();
6239 let path_count: usize = snapshots
6240 .iter()
6241 .map(|s| {
6242 if query.include_ignored() {
6243 s.file_count()
6244 } else {
6245 s.visible_file_count()
6246 }
6247 })
6248 .sum();
6249 if path_count == 0 {
6250 let (_, rx) = smol::channel::bounded(1024);
6251 return rx;
6252 }
6253 let workers = background.num_cpus().min(path_count);
6254 let (matching_paths_tx, matching_paths_rx) = smol::channel::bounded(1024);
6255 let mut unnamed_files = vec![];
6256 let opened_buffers = self
6257 .opened_buffers
6258 .iter()
6259 .filter_map(|(_, b)| {
6260 let buffer = b.upgrade()?;
6261 let (is_ignored, snapshot) = buffer.update(cx, |buffer, cx| {
6262 let is_ignored = buffer
6263 .project_path(cx)
6264 .and_then(|path| self.entry_for_path(&path, cx))
6265 .map_or(false, |entry| entry.is_ignored);
6266 (is_ignored, buffer.snapshot())
6267 });
6268 if is_ignored && !query.include_ignored() {
6269 return None;
6270 } else if let Some(file) = snapshot.file() {
6271 let matched_path = if include_root {
6272 query.file_matches(Some(&file.full_path(cx)))
6273 } else {
6274 query.file_matches(Some(file.path()))
6275 };
6276
6277 if matched_path {
6278 Some((file.path().clone(), (buffer, snapshot)))
6279 } else {
6280 None
6281 }
6282 } else {
6283 unnamed_files.push(buffer);
6284 None
6285 }
6286 })
6287 .collect();
6288 cx.background_executor()
6289 .spawn(Self::background_search(
6290 unnamed_files,
6291 opened_buffers,
6292 cx.background_executor().clone(),
6293 self.fs.clone(),
6294 workers,
6295 query.clone(),
6296 include_root,
6297 path_count,
6298 snapshots,
6299 matching_paths_tx,
6300 ))
6301 .detach();
6302
6303 let (result_tx, result_rx) = smol::channel::bounded(1024);
6304
6305 cx.spawn(|this, mut cx| async move {
6306 const MAX_SEARCH_RESULT_FILES: usize = 5_000;
6307 const MAX_SEARCH_RESULT_RANGES: usize = 10_000;
6308
6309 let mut matching_paths = matching_paths_rx
6310 .take(MAX_SEARCH_RESULT_FILES + 1)
6311 .collect::<Vec<_>>()
6312 .await;
6313 let mut limit_reached = if matching_paths.len() > MAX_SEARCH_RESULT_FILES {
6314 matching_paths.pop();
6315 true
6316 } else {
6317 false
6318 };
6319 matching_paths.sort_by_key(|candidate| (candidate.is_ignored(), candidate.path()));
6320
6321 let mut range_count = 0;
6322 let query = Arc::new(query);
6323
6324 // Now that we know what paths match the query, we will load at most
6325 // 64 buffers at a time to avoid overwhelming the main thread. For each
6326 // opened buffer, we will spawn a background task that retrieves all the
6327 // ranges in the buffer matched by the query.
6328 'outer: for matching_paths_chunk in matching_paths.chunks(64) {
6329 let mut chunk_results = Vec::new();
6330 for matching_path in matching_paths_chunk {
6331 let query = query.clone();
6332 let buffer = match matching_path {
6333 SearchMatchCandidate::OpenBuffer { buffer, .. } => {
6334 Task::ready(Ok(buffer.clone()))
6335 }
6336 SearchMatchCandidate::Path {
6337 worktree_id, path, ..
6338 } => this.update(&mut cx, |this, cx| {
6339 this.open_buffer((*worktree_id, path.clone()), cx)
6340 })?,
6341 };
6342
6343 chunk_results.push(cx.spawn(|cx| async move {
6344 let buffer = buffer.await?;
6345 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot())?;
6346 let ranges = cx
6347 .background_executor()
6348 .spawn(async move {
6349 query
6350 .search(&snapshot, None)
6351 .await
6352 .iter()
6353 .map(|range| {
6354 snapshot.anchor_before(range.start)
6355 ..snapshot.anchor_after(range.end)
6356 })
6357 .collect::<Vec<_>>()
6358 })
6359 .await;
6360 anyhow::Ok((buffer, ranges))
6361 }));
6362 }
6363
6364 let chunk_results = futures::future::join_all(chunk_results).await;
6365 for result in chunk_results {
6366 if let Some((buffer, ranges)) = result.log_err() {
6367 range_count += ranges.len();
6368 result_tx
6369 .send(SearchResult::Buffer { buffer, ranges })
6370 .await?;
6371 if range_count > MAX_SEARCH_RESULT_RANGES {
6372 limit_reached = true;
6373 break 'outer;
6374 }
6375 }
6376 }
6377 }
6378
6379 if limit_reached {
6380 result_tx.send(SearchResult::LimitReached).await?;
6381 }
6382
6383 anyhow::Ok(())
6384 })
6385 .detach();
6386
6387 result_rx
6388 }
6389
6390 /// Pick paths that might potentially contain a match of a given search query.
6391 #[allow(clippy::too_many_arguments)]
6392 async fn background_search(
6393 unnamed_buffers: Vec<Model<Buffer>>,
6394 opened_buffers: HashMap<Arc<Path>, (Model<Buffer>, BufferSnapshot)>,
6395 executor: BackgroundExecutor,
6396 fs: Arc<dyn Fs>,
6397 workers: usize,
6398 query: SearchQuery,
6399 include_root: bool,
6400 path_count: usize,
6401 snapshots: Vec<LocalSnapshot>,
6402 matching_paths_tx: Sender<SearchMatchCandidate>,
6403 ) {
6404 let fs = &fs;
6405 let query = &query;
6406 let matching_paths_tx = &matching_paths_tx;
6407 let snapshots = &snapshots;
6408 for buffer in unnamed_buffers {
6409 matching_paths_tx
6410 .send(SearchMatchCandidate::OpenBuffer {
6411 buffer: buffer.clone(),
6412 path: None,
6413 })
6414 .await
6415 .log_err();
6416 }
6417 for (path, (buffer, _)) in opened_buffers.iter() {
6418 matching_paths_tx
6419 .send(SearchMatchCandidate::OpenBuffer {
6420 buffer: buffer.clone(),
6421 path: Some(path.clone()),
6422 })
6423 .await
6424 .log_err();
6425 }
6426
6427 let paths_per_worker = (path_count + workers - 1) / workers;
6428
6429 executor
6430 .scoped(|scope| {
6431 let max_concurrent_workers = Arc::new(Semaphore::new(workers));
6432
6433 for worker_ix in 0..workers {
6434 let worker_start_ix = worker_ix * paths_per_worker;
6435 let worker_end_ix = worker_start_ix + paths_per_worker;
6436 let opened_buffers = opened_buffers.clone();
6437 let limiter = Arc::clone(&max_concurrent_workers);
6438 scope.spawn({
6439 async move {
6440 let _guard = limiter.acquire().await;
6441 search_snapshots(
6442 snapshots,
6443 worker_start_ix,
6444 worker_end_ix,
6445 query,
6446 matching_paths_tx,
6447 &opened_buffers,
6448 include_root,
6449 fs,
6450 )
6451 .await;
6452 }
6453 });
6454 }
6455
6456 if query.include_ignored() {
6457 for snapshot in snapshots {
6458 for ignored_entry in snapshot.entries(true).filter(|e| e.is_ignored) {
6459 let limiter = Arc::clone(&max_concurrent_workers);
6460 scope.spawn(async move {
6461 let _guard = limiter.acquire().await;
6462 search_ignored_entry(
6463 snapshot,
6464 ignored_entry,
6465 fs,
6466 query,
6467 matching_paths_tx,
6468 )
6469 .await;
6470 });
6471 }
6472 }
6473 }
6474 })
6475 .await;
6476 }
6477
6478 pub fn request_lsp<R: LspCommand>(
6479 &self,
6480 buffer_handle: Model<Buffer>,
6481 server: LanguageServerToQuery,
6482 request: R,
6483 cx: &mut ModelContext<Self>,
6484 ) -> Task<Result<R::Response>>
6485 where
6486 <R::LspRequest as lsp::request::Request>::Result: Send,
6487 <R::LspRequest as lsp::request::Request>::Params: Send,
6488 {
6489 let buffer = buffer_handle.read(cx);
6490 if self.is_local() {
6491 let language_server = match server {
6492 LanguageServerToQuery::Primary => {
6493 match self.primary_language_server_for_buffer(buffer, cx) {
6494 Some((_, server)) => Some(Arc::clone(server)),
6495 None => return Task::ready(Ok(Default::default())),
6496 }
6497 }
6498 LanguageServerToQuery::Other(id) => self
6499 .language_server_for_buffer(buffer, id, cx)
6500 .map(|(_, server)| Arc::clone(server)),
6501 };
6502 let file = File::from_dyn(buffer.file()).and_then(File::as_local);
6503 if let (Some(file), Some(language_server)) = (file, language_server) {
6504 let lsp_params = request.to_lsp(&file.abs_path(cx), buffer, &language_server, cx);
6505 return cx.spawn(move |this, cx| async move {
6506 if !request.check_capabilities(language_server.capabilities()) {
6507 return Ok(Default::default());
6508 }
6509
6510 let result = language_server.request::<R::LspRequest>(lsp_params).await;
6511 let response = match result {
6512 Ok(response) => response,
6513
6514 Err(err) => {
6515 log::warn!(
6516 "Generic lsp request to {} failed: {}",
6517 language_server.name(),
6518 err
6519 );
6520 return Err(err);
6521 }
6522 };
6523
6524 request
6525 .response_from_lsp(
6526 response,
6527 this.upgrade().ok_or_else(|| anyhow!("no app context"))?,
6528 buffer_handle,
6529 language_server.server_id(),
6530 cx,
6531 )
6532 .await
6533 });
6534 }
6535 } else if let Some(project_id) = self.remote_id() {
6536 return self.send_lsp_proto_request(buffer_handle, project_id, request, cx);
6537 }
6538
6539 Task::ready(Ok(Default::default()))
6540 }
6541
6542 fn send_lsp_proto_request<R: LspCommand>(
6543 &self,
6544 buffer: Model<Buffer>,
6545 project_id: u64,
6546 request: R,
6547 cx: &mut ModelContext<'_, Project>,
6548 ) -> Task<anyhow::Result<<R as LspCommand>::Response>> {
6549 let rpc = self.client.clone();
6550 let message = request.to_proto(project_id, buffer.read(cx));
6551 cx.spawn(move |this, mut cx| async move {
6552 // Ensure the project is still alive by the time the task
6553 // is scheduled.
6554 this.upgrade().context("project dropped")?;
6555 let response = rpc.request(message).await?;
6556 let this = this.upgrade().context("project dropped")?;
6557 if this.update(&mut cx, |this, _| this.is_disconnected())? {
6558 Err(anyhow!("disconnected before completing request"))
6559 } else {
6560 request
6561 .response_from_proto(response, this, buffer, cx)
6562 .await
6563 }
6564 })
6565 }
6566
6567 pub fn find_or_create_local_worktree(
6568 &mut self,
6569 abs_path: impl AsRef<Path>,
6570 visible: bool,
6571 cx: &mut ModelContext<Self>,
6572 ) -> Task<Result<(Model<Worktree>, PathBuf)>> {
6573 let abs_path = abs_path.as_ref();
6574 if let Some((tree, relative_path)) = self.find_local_worktree(abs_path, cx) {
6575 Task::ready(Ok((tree, relative_path)))
6576 } else {
6577 let worktree = self.create_local_worktree(abs_path, visible, cx);
6578 cx.background_executor()
6579 .spawn(async move { Ok((worktree.await?, PathBuf::new())) })
6580 }
6581 }
6582
6583 pub fn find_local_worktree(
6584 &self,
6585 abs_path: &Path,
6586 cx: &AppContext,
6587 ) -> Option<(Model<Worktree>, PathBuf)> {
6588 for tree in &self.worktrees {
6589 if let Some(tree) = tree.upgrade() {
6590 if let Some(relative_path) = tree
6591 .read(cx)
6592 .as_local()
6593 .and_then(|t| abs_path.strip_prefix(t.abs_path()).ok())
6594 {
6595 return Some((tree.clone(), relative_path.into()));
6596 }
6597 }
6598 }
6599 None
6600 }
6601
6602 pub fn is_shared(&self) -> bool {
6603 match &self.client_state {
6604 ProjectClientState::Shared { .. } => true,
6605 ProjectClientState::Local | ProjectClientState::Remote { .. } => false,
6606 }
6607 }
6608
6609 fn create_local_worktree(
6610 &mut self,
6611 abs_path: impl AsRef<Path>,
6612 visible: bool,
6613 cx: &mut ModelContext<Self>,
6614 ) -> Task<Result<Model<Worktree>>> {
6615 let fs = self.fs.clone();
6616 let client = self.client.clone();
6617 let next_entry_id = self.next_entry_id.clone();
6618 let path: Arc<Path> = abs_path.as_ref().into();
6619 let task = self
6620 .loading_local_worktrees
6621 .entry(path.clone())
6622 .or_insert_with(|| {
6623 cx.spawn(move |project, mut cx| {
6624 async move {
6625 let worktree = Worktree::local(
6626 client.clone(),
6627 path.clone(),
6628 visible,
6629 fs,
6630 next_entry_id,
6631 &mut cx,
6632 )
6633 .await;
6634
6635 project.update(&mut cx, |project, _| {
6636 project.loading_local_worktrees.remove(&path);
6637 })?;
6638
6639 let worktree = worktree?;
6640 project
6641 .update(&mut cx, |project, cx| project.add_worktree(&worktree, cx))?;
6642 Ok(worktree)
6643 }
6644 .map_err(Arc::new)
6645 })
6646 .shared()
6647 })
6648 .clone();
6649 cx.background_executor().spawn(async move {
6650 match task.await {
6651 Ok(worktree) => Ok(worktree),
6652 Err(err) => Err(anyhow!("{}", err)),
6653 }
6654 })
6655 }
6656
6657 pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
6658 let mut servers_to_remove = HashMap::default();
6659 let mut servers_to_preserve = HashSet::default();
6660 for ((worktree_id, server_name), &server_id) in &self.language_server_ids {
6661 if worktree_id == &id_to_remove {
6662 servers_to_remove.insert(server_id, server_name.clone());
6663 } else {
6664 servers_to_preserve.insert(server_id);
6665 }
6666 }
6667 servers_to_remove.retain(|server_id, _| !servers_to_preserve.contains(server_id));
6668 for (server_id_to_remove, server_name) in servers_to_remove {
6669 self.language_server_ids
6670 .remove(&(id_to_remove, server_name));
6671 self.language_server_statuses.remove(&server_id_to_remove);
6672 self.language_server_watched_paths
6673 .remove(&server_id_to_remove);
6674 self.last_workspace_edits_by_language_server
6675 .remove(&server_id_to_remove);
6676 self.language_servers.remove(&server_id_to_remove);
6677 cx.emit(Event::LanguageServerRemoved(server_id_to_remove));
6678 }
6679
6680 let mut prettier_instances_to_clean = FuturesUnordered::new();
6681 if let Some(prettier_paths) = self.prettiers_per_worktree.remove(&id_to_remove) {
6682 for path in prettier_paths.iter().flatten() {
6683 if let Some(prettier_instance) = self.prettier_instances.remove(path) {
6684 prettier_instances_to_clean.push(async move {
6685 prettier_instance
6686 .server()
6687 .await
6688 .map(|server| server.server_id())
6689 });
6690 }
6691 }
6692 }
6693 cx.spawn(|project, mut cx| async move {
6694 while let Some(prettier_server_id) = prettier_instances_to_clean.next().await {
6695 if let Some(prettier_server_id) = prettier_server_id {
6696 project
6697 .update(&mut cx, |project, cx| {
6698 project
6699 .supplementary_language_servers
6700 .remove(&prettier_server_id);
6701 cx.emit(Event::LanguageServerRemoved(prettier_server_id));
6702 })
6703 .ok();
6704 }
6705 }
6706 })
6707 .detach();
6708
6709 self.task_inventory().update(cx, |inventory, _| {
6710 inventory.remove_worktree_sources(id_to_remove);
6711 });
6712
6713 self.worktrees.retain(|worktree| {
6714 if let Some(worktree) = worktree.upgrade() {
6715 let id = worktree.read(cx).id();
6716 if id == id_to_remove {
6717 cx.emit(Event::WorktreeRemoved(id));
6718 false
6719 } else {
6720 true
6721 }
6722 } else {
6723 false
6724 }
6725 });
6726 self.metadata_changed(cx);
6727 }
6728
6729 fn add_worktree(&mut self, worktree: &Model<Worktree>, cx: &mut ModelContext<Self>) {
6730 cx.observe(worktree, |_, _, cx| cx.notify()).detach();
6731 cx.subscribe(worktree, |this, worktree, event, cx| {
6732 let is_local = worktree.read(cx).is_local();
6733 match event {
6734 worktree::Event::UpdatedEntries(changes) => {
6735 if is_local {
6736 this.update_local_worktree_buffers(&worktree, changes, cx);
6737 this.update_local_worktree_language_servers(&worktree, changes, cx);
6738 this.update_local_worktree_settings(&worktree, changes, cx);
6739 this.update_prettier_settings(&worktree, changes, cx);
6740 }
6741
6742 cx.emit(Event::WorktreeUpdatedEntries(
6743 worktree.read(cx).id(),
6744 changes.clone(),
6745 ));
6746 }
6747 worktree::Event::UpdatedGitRepositories(updated_repos) => {
6748 if is_local {
6749 this.update_local_worktree_buffers_git_repos(worktree, updated_repos, cx)
6750 }
6751 }
6752 }
6753 })
6754 .detach();
6755
6756 let push_strong_handle = {
6757 let worktree = worktree.read(cx);
6758 self.is_shared() || worktree.is_visible() || worktree.is_remote()
6759 };
6760 if push_strong_handle {
6761 self.worktrees
6762 .push(WorktreeHandle::Strong(worktree.clone()));
6763 } else {
6764 self.worktrees
6765 .push(WorktreeHandle::Weak(worktree.downgrade()));
6766 }
6767
6768 let handle_id = worktree.entity_id();
6769 cx.observe_release(worktree, move |this, worktree, cx| {
6770 let _ = this.remove_worktree(worktree.id(), cx);
6771 cx.update_global::<SettingsStore, _>(|store, cx| {
6772 store
6773 .clear_local_settings(handle_id.as_u64() as usize, cx)
6774 .log_err()
6775 });
6776 })
6777 .detach();
6778
6779 cx.emit(Event::WorktreeAdded);
6780 self.metadata_changed(cx);
6781 }
6782
6783 fn update_local_worktree_buffers(
6784 &mut self,
6785 worktree_handle: &Model<Worktree>,
6786 changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
6787 cx: &mut ModelContext<Self>,
6788 ) {
6789 let snapshot = worktree_handle.read(cx).snapshot();
6790
6791 let mut renamed_buffers = Vec::new();
6792 for (path, entry_id, _) in changes {
6793 let worktree_id = worktree_handle.read(cx).id();
6794 let project_path = ProjectPath {
6795 worktree_id,
6796 path: path.clone(),
6797 };
6798
6799 let buffer_id = match self.local_buffer_ids_by_entry_id.get(entry_id) {
6800 Some(&buffer_id) => buffer_id,
6801 None => match self.local_buffer_ids_by_path.get(&project_path) {
6802 Some(&buffer_id) => buffer_id,
6803 None => {
6804 continue;
6805 }
6806 },
6807 };
6808
6809 let open_buffer = self.opened_buffers.get(&buffer_id);
6810 let buffer = if let Some(buffer) = open_buffer.and_then(|buffer| buffer.upgrade()) {
6811 buffer
6812 } else {
6813 self.opened_buffers.remove(&buffer_id);
6814 self.local_buffer_ids_by_path.remove(&project_path);
6815 self.local_buffer_ids_by_entry_id.remove(entry_id);
6816 continue;
6817 };
6818
6819 buffer.update(cx, |buffer, cx| {
6820 if let Some(old_file) = File::from_dyn(buffer.file()) {
6821 if old_file.worktree != *worktree_handle {
6822 return;
6823 }
6824
6825 let new_file = if let Some(entry) = old_file
6826 .entry_id
6827 .and_then(|entry_id| snapshot.entry_for_id(entry_id))
6828 {
6829 File {
6830 is_local: true,
6831 entry_id: Some(entry.id),
6832 mtime: entry.mtime,
6833 path: entry.path.clone(),
6834 worktree: worktree_handle.clone(),
6835 is_deleted: false,
6836 is_private: entry.is_private,
6837 }
6838 } else if let Some(entry) = snapshot.entry_for_path(old_file.path().as_ref()) {
6839 File {
6840 is_local: true,
6841 entry_id: Some(entry.id),
6842 mtime: entry.mtime,
6843 path: entry.path.clone(),
6844 worktree: worktree_handle.clone(),
6845 is_deleted: false,
6846 is_private: entry.is_private,
6847 }
6848 } else {
6849 File {
6850 is_local: true,
6851 entry_id: old_file.entry_id,
6852 path: old_file.path().clone(),
6853 mtime: old_file.mtime(),
6854 worktree: worktree_handle.clone(),
6855 is_deleted: true,
6856 is_private: old_file.is_private,
6857 }
6858 };
6859
6860 let old_path = old_file.abs_path(cx);
6861 if new_file.abs_path(cx) != old_path {
6862 renamed_buffers.push((cx.handle(), old_file.clone()));
6863 self.local_buffer_ids_by_path.remove(&project_path);
6864 self.local_buffer_ids_by_path.insert(
6865 ProjectPath {
6866 worktree_id,
6867 path: path.clone(),
6868 },
6869 buffer_id,
6870 );
6871 }
6872
6873 if new_file.entry_id != Some(*entry_id) {
6874 self.local_buffer_ids_by_entry_id.remove(entry_id);
6875 if let Some(entry_id) = new_file.entry_id {
6876 self.local_buffer_ids_by_entry_id
6877 .insert(entry_id, buffer_id);
6878 }
6879 }
6880
6881 if new_file != *old_file {
6882 if let Some(project_id) = self.remote_id() {
6883 self.client
6884 .send(proto::UpdateBufferFile {
6885 project_id,
6886 buffer_id: buffer_id.into(),
6887 file: Some(new_file.to_proto()),
6888 })
6889 .log_err();
6890 }
6891
6892 buffer.file_updated(Arc::new(new_file), cx);
6893 }
6894 }
6895 });
6896 }
6897
6898 for (buffer, old_file) in renamed_buffers {
6899 self.unregister_buffer_from_language_servers(&buffer, &old_file, cx);
6900 self.detect_language_for_buffer(&buffer, cx);
6901 self.register_buffer_with_language_servers(&buffer, cx);
6902 }
6903 }
6904
6905 fn update_local_worktree_language_servers(
6906 &mut self,
6907 worktree_handle: &Model<Worktree>,
6908 changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
6909 cx: &mut ModelContext<Self>,
6910 ) {
6911 if changes.is_empty() {
6912 return;
6913 }
6914
6915 let worktree_id = worktree_handle.read(cx).id();
6916 let mut language_server_ids = self
6917 .language_server_ids
6918 .iter()
6919 .filter_map(|((server_worktree_id, _), server_id)| {
6920 (*server_worktree_id == worktree_id).then_some(*server_id)
6921 })
6922 .collect::<Vec<_>>();
6923 language_server_ids.sort();
6924 language_server_ids.dedup();
6925
6926 let abs_path = worktree_handle.read(cx).abs_path();
6927 for server_id in &language_server_ids {
6928 if let Some(LanguageServerState::Running { server, .. }) =
6929 self.language_servers.get(server_id)
6930 {
6931 if let Some(watched_paths) = self
6932 .language_server_watched_paths
6933 .get(&server_id)
6934 .and_then(|paths| paths.get(&worktree_id))
6935 {
6936 let params = lsp::DidChangeWatchedFilesParams {
6937 changes: changes
6938 .iter()
6939 .filter_map(|(path, _, change)| {
6940 if !watched_paths.is_match(&path) {
6941 return None;
6942 }
6943 let typ = match change {
6944 PathChange::Loaded => return None,
6945 PathChange::Added => lsp::FileChangeType::CREATED,
6946 PathChange::Removed => lsp::FileChangeType::DELETED,
6947 PathChange::Updated => lsp::FileChangeType::CHANGED,
6948 PathChange::AddedOrUpdated => lsp::FileChangeType::CHANGED,
6949 };
6950 Some(lsp::FileEvent {
6951 uri: lsp::Url::from_file_path(abs_path.join(path)).unwrap(),
6952 typ,
6953 })
6954 })
6955 .collect(),
6956 };
6957 if !params.changes.is_empty() {
6958 server
6959 .notify::<lsp::notification::DidChangeWatchedFiles>(params)
6960 .log_err();
6961 }
6962 }
6963 }
6964 }
6965 }
6966
6967 fn update_local_worktree_buffers_git_repos(
6968 &mut self,
6969 worktree_handle: Model<Worktree>,
6970 changed_repos: &UpdatedGitRepositoriesSet,
6971 cx: &mut ModelContext<Self>,
6972 ) {
6973 debug_assert!(worktree_handle.read(cx).is_local());
6974
6975 // Identify the loading buffers whose containing repository that has changed.
6976 let future_buffers = self
6977 .loading_buffers_by_path
6978 .iter()
6979 .filter_map(|(project_path, receiver)| {
6980 if project_path.worktree_id != worktree_handle.read(cx).id() {
6981 return None;
6982 }
6983 let path = &project_path.path;
6984 changed_repos
6985 .iter()
6986 .find(|(work_dir, _)| path.starts_with(work_dir))?;
6987 let receiver = receiver.clone();
6988 let path = path.clone();
6989 Some(async move {
6990 wait_for_loading_buffer(receiver)
6991 .await
6992 .ok()
6993 .map(|buffer| (buffer, path))
6994 })
6995 })
6996 .collect::<FuturesUnordered<_>>();
6997
6998 // Identify the current buffers whose containing repository has changed.
6999 let current_buffers = self
7000 .opened_buffers
7001 .values()
7002 .filter_map(|buffer| {
7003 let buffer = buffer.upgrade()?;
7004 let file = File::from_dyn(buffer.read(cx).file())?;
7005 if file.worktree != worktree_handle {
7006 return None;
7007 }
7008 let path = file.path();
7009 changed_repos
7010 .iter()
7011 .find(|(work_dir, _)| path.starts_with(work_dir))?;
7012 Some((buffer, path.clone()))
7013 })
7014 .collect::<Vec<_>>();
7015
7016 if future_buffers.len() + current_buffers.len() == 0 {
7017 return;
7018 }
7019
7020 let remote_id = self.remote_id();
7021 let client = self.client.clone();
7022 cx.spawn(move |_, mut cx| async move {
7023 // Wait for all of the buffers to load.
7024 let future_buffers = future_buffers.collect::<Vec<_>>().await;
7025
7026 // Reload the diff base for every buffer whose containing git repository has changed.
7027 let snapshot =
7028 worktree_handle.update(&mut cx, |tree, _| tree.as_local().unwrap().snapshot())?;
7029 let diff_bases_by_buffer = cx
7030 .background_executor()
7031 .spawn(async move {
7032 future_buffers
7033 .into_iter()
7034 .flatten()
7035 .chain(current_buffers)
7036 .filter_map(|(buffer, path)| {
7037 let (work_directory, repo) =
7038 snapshot.repository_and_work_directory_for_path(&path)?;
7039 let repo = snapshot.get_local_repo(&repo)?;
7040 let relative_path = path.strip_prefix(&work_directory).ok()?;
7041 let base_text = repo.load_index_text(relative_path);
7042 Some((buffer, base_text))
7043 })
7044 .collect::<Vec<_>>()
7045 })
7046 .await;
7047
7048 // Assign the new diff bases on all of the buffers.
7049 for (buffer, diff_base) in diff_bases_by_buffer {
7050 let buffer_id = buffer.update(&mut cx, |buffer, cx| {
7051 buffer.set_diff_base(diff_base.clone(), cx);
7052 buffer.remote_id().into()
7053 })?;
7054 if let Some(project_id) = remote_id {
7055 client
7056 .send(proto::UpdateDiffBase {
7057 project_id,
7058 buffer_id,
7059 diff_base,
7060 })
7061 .log_err();
7062 }
7063 }
7064
7065 anyhow::Ok(())
7066 })
7067 .detach();
7068 }
7069
7070 fn update_local_worktree_settings(
7071 &mut self,
7072 worktree: &Model<Worktree>,
7073 changes: &UpdatedEntriesSet,
7074 cx: &mut ModelContext<Self>,
7075 ) {
7076 if worktree.read(cx).as_local().is_none() {
7077 return;
7078 }
7079 let project_id = self.remote_id();
7080 let worktree_id = worktree.entity_id();
7081 let remote_worktree_id = worktree.read(cx).id();
7082
7083 let mut settings_contents = Vec::new();
7084 for (path, _, change) in changes.iter() {
7085 let removed = change == &PathChange::Removed;
7086 let abs_path = match worktree.read(cx).absolutize(path) {
7087 Ok(abs_path) => abs_path,
7088 Err(e) => {
7089 log::warn!("Cannot absolutize {path:?} received as {change:?} FS change: {e}");
7090 continue;
7091 }
7092 };
7093
7094 if abs_path.ends_with(&*LOCAL_SETTINGS_RELATIVE_PATH) {
7095 let settings_dir = Arc::from(
7096 path.ancestors()
7097 .nth(LOCAL_SETTINGS_RELATIVE_PATH.components().count())
7098 .unwrap(),
7099 );
7100 let fs = self.fs.clone();
7101 settings_contents.push(async move {
7102 (
7103 settings_dir,
7104 if removed {
7105 None
7106 } else {
7107 Some(async move { fs.load(&abs_path).await }.await)
7108 },
7109 )
7110 });
7111 } else if abs_path.ends_with(&*LOCAL_TASKS_RELATIVE_PATH) {
7112 self.task_inventory().update(cx, |task_inventory, cx| {
7113 if removed {
7114 task_inventory.remove_local_static_source(&abs_path);
7115 } else {
7116 let fs = self.fs.clone();
7117 let task_abs_path = abs_path.clone();
7118 task_inventory.add_source(
7119 TaskSourceKind::Worktree {
7120 id: remote_worktree_id,
7121 abs_path,
7122 },
7123 |cx| {
7124 let tasks_file_rx =
7125 watch_config_file(&cx.background_executor(), fs, task_abs_path);
7126 StaticSource::new(
7127 format!("local_tasks_for_workspace_{remote_worktree_id}"),
7128 TrackedFile::new(tasks_file_rx, cx),
7129 cx,
7130 )
7131 },
7132 cx,
7133 );
7134 }
7135 })
7136 } else if abs_path.ends_with(&*LOCAL_VSCODE_TASKS_RELATIVE_PATH) {
7137 self.task_inventory().update(cx, |task_inventory, cx| {
7138 if removed {
7139 task_inventory.remove_local_static_source(&abs_path);
7140 } else {
7141 let fs = self.fs.clone();
7142 let task_abs_path = abs_path.clone();
7143 task_inventory.add_source(
7144 TaskSourceKind::Worktree {
7145 id: remote_worktree_id,
7146 abs_path,
7147 },
7148 |cx| {
7149 let tasks_file_rx =
7150 watch_config_file(&cx.background_executor(), fs, task_abs_path);
7151 StaticSource::new(
7152 format!(
7153 "local_vscode_tasks_for_workspace_{remote_worktree_id}"
7154 ),
7155 TrackedFile::new_convertible::<task::VsCodeTaskFile>(
7156 tasks_file_rx,
7157 cx,
7158 ),
7159 cx,
7160 )
7161 },
7162 cx,
7163 );
7164 }
7165 })
7166 }
7167 }
7168
7169 if settings_contents.is_empty() {
7170 return;
7171 }
7172
7173 let client = self.client.clone();
7174 cx.spawn(move |_, cx| async move {
7175 let settings_contents: Vec<(Arc<Path>, _)> =
7176 futures::future::join_all(settings_contents).await;
7177 cx.update(|cx| {
7178 cx.update_global::<SettingsStore, _>(|store, cx| {
7179 for (directory, file_content) in settings_contents {
7180 let file_content = file_content.and_then(|content| content.log_err());
7181 store
7182 .set_local_settings(
7183 worktree_id.as_u64() as usize,
7184 directory.clone(),
7185 file_content.as_deref(),
7186 cx,
7187 )
7188 .log_err();
7189 if let Some(remote_id) = project_id {
7190 client
7191 .send(proto::UpdateWorktreeSettings {
7192 project_id: remote_id,
7193 worktree_id: remote_worktree_id.to_proto(),
7194 path: directory.to_string_lossy().into_owned(),
7195 content: file_content,
7196 })
7197 .log_err();
7198 }
7199 }
7200 });
7201 })
7202 .ok();
7203 })
7204 .detach();
7205 }
7206
7207 pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
7208 let new_active_entry = entry.and_then(|project_path| {
7209 let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
7210 let entry = worktree.read(cx).entry_for_path(project_path.path)?;
7211 Some(entry.id)
7212 });
7213 if new_active_entry != self.active_entry {
7214 self.active_entry = new_active_entry;
7215 cx.emit(Event::ActiveEntryChanged(new_active_entry));
7216 }
7217 }
7218
7219 pub fn language_servers_running_disk_based_diagnostics(
7220 &self,
7221 ) -> impl Iterator<Item = LanguageServerId> + '_ {
7222 self.language_server_statuses
7223 .iter()
7224 .filter_map(|(id, status)| {
7225 if status.has_pending_diagnostic_updates {
7226 Some(*id)
7227 } else {
7228 None
7229 }
7230 })
7231 }
7232
7233 pub fn diagnostic_summary(&self, include_ignored: bool, cx: &AppContext) -> DiagnosticSummary {
7234 let mut summary = DiagnosticSummary::default();
7235 for (_, _, path_summary) in
7236 self.diagnostic_summaries(include_ignored, cx)
7237 .filter(|(path, _, _)| {
7238 let worktree = self.entry_for_path(path, cx).map(|entry| entry.is_ignored);
7239 include_ignored || worktree == Some(false)
7240 })
7241 {
7242 summary.error_count += path_summary.error_count;
7243 summary.warning_count += path_summary.warning_count;
7244 }
7245 summary
7246 }
7247
7248 pub fn diagnostic_summaries<'a>(
7249 &'a self,
7250 include_ignored: bool,
7251 cx: &'a AppContext,
7252 ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
7253 self.visible_worktrees(cx)
7254 .flat_map(move |worktree| {
7255 let worktree = worktree.read(cx);
7256 let worktree_id = worktree.id();
7257 worktree
7258 .diagnostic_summaries()
7259 .map(move |(path, server_id, summary)| {
7260 (ProjectPath { worktree_id, path }, server_id, summary)
7261 })
7262 })
7263 .filter(move |(path, _, _)| {
7264 let worktree = self.entry_for_path(path, cx).map(|entry| entry.is_ignored);
7265 include_ignored || worktree == Some(false)
7266 })
7267 }
7268
7269 pub fn disk_based_diagnostics_started(
7270 &mut self,
7271 language_server_id: LanguageServerId,
7272 cx: &mut ModelContext<Self>,
7273 ) {
7274 cx.emit(Event::DiskBasedDiagnosticsStarted { language_server_id });
7275 }
7276
7277 pub fn disk_based_diagnostics_finished(
7278 &mut self,
7279 language_server_id: LanguageServerId,
7280 cx: &mut ModelContext<Self>,
7281 ) {
7282 cx.emit(Event::DiskBasedDiagnosticsFinished { language_server_id });
7283 }
7284
7285 pub fn active_entry(&self) -> Option<ProjectEntryId> {
7286 self.active_entry
7287 }
7288
7289 pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Entry> {
7290 self.worktree_for_id(path.worktree_id, cx)?
7291 .read(cx)
7292 .entry_for_path(&path.path)
7293 .cloned()
7294 }
7295
7296 pub fn path_for_entry(&self, entry_id: ProjectEntryId, cx: &AppContext) -> Option<ProjectPath> {
7297 let worktree = self.worktree_for_entry(entry_id, cx)?;
7298 let worktree = worktree.read(cx);
7299 let worktree_id = worktree.id();
7300 let path = worktree.entry_for_id(entry_id)?.path.clone();
7301 Some(ProjectPath { worktree_id, path })
7302 }
7303
7304 pub fn absolute_path(&self, project_path: &ProjectPath, cx: &AppContext) -> Option<PathBuf> {
7305 let workspace_root = self
7306 .worktree_for_id(project_path.worktree_id, cx)?
7307 .read(cx)
7308 .abs_path();
7309 let project_path = project_path.path.as_ref();
7310
7311 Some(if project_path == Path::new("") {
7312 workspace_root.to_path_buf()
7313 } else {
7314 workspace_root.join(project_path)
7315 })
7316 }
7317
7318 pub fn get_repo(
7319 &self,
7320 project_path: &ProjectPath,
7321 cx: &AppContext,
7322 ) -> Option<Arc<Mutex<dyn GitRepository>>> {
7323 self.worktree_for_id(project_path.worktree_id, cx)?
7324 .read(cx)
7325 .as_local()?
7326 .snapshot()
7327 .local_git_repo(&project_path.path)
7328 }
7329
7330 // RPC message handlers
7331
7332 async fn handle_unshare_project(
7333 this: Model<Self>,
7334 _: TypedEnvelope<proto::UnshareProject>,
7335 _: Arc<Client>,
7336 mut cx: AsyncAppContext,
7337 ) -> Result<()> {
7338 this.update(&mut cx, |this, cx| {
7339 if this.is_local() {
7340 this.unshare(cx)?;
7341 } else {
7342 this.disconnected_from_host(cx);
7343 }
7344 Ok(())
7345 })?
7346 }
7347
7348 async fn handle_add_collaborator(
7349 this: Model<Self>,
7350 mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
7351 _: Arc<Client>,
7352 mut cx: AsyncAppContext,
7353 ) -> Result<()> {
7354 let collaborator = envelope
7355 .payload
7356 .collaborator
7357 .take()
7358 .ok_or_else(|| anyhow!("empty collaborator"))?;
7359
7360 let collaborator = Collaborator::from_proto(collaborator)?;
7361 this.update(&mut cx, |this, cx| {
7362 this.shared_buffers.remove(&collaborator.peer_id);
7363 cx.emit(Event::CollaboratorJoined(collaborator.peer_id));
7364 this.collaborators
7365 .insert(collaborator.peer_id, collaborator);
7366 cx.notify();
7367 })?;
7368
7369 Ok(())
7370 }
7371
7372 async fn handle_update_project_collaborator(
7373 this: Model<Self>,
7374 envelope: TypedEnvelope<proto::UpdateProjectCollaborator>,
7375 _: Arc<Client>,
7376 mut cx: AsyncAppContext,
7377 ) -> Result<()> {
7378 let old_peer_id = envelope
7379 .payload
7380 .old_peer_id
7381 .ok_or_else(|| anyhow!("missing old peer id"))?;
7382 let new_peer_id = envelope
7383 .payload
7384 .new_peer_id
7385 .ok_or_else(|| anyhow!("missing new peer id"))?;
7386 this.update(&mut cx, |this, cx| {
7387 let collaborator = this
7388 .collaborators
7389 .remove(&old_peer_id)
7390 .ok_or_else(|| anyhow!("received UpdateProjectCollaborator for unknown peer"))?;
7391 let is_host = collaborator.replica_id == 0;
7392 this.collaborators.insert(new_peer_id, collaborator);
7393
7394 let buffers = this.shared_buffers.remove(&old_peer_id);
7395 log::info!(
7396 "peer {} became {}. moving buffers {:?}",
7397 old_peer_id,
7398 new_peer_id,
7399 &buffers
7400 );
7401 if let Some(buffers) = buffers {
7402 this.shared_buffers.insert(new_peer_id, buffers);
7403 }
7404
7405 if is_host {
7406 this.opened_buffers
7407 .retain(|_, buffer| !matches!(buffer, OpenBuffer::Operations(_)));
7408 this.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
7409 .unwrap();
7410 }
7411
7412 cx.emit(Event::CollaboratorUpdated {
7413 old_peer_id,
7414 new_peer_id,
7415 });
7416 cx.notify();
7417 Ok(())
7418 })?
7419 }
7420
7421 async fn handle_remove_collaborator(
7422 this: Model<Self>,
7423 envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
7424 _: Arc<Client>,
7425 mut cx: AsyncAppContext,
7426 ) -> Result<()> {
7427 this.update(&mut cx, |this, cx| {
7428 let peer_id = envelope
7429 .payload
7430 .peer_id
7431 .ok_or_else(|| anyhow!("invalid peer id"))?;
7432 let replica_id = this
7433 .collaborators
7434 .remove(&peer_id)
7435 .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
7436 .replica_id;
7437 for buffer in this.opened_buffers.values() {
7438 if let Some(buffer) = buffer.upgrade() {
7439 buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
7440 }
7441 }
7442 this.shared_buffers.remove(&peer_id);
7443
7444 cx.emit(Event::CollaboratorLeft(peer_id));
7445 cx.notify();
7446 Ok(())
7447 })?
7448 }
7449
7450 async fn handle_update_project(
7451 this: Model<Self>,
7452 envelope: TypedEnvelope<proto::UpdateProject>,
7453 _: Arc<Client>,
7454 mut cx: AsyncAppContext,
7455 ) -> Result<()> {
7456 this.update(&mut cx, |this, cx| {
7457 // Don't handle messages that were sent before the response to us joining the project
7458 if envelope.message_id > this.join_project_response_message_id {
7459 this.set_worktrees_from_proto(envelope.payload.worktrees, cx)?;
7460 }
7461 Ok(())
7462 })?
7463 }
7464
7465 async fn handle_update_worktree(
7466 this: Model<Self>,
7467 envelope: TypedEnvelope<proto::UpdateWorktree>,
7468 _: Arc<Client>,
7469 mut cx: AsyncAppContext,
7470 ) -> Result<()> {
7471 this.update(&mut cx, |this, cx| {
7472 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
7473 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
7474 worktree.update(cx, |worktree, _| {
7475 let worktree = worktree.as_remote_mut().unwrap();
7476 worktree.update_from_remote(envelope.payload);
7477 });
7478 }
7479 Ok(())
7480 })?
7481 }
7482
7483 async fn handle_update_worktree_settings(
7484 this: Model<Self>,
7485 envelope: TypedEnvelope<proto::UpdateWorktreeSettings>,
7486 _: Arc<Client>,
7487 mut cx: AsyncAppContext,
7488 ) -> Result<()> {
7489 this.update(&mut cx, |this, cx| {
7490 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
7491 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
7492 cx.update_global::<SettingsStore, _>(|store, cx| {
7493 store
7494 .set_local_settings(
7495 worktree.entity_id().as_u64() as usize,
7496 PathBuf::from(&envelope.payload.path).into(),
7497 envelope.payload.content.as_deref(),
7498 cx,
7499 )
7500 .log_err();
7501 });
7502 }
7503 Ok(())
7504 })?
7505 }
7506
7507 async fn handle_create_project_entry(
7508 this: Model<Self>,
7509 envelope: TypedEnvelope<proto::CreateProjectEntry>,
7510 _: Arc<Client>,
7511 mut cx: AsyncAppContext,
7512 ) -> Result<proto::ProjectEntryResponse> {
7513 let worktree = this.update(&mut cx, |this, cx| {
7514 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
7515 this.worktree_for_id(worktree_id, cx)
7516 .ok_or_else(|| anyhow!("worktree not found"))
7517 })??;
7518 let worktree_scan_id = worktree.update(&mut cx, |worktree, _| worktree.scan_id())?;
7519 let entry = worktree
7520 .update(&mut cx, |worktree, cx| {
7521 let worktree = worktree.as_local_mut().unwrap();
7522 let path = PathBuf::from(envelope.payload.path);
7523 worktree.create_entry(path, envelope.payload.is_directory, cx)
7524 })?
7525 .await?;
7526 Ok(proto::ProjectEntryResponse {
7527 entry: entry.as_ref().map(|e| e.into()),
7528 worktree_scan_id: worktree_scan_id as u64,
7529 })
7530 }
7531
7532 async fn handle_rename_project_entry(
7533 this: Model<Self>,
7534 envelope: TypedEnvelope<proto::RenameProjectEntry>,
7535 _: Arc<Client>,
7536 mut cx: AsyncAppContext,
7537 ) -> Result<proto::ProjectEntryResponse> {
7538 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
7539 let worktree = this.update(&mut cx, |this, cx| {
7540 this.worktree_for_entry(entry_id, cx)
7541 .ok_or_else(|| anyhow!("worktree not found"))
7542 })??;
7543 let worktree_scan_id = worktree.update(&mut cx, |worktree, _| worktree.scan_id())?;
7544 let entry = worktree
7545 .update(&mut cx, |worktree, cx| {
7546 let new_path = PathBuf::from(envelope.payload.new_path);
7547 worktree
7548 .as_local_mut()
7549 .unwrap()
7550 .rename_entry(entry_id, new_path, cx)
7551 })?
7552 .await?;
7553 Ok(proto::ProjectEntryResponse {
7554 entry: entry.as_ref().map(|e| e.into()),
7555 worktree_scan_id: worktree_scan_id as u64,
7556 })
7557 }
7558
7559 async fn handle_copy_project_entry(
7560 this: Model<Self>,
7561 envelope: TypedEnvelope<proto::CopyProjectEntry>,
7562 _: Arc<Client>,
7563 mut cx: AsyncAppContext,
7564 ) -> Result<proto::ProjectEntryResponse> {
7565 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
7566 let worktree = this.update(&mut cx, |this, cx| {
7567 this.worktree_for_entry(entry_id, cx)
7568 .ok_or_else(|| anyhow!("worktree not found"))
7569 })??;
7570 let worktree_scan_id = worktree.update(&mut cx, |worktree, _| worktree.scan_id())?;
7571 let entry = worktree
7572 .update(&mut cx, |worktree, cx| {
7573 let new_path = PathBuf::from(envelope.payload.new_path);
7574 worktree
7575 .as_local_mut()
7576 .unwrap()
7577 .copy_entry(entry_id, new_path, cx)
7578 })?
7579 .await?;
7580 Ok(proto::ProjectEntryResponse {
7581 entry: entry.as_ref().map(|e| e.into()),
7582 worktree_scan_id: worktree_scan_id as u64,
7583 })
7584 }
7585
7586 async fn handle_delete_project_entry(
7587 this: Model<Self>,
7588 envelope: TypedEnvelope<proto::DeleteProjectEntry>,
7589 _: Arc<Client>,
7590 mut cx: AsyncAppContext,
7591 ) -> Result<proto::ProjectEntryResponse> {
7592 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
7593
7594 this.update(&mut cx, |_, cx| cx.emit(Event::DeletedEntry(entry_id)))?;
7595
7596 let worktree = this.update(&mut cx, |this, cx| {
7597 this.worktree_for_entry(entry_id, cx)
7598 .ok_or_else(|| anyhow!("worktree not found"))
7599 })??;
7600 let worktree_scan_id = worktree.update(&mut cx, |worktree, _| worktree.scan_id())?;
7601 worktree
7602 .update(&mut cx, |worktree, cx| {
7603 worktree
7604 .as_local_mut()
7605 .unwrap()
7606 .delete_entry(entry_id, cx)
7607 .ok_or_else(|| anyhow!("invalid entry"))
7608 })??
7609 .await?;
7610 Ok(proto::ProjectEntryResponse {
7611 entry: None,
7612 worktree_scan_id: worktree_scan_id as u64,
7613 })
7614 }
7615
7616 async fn handle_expand_project_entry(
7617 this: Model<Self>,
7618 envelope: TypedEnvelope<proto::ExpandProjectEntry>,
7619 _: Arc<Client>,
7620 mut cx: AsyncAppContext,
7621 ) -> Result<proto::ExpandProjectEntryResponse> {
7622 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
7623 let worktree = this
7624 .update(&mut cx, |this, cx| this.worktree_for_entry(entry_id, cx))?
7625 .ok_or_else(|| anyhow!("invalid request"))?;
7626 worktree
7627 .update(&mut cx, |worktree, cx| {
7628 worktree
7629 .as_local_mut()
7630 .unwrap()
7631 .expand_entry(entry_id, cx)
7632 .ok_or_else(|| anyhow!("invalid entry"))
7633 })??
7634 .await?;
7635 let worktree_scan_id = worktree.update(&mut cx, |worktree, _| worktree.scan_id())? as u64;
7636 Ok(proto::ExpandProjectEntryResponse { worktree_scan_id })
7637 }
7638
7639 async fn handle_update_diagnostic_summary(
7640 this: Model<Self>,
7641 envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
7642 _: Arc<Client>,
7643 mut cx: AsyncAppContext,
7644 ) -> Result<()> {
7645 this.update(&mut cx, |this, cx| {
7646 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
7647 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
7648 if let Some(summary) = envelope.payload.summary {
7649 let project_path = ProjectPath {
7650 worktree_id,
7651 path: Path::new(&summary.path).into(),
7652 };
7653 worktree.update(cx, |worktree, _| {
7654 worktree
7655 .as_remote_mut()
7656 .unwrap()
7657 .update_diagnostic_summary(project_path.path.clone(), &summary);
7658 });
7659 cx.emit(Event::DiagnosticsUpdated {
7660 language_server_id: LanguageServerId(summary.language_server_id as usize),
7661 path: project_path,
7662 });
7663 }
7664 }
7665 Ok(())
7666 })?
7667 }
7668
7669 async fn handle_start_language_server(
7670 this: Model<Self>,
7671 envelope: TypedEnvelope<proto::StartLanguageServer>,
7672 _: Arc<Client>,
7673 mut cx: AsyncAppContext,
7674 ) -> Result<()> {
7675 let server = envelope
7676 .payload
7677 .server
7678 .ok_or_else(|| anyhow!("invalid server"))?;
7679 this.update(&mut cx, |this, cx| {
7680 this.language_server_statuses.insert(
7681 LanguageServerId(server.id as usize),
7682 LanguageServerStatus {
7683 name: server.name,
7684 pending_work: Default::default(),
7685 has_pending_diagnostic_updates: false,
7686 progress_tokens: Default::default(),
7687 },
7688 );
7689 cx.notify();
7690 })?;
7691 Ok(())
7692 }
7693
7694 async fn handle_update_language_server(
7695 this: Model<Self>,
7696 envelope: TypedEnvelope<proto::UpdateLanguageServer>,
7697 _: Arc<Client>,
7698 mut cx: AsyncAppContext,
7699 ) -> Result<()> {
7700 this.update(&mut cx, |this, cx| {
7701 let language_server_id = LanguageServerId(envelope.payload.language_server_id as usize);
7702
7703 match envelope
7704 .payload
7705 .variant
7706 .ok_or_else(|| anyhow!("invalid variant"))?
7707 {
7708 proto::update_language_server::Variant::WorkStart(payload) => {
7709 this.on_lsp_work_start(
7710 language_server_id,
7711 payload.token,
7712 LanguageServerProgress {
7713 message: payload.message,
7714 percentage: payload.percentage.map(|p| p as usize),
7715 last_update_at: Instant::now(),
7716 },
7717 cx,
7718 );
7719 }
7720
7721 proto::update_language_server::Variant::WorkProgress(payload) => {
7722 this.on_lsp_work_progress(
7723 language_server_id,
7724 payload.token,
7725 LanguageServerProgress {
7726 message: payload.message,
7727 percentage: payload.percentage.map(|p| p as usize),
7728 last_update_at: Instant::now(),
7729 },
7730 cx,
7731 );
7732 }
7733
7734 proto::update_language_server::Variant::WorkEnd(payload) => {
7735 this.on_lsp_work_end(language_server_id, payload.token, cx);
7736 }
7737
7738 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(_) => {
7739 this.disk_based_diagnostics_started(language_server_id, cx);
7740 }
7741
7742 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(_) => {
7743 this.disk_based_diagnostics_finished(language_server_id, cx)
7744 }
7745 }
7746
7747 Ok(())
7748 })?
7749 }
7750
7751 async fn handle_update_buffer(
7752 this: Model<Self>,
7753 envelope: TypedEnvelope<proto::UpdateBuffer>,
7754 _: Arc<Client>,
7755 mut cx: AsyncAppContext,
7756 ) -> Result<proto::Ack> {
7757 this.update(&mut cx, |this, cx| {
7758 let payload = envelope.payload.clone();
7759 let buffer_id = BufferId::new(payload.buffer_id)?;
7760 let ops = payload
7761 .operations
7762 .into_iter()
7763 .map(language::proto::deserialize_operation)
7764 .collect::<Result<Vec<_>, _>>()?;
7765 let is_remote = this.is_remote();
7766 match this.opened_buffers.entry(buffer_id) {
7767 hash_map::Entry::Occupied(mut e) => match e.get_mut() {
7768 OpenBuffer::Strong(buffer) => {
7769 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
7770 }
7771 OpenBuffer::Operations(operations) => operations.extend_from_slice(&ops),
7772 OpenBuffer::Weak(_) => {}
7773 },
7774 hash_map::Entry::Vacant(e) => {
7775 assert!(
7776 is_remote,
7777 "received buffer update from {:?}",
7778 envelope.original_sender_id
7779 );
7780 e.insert(OpenBuffer::Operations(ops));
7781 }
7782 }
7783 Ok(proto::Ack {})
7784 })?
7785 }
7786
7787 async fn handle_create_buffer_for_peer(
7788 this: Model<Self>,
7789 envelope: TypedEnvelope<proto::CreateBufferForPeer>,
7790 _: Arc<Client>,
7791 mut cx: AsyncAppContext,
7792 ) -> Result<()> {
7793 this.update(&mut cx, |this, cx| {
7794 match envelope
7795 .payload
7796 .variant
7797 .ok_or_else(|| anyhow!("missing variant"))?
7798 {
7799 proto::create_buffer_for_peer::Variant::State(mut state) => {
7800 let buffer_id = BufferId::new(state.id)?;
7801
7802 let buffer_result = maybe!({
7803 let mut buffer_file = None;
7804 if let Some(file) = state.file.take() {
7805 let worktree_id = WorktreeId::from_proto(file.worktree_id);
7806 let worktree =
7807 this.worktree_for_id(worktree_id, cx).ok_or_else(|| {
7808 anyhow!("no worktree found for id {}", file.worktree_id)
7809 })?;
7810 buffer_file =
7811 Some(Arc::new(File::from_proto(file, worktree.clone(), cx)?)
7812 as Arc<dyn language::File>);
7813 }
7814 Buffer::from_proto(this.replica_id(), this.capability(), state, buffer_file)
7815 });
7816
7817 match buffer_result {
7818 Ok(buffer) => {
7819 let buffer = cx.new_model(|_| buffer);
7820 this.incomplete_remote_buffers.insert(buffer_id, buffer);
7821 }
7822 Err(error) => {
7823 if let Some(listeners) = this.loading_buffers.remove(&buffer_id) {
7824 for listener in listeners {
7825 listener.send(Err(anyhow!(error.cloned()))).ok();
7826 }
7827 }
7828 }
7829 };
7830 }
7831 proto::create_buffer_for_peer::Variant::Chunk(chunk) => {
7832 let buffer_id = BufferId::new(chunk.buffer_id)?;
7833 let buffer = this
7834 .incomplete_remote_buffers
7835 .get(&buffer_id)
7836 .cloned()
7837 .ok_or_else(|| {
7838 anyhow!(
7839 "received chunk for buffer {} without initial state",
7840 chunk.buffer_id
7841 )
7842 })?;
7843
7844 let result = maybe!({
7845 let operations = chunk
7846 .operations
7847 .into_iter()
7848 .map(language::proto::deserialize_operation)
7849 .collect::<Result<Vec<_>>>()?;
7850 buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))
7851 });
7852
7853 if let Err(error) = result {
7854 this.incomplete_remote_buffers.remove(&buffer_id);
7855 if let Some(listeners) = this.loading_buffers.remove(&buffer_id) {
7856 for listener in listeners {
7857 listener.send(Err(error.cloned())).ok();
7858 }
7859 }
7860 } else {
7861 if chunk.is_last {
7862 this.incomplete_remote_buffers.remove(&buffer_id);
7863 this.register_buffer(&buffer, cx)?;
7864 }
7865 }
7866 }
7867 }
7868
7869 Ok(())
7870 })?
7871 }
7872
7873 async fn handle_update_diff_base(
7874 this: Model<Self>,
7875 envelope: TypedEnvelope<proto::UpdateDiffBase>,
7876 _: Arc<Client>,
7877 mut cx: AsyncAppContext,
7878 ) -> Result<()> {
7879 this.update(&mut cx, |this, cx| {
7880 let buffer_id = envelope.payload.buffer_id;
7881 let buffer_id = BufferId::new(buffer_id)?;
7882 let diff_base = envelope.payload.diff_base;
7883 if let Some(buffer) = this
7884 .opened_buffers
7885 .get_mut(&buffer_id)
7886 .and_then(|b| b.upgrade())
7887 .or_else(|| this.incomplete_remote_buffers.get(&buffer_id).cloned())
7888 {
7889 buffer.update(cx, |buffer, cx| buffer.set_diff_base(diff_base, cx));
7890 }
7891 Ok(())
7892 })?
7893 }
7894
7895 async fn handle_update_buffer_file(
7896 this: Model<Self>,
7897 envelope: TypedEnvelope<proto::UpdateBufferFile>,
7898 _: Arc<Client>,
7899 mut cx: AsyncAppContext,
7900 ) -> Result<()> {
7901 let buffer_id = envelope.payload.buffer_id;
7902 let buffer_id = BufferId::new(buffer_id)?;
7903
7904 this.update(&mut cx, |this, cx| {
7905 let payload = envelope.payload.clone();
7906 if let Some(buffer) = this
7907 .opened_buffers
7908 .get(&buffer_id)
7909 .and_then(|b| b.upgrade())
7910 .or_else(|| this.incomplete_remote_buffers.get(&buffer_id).cloned())
7911 {
7912 let file = payload.file.ok_or_else(|| anyhow!("invalid file"))?;
7913 let worktree = this
7914 .worktree_for_id(WorktreeId::from_proto(file.worktree_id), cx)
7915 .ok_or_else(|| anyhow!("no such worktree"))?;
7916 let file = File::from_proto(file, worktree, cx)?;
7917 buffer.update(cx, |buffer, cx| {
7918 buffer.file_updated(Arc::new(file), cx);
7919 });
7920 this.detect_language_for_buffer(&buffer, cx);
7921 }
7922 Ok(())
7923 })?
7924 }
7925
7926 async fn handle_save_buffer(
7927 this: Model<Self>,
7928 envelope: TypedEnvelope<proto::SaveBuffer>,
7929 _: Arc<Client>,
7930 mut cx: AsyncAppContext,
7931 ) -> Result<proto::BufferSaved> {
7932 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
7933 let (project_id, buffer) = this.update(&mut cx, |this, _cx| {
7934 let project_id = this.remote_id().ok_or_else(|| anyhow!("not connected"))?;
7935 let buffer = this
7936 .opened_buffers
7937 .get(&buffer_id)
7938 .and_then(|buffer| buffer.upgrade())
7939 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
7940 anyhow::Ok((project_id, buffer))
7941 })??;
7942 buffer
7943 .update(&mut cx, |buffer, _| {
7944 buffer.wait_for_version(deserialize_version(&envelope.payload.version))
7945 })?
7946 .await?;
7947 let buffer_id = buffer.update(&mut cx, |buffer, _| buffer.remote_id())?;
7948
7949 this.update(&mut cx, |this, cx| this.save_buffer(buffer.clone(), cx))?
7950 .await?;
7951 buffer.update(&mut cx, |buffer, _| proto::BufferSaved {
7952 project_id,
7953 buffer_id: buffer_id.into(),
7954 version: serialize_version(buffer.saved_version()),
7955 mtime: buffer.saved_mtime().map(|time| time.into()),
7956 fingerprint: language::proto::serialize_fingerprint(buffer.saved_version_fingerprint()),
7957 })
7958 }
7959
7960 async fn handle_reload_buffers(
7961 this: Model<Self>,
7962 envelope: TypedEnvelope<proto::ReloadBuffers>,
7963 _: Arc<Client>,
7964 mut cx: AsyncAppContext,
7965 ) -> Result<proto::ReloadBuffersResponse> {
7966 let sender_id = envelope.original_sender_id()?;
7967 let reload = this.update(&mut cx, |this, cx| {
7968 let mut buffers = HashSet::default();
7969 for buffer_id in &envelope.payload.buffer_ids {
7970 let buffer_id = BufferId::new(*buffer_id)?;
7971 buffers.insert(
7972 this.opened_buffers
7973 .get(&buffer_id)
7974 .and_then(|buffer| buffer.upgrade())
7975 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
7976 );
7977 }
7978 Ok::<_, anyhow::Error>(this.reload_buffers(buffers, false, cx))
7979 })??;
7980
7981 let project_transaction = reload.await?;
7982 let project_transaction = this.update(&mut cx, |this, cx| {
7983 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
7984 })?;
7985 Ok(proto::ReloadBuffersResponse {
7986 transaction: Some(project_transaction),
7987 })
7988 }
7989
7990 async fn handle_synchronize_buffers(
7991 this: Model<Self>,
7992 envelope: TypedEnvelope<proto::SynchronizeBuffers>,
7993 _: Arc<Client>,
7994 mut cx: AsyncAppContext,
7995 ) -> Result<proto::SynchronizeBuffersResponse> {
7996 let project_id = envelope.payload.project_id;
7997 let mut response = proto::SynchronizeBuffersResponse {
7998 buffers: Default::default(),
7999 };
8000
8001 this.update(&mut cx, |this, cx| {
8002 let Some(guest_id) = envelope.original_sender_id else {
8003 error!("missing original_sender_id on SynchronizeBuffers request");
8004 bail!("missing original_sender_id on SynchronizeBuffers request");
8005 };
8006
8007 this.shared_buffers.entry(guest_id).or_default().clear();
8008 for buffer in envelope.payload.buffers {
8009 let buffer_id = BufferId::new(buffer.id)?;
8010 let remote_version = language::proto::deserialize_version(&buffer.version);
8011 if let Some(buffer) = this.buffer_for_id(buffer_id) {
8012 this.shared_buffers
8013 .entry(guest_id)
8014 .or_default()
8015 .insert(buffer_id);
8016
8017 let buffer = buffer.read(cx);
8018 response.buffers.push(proto::BufferVersion {
8019 id: buffer_id.into(),
8020 version: language::proto::serialize_version(&buffer.version),
8021 });
8022
8023 let operations = buffer.serialize_ops(Some(remote_version), cx);
8024 let client = this.client.clone();
8025 if let Some(file) = buffer.file() {
8026 client
8027 .send(proto::UpdateBufferFile {
8028 project_id,
8029 buffer_id: buffer_id.into(),
8030 file: Some(file.to_proto()),
8031 })
8032 .log_err();
8033 }
8034
8035 client
8036 .send(proto::UpdateDiffBase {
8037 project_id,
8038 buffer_id: buffer_id.into(),
8039 diff_base: buffer.diff_base().map(Into::into),
8040 })
8041 .log_err();
8042
8043 client
8044 .send(proto::BufferReloaded {
8045 project_id,
8046 buffer_id: buffer_id.into(),
8047 version: language::proto::serialize_version(buffer.saved_version()),
8048 mtime: buffer.saved_mtime().map(|time| time.into()),
8049 fingerprint: language::proto::serialize_fingerprint(
8050 buffer.saved_version_fingerprint(),
8051 ),
8052 line_ending: language::proto::serialize_line_ending(
8053 buffer.line_ending(),
8054 ) as i32,
8055 })
8056 .log_err();
8057
8058 cx.background_executor()
8059 .spawn(
8060 async move {
8061 let operations = operations.await;
8062 for chunk in split_operations(operations) {
8063 client
8064 .request(proto::UpdateBuffer {
8065 project_id,
8066 buffer_id: buffer_id.into(),
8067 operations: chunk,
8068 })
8069 .await?;
8070 }
8071 anyhow::Ok(())
8072 }
8073 .log_err(),
8074 )
8075 .detach();
8076 }
8077 }
8078 Ok(())
8079 })??;
8080
8081 Ok(response)
8082 }
8083
8084 async fn handle_format_buffers(
8085 this: Model<Self>,
8086 envelope: TypedEnvelope<proto::FormatBuffers>,
8087 _: Arc<Client>,
8088 mut cx: AsyncAppContext,
8089 ) -> Result<proto::FormatBuffersResponse> {
8090 let sender_id = envelope.original_sender_id()?;
8091 let format = this.update(&mut cx, |this, cx| {
8092 let mut buffers = HashSet::default();
8093 for buffer_id in &envelope.payload.buffer_ids {
8094 let buffer_id = BufferId::new(*buffer_id)?;
8095 buffers.insert(
8096 this.opened_buffers
8097 .get(&buffer_id)
8098 .and_then(|buffer| buffer.upgrade())
8099 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
8100 );
8101 }
8102 let trigger = FormatTrigger::from_proto(envelope.payload.trigger);
8103 Ok::<_, anyhow::Error>(this.format(buffers, false, trigger, cx))
8104 })??;
8105
8106 let project_transaction = format.await?;
8107 let project_transaction = this.update(&mut cx, |this, cx| {
8108 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
8109 })?;
8110 Ok(proto::FormatBuffersResponse {
8111 transaction: Some(project_transaction),
8112 })
8113 }
8114
8115 async fn handle_apply_additional_edits_for_completion(
8116 this: Model<Self>,
8117 envelope: TypedEnvelope<proto::ApplyCompletionAdditionalEdits>,
8118 _: Arc<Client>,
8119 mut cx: AsyncAppContext,
8120 ) -> Result<proto::ApplyCompletionAdditionalEditsResponse> {
8121 let languages = this.update(&mut cx, |this, _| this.languages.clone())?;
8122 let (buffer, completion) = this.update(&mut cx, |this, cx| {
8123 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
8124 let buffer = this
8125 .opened_buffers
8126 .get(&buffer_id)
8127 .and_then(|buffer| buffer.upgrade())
8128 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
8129 let language = buffer.read(cx).language();
8130 let completion = language::proto::deserialize_completion(
8131 envelope
8132 .payload
8133 .completion
8134 .ok_or_else(|| anyhow!("invalid completion"))?,
8135 language.cloned(),
8136 &languages,
8137 );
8138 Ok::<_, anyhow::Error>((buffer, completion))
8139 })??;
8140
8141 let completion = completion.await?;
8142
8143 let apply_additional_edits = this.update(&mut cx, |this, cx| {
8144 this.apply_additional_edits_for_completion(buffer, completion, false, cx)
8145 })?;
8146
8147 Ok(proto::ApplyCompletionAdditionalEditsResponse {
8148 transaction: apply_additional_edits
8149 .await?
8150 .as_ref()
8151 .map(language::proto::serialize_transaction),
8152 })
8153 }
8154
8155 async fn handle_resolve_completion_documentation(
8156 this: Model<Self>,
8157 envelope: TypedEnvelope<proto::ResolveCompletionDocumentation>,
8158 _: Arc<Client>,
8159 mut cx: AsyncAppContext,
8160 ) -> Result<proto::ResolveCompletionDocumentationResponse> {
8161 let lsp_completion = serde_json::from_slice(&envelope.payload.lsp_completion)?;
8162
8163 let completion = this
8164 .read_with(&mut cx, |this, _| {
8165 let id = LanguageServerId(envelope.payload.language_server_id as usize);
8166 let Some(server) = this.language_server_for_id(id) else {
8167 return Err(anyhow!("No language server {id}"));
8168 };
8169
8170 Ok(server.request::<lsp::request::ResolveCompletionItem>(lsp_completion))
8171 })??
8172 .await?;
8173
8174 let mut is_markdown = false;
8175 let text = match completion.documentation {
8176 Some(lsp::Documentation::String(text)) => text,
8177
8178 Some(lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value })) => {
8179 is_markdown = kind == lsp::MarkupKind::Markdown;
8180 value
8181 }
8182
8183 _ => String::new(),
8184 };
8185
8186 Ok(proto::ResolveCompletionDocumentationResponse { text, is_markdown })
8187 }
8188
8189 async fn handle_apply_code_action(
8190 this: Model<Self>,
8191 envelope: TypedEnvelope<proto::ApplyCodeAction>,
8192 _: Arc<Client>,
8193 mut cx: AsyncAppContext,
8194 ) -> Result<proto::ApplyCodeActionResponse> {
8195 let sender_id = envelope.original_sender_id()?;
8196 let action = language::proto::deserialize_code_action(
8197 envelope
8198 .payload
8199 .action
8200 .ok_or_else(|| anyhow!("invalid action"))?,
8201 )?;
8202 let apply_code_action = this.update(&mut cx, |this, cx| {
8203 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
8204 let buffer = this
8205 .opened_buffers
8206 .get(&buffer_id)
8207 .and_then(|buffer| buffer.upgrade())
8208 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
8209 Ok::<_, anyhow::Error>(this.apply_code_action(buffer, action, false, cx))
8210 })??;
8211
8212 let project_transaction = apply_code_action.await?;
8213 let project_transaction = this.update(&mut cx, |this, cx| {
8214 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
8215 })?;
8216 Ok(proto::ApplyCodeActionResponse {
8217 transaction: Some(project_transaction),
8218 })
8219 }
8220
8221 async fn handle_on_type_formatting(
8222 this: Model<Self>,
8223 envelope: TypedEnvelope<proto::OnTypeFormatting>,
8224 _: Arc<Client>,
8225 mut cx: AsyncAppContext,
8226 ) -> Result<proto::OnTypeFormattingResponse> {
8227 let on_type_formatting = this.update(&mut cx, |this, cx| {
8228 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
8229 let buffer = this
8230 .opened_buffers
8231 .get(&buffer_id)
8232 .and_then(|buffer| buffer.upgrade())
8233 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
8234 let position = envelope
8235 .payload
8236 .position
8237 .and_then(deserialize_anchor)
8238 .ok_or_else(|| anyhow!("invalid position"))?;
8239 Ok::<_, anyhow::Error>(this.apply_on_type_formatting(
8240 buffer,
8241 position,
8242 envelope.payload.trigger.clone(),
8243 cx,
8244 ))
8245 })??;
8246
8247 let transaction = on_type_formatting
8248 .await?
8249 .as_ref()
8250 .map(language::proto::serialize_transaction);
8251 Ok(proto::OnTypeFormattingResponse { transaction })
8252 }
8253
8254 async fn handle_inlay_hints(
8255 this: Model<Self>,
8256 envelope: TypedEnvelope<proto::InlayHints>,
8257 _: Arc<Client>,
8258 mut cx: AsyncAppContext,
8259 ) -> Result<proto::InlayHintsResponse> {
8260 let sender_id = envelope.original_sender_id()?;
8261 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
8262 let buffer = this.update(&mut cx, |this, _| {
8263 this.opened_buffers
8264 .get(&buffer_id)
8265 .and_then(|buffer| buffer.upgrade())
8266 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
8267 })??;
8268 buffer
8269 .update(&mut cx, |buffer, _| {
8270 buffer.wait_for_version(deserialize_version(&envelope.payload.version))
8271 })?
8272 .await
8273 .with_context(|| format!("waiting for version for buffer {}", buffer.entity_id()))?;
8274
8275 let start = envelope
8276 .payload
8277 .start
8278 .and_then(deserialize_anchor)
8279 .context("missing range start")?;
8280 let end = envelope
8281 .payload
8282 .end
8283 .and_then(deserialize_anchor)
8284 .context("missing range end")?;
8285 let buffer_hints = this
8286 .update(&mut cx, |project, cx| {
8287 project.inlay_hints(buffer.clone(), start..end, cx)
8288 })?
8289 .await
8290 .context("inlay hints fetch")?;
8291
8292 this.update(&mut cx, |project, cx| {
8293 InlayHints::response_to_proto(
8294 buffer_hints,
8295 project,
8296 sender_id,
8297 &buffer.read(cx).version(),
8298 cx,
8299 )
8300 })
8301 }
8302
8303 async fn handle_resolve_inlay_hint(
8304 this: Model<Self>,
8305 envelope: TypedEnvelope<proto::ResolveInlayHint>,
8306 _: Arc<Client>,
8307 mut cx: AsyncAppContext,
8308 ) -> Result<proto::ResolveInlayHintResponse> {
8309 let proto_hint = envelope
8310 .payload
8311 .hint
8312 .expect("incorrect protobuf resolve inlay hint message: missing the inlay hint");
8313 let hint = InlayHints::proto_to_project_hint(proto_hint)
8314 .context("resolved proto inlay hint conversion")?;
8315 let buffer = this.update(&mut cx, |this, _cx| {
8316 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
8317 this.opened_buffers
8318 .get(&buffer_id)
8319 .and_then(|buffer| buffer.upgrade())
8320 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))
8321 })??;
8322 let response_hint = this
8323 .update(&mut cx, |project, cx| {
8324 project.resolve_inlay_hint(
8325 hint,
8326 buffer,
8327 LanguageServerId(envelope.payload.language_server_id as usize),
8328 cx,
8329 )
8330 })?
8331 .await
8332 .context("inlay hints fetch")?;
8333 Ok(proto::ResolveInlayHintResponse {
8334 hint: Some(InlayHints::project_to_proto_hint(response_hint)),
8335 })
8336 }
8337
8338 async fn try_resolve_code_action(
8339 lang_server: &LanguageServer,
8340 action: &mut CodeAction,
8341 ) -> anyhow::Result<()> {
8342 if GetCodeActions::can_resolve_actions(&lang_server.capabilities()) {
8343 if action.lsp_action.data.is_some()
8344 && (action.lsp_action.command.is_none() || action.lsp_action.edit.is_none())
8345 {
8346 action.lsp_action = lang_server
8347 .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action.clone())
8348 .await?;
8349 }
8350 }
8351
8352 anyhow::Ok(())
8353 }
8354
8355 async fn handle_refresh_inlay_hints(
8356 this: Model<Self>,
8357 _: TypedEnvelope<proto::RefreshInlayHints>,
8358 _: Arc<Client>,
8359 mut cx: AsyncAppContext,
8360 ) -> Result<proto::Ack> {
8361 this.update(&mut cx, |_, cx| {
8362 cx.emit(Event::RefreshInlayHints);
8363 })?;
8364 Ok(proto::Ack {})
8365 }
8366
8367 async fn handle_lsp_command<T: LspCommand>(
8368 this: Model<Self>,
8369 envelope: TypedEnvelope<T::ProtoRequest>,
8370 _: Arc<Client>,
8371 mut cx: AsyncAppContext,
8372 ) -> Result<<T::ProtoRequest as proto::RequestMessage>::Response>
8373 where
8374 <T::LspRequest as lsp::request::Request>::Params: Send,
8375 <T::LspRequest as lsp::request::Request>::Result: Send,
8376 {
8377 let sender_id = envelope.original_sender_id()?;
8378 let buffer_id = T::buffer_id_from_proto(&envelope.payload)?;
8379 let buffer_handle = this.update(&mut cx, |this, _cx| {
8380 this.opened_buffers
8381 .get(&buffer_id)
8382 .and_then(|buffer| buffer.upgrade())
8383 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))
8384 })??;
8385 let request = T::from_proto(
8386 envelope.payload,
8387 this.clone(),
8388 buffer_handle.clone(),
8389 cx.clone(),
8390 )
8391 .await?;
8392 let response = this
8393 .update(&mut cx, |this, cx| {
8394 this.request_lsp(
8395 buffer_handle.clone(),
8396 LanguageServerToQuery::Primary,
8397 request,
8398 cx,
8399 )
8400 })?
8401 .await?;
8402 this.update(&mut cx, |this, cx| {
8403 Ok(T::response_to_proto(
8404 response,
8405 this,
8406 sender_id,
8407 &buffer_handle.read(cx).version(),
8408 cx,
8409 ))
8410 })?
8411 }
8412
8413 async fn handle_get_project_symbols(
8414 this: Model<Self>,
8415 envelope: TypedEnvelope<proto::GetProjectSymbols>,
8416 _: Arc<Client>,
8417 mut cx: AsyncAppContext,
8418 ) -> Result<proto::GetProjectSymbolsResponse> {
8419 let symbols = this
8420 .update(&mut cx, |this, cx| {
8421 this.symbols(&envelope.payload.query, cx)
8422 })?
8423 .await?;
8424
8425 Ok(proto::GetProjectSymbolsResponse {
8426 symbols: symbols.iter().map(serialize_symbol).collect(),
8427 })
8428 }
8429
8430 async fn handle_search_project(
8431 this: Model<Self>,
8432 envelope: TypedEnvelope<proto::SearchProject>,
8433 _: Arc<Client>,
8434 mut cx: AsyncAppContext,
8435 ) -> Result<proto::SearchProjectResponse> {
8436 let peer_id = envelope.original_sender_id()?;
8437 let query = SearchQuery::from_proto(envelope.payload)?;
8438 let mut result = this.update(&mut cx, |this, cx| this.search(query, cx))?;
8439
8440 cx.spawn(move |mut cx| async move {
8441 let mut locations = Vec::new();
8442 let mut limit_reached = false;
8443 while let Some(result) = result.next().await {
8444 match result {
8445 SearchResult::Buffer { buffer, ranges } => {
8446 for range in ranges {
8447 let start = serialize_anchor(&range.start);
8448 let end = serialize_anchor(&range.end);
8449 let buffer_id = this.update(&mut cx, |this, cx| {
8450 this.create_buffer_for_peer(&buffer, peer_id, cx).into()
8451 })?;
8452 locations.push(proto::Location {
8453 buffer_id,
8454 start: Some(start),
8455 end: Some(end),
8456 });
8457 }
8458 }
8459 SearchResult::LimitReached => limit_reached = true,
8460 }
8461 }
8462 Ok(proto::SearchProjectResponse {
8463 locations,
8464 limit_reached,
8465 })
8466 })
8467 .await
8468 }
8469
8470 async fn handle_open_buffer_for_symbol(
8471 this: Model<Self>,
8472 envelope: TypedEnvelope<proto::OpenBufferForSymbol>,
8473 _: Arc<Client>,
8474 mut cx: AsyncAppContext,
8475 ) -> Result<proto::OpenBufferForSymbolResponse> {
8476 let peer_id = envelope.original_sender_id()?;
8477 let symbol = envelope
8478 .payload
8479 .symbol
8480 .ok_or_else(|| anyhow!("invalid symbol"))?;
8481 let symbol = this
8482 .update(&mut cx, |this, _cx| this.deserialize_symbol(symbol))?
8483 .await?;
8484 let symbol = this.update(&mut cx, |this, _| {
8485 let signature = this.symbol_signature(&symbol.path);
8486 if signature == symbol.signature {
8487 Ok(symbol)
8488 } else {
8489 Err(anyhow!("invalid symbol signature"))
8490 }
8491 })??;
8492 let buffer = this
8493 .update(&mut cx, |this, cx| this.open_buffer_for_symbol(&symbol, cx))?
8494 .await?;
8495
8496 this.update(&mut cx, |this, cx| {
8497 let is_private = buffer
8498 .read(cx)
8499 .file()
8500 .map(|f| f.is_private())
8501 .unwrap_or_default();
8502 if is_private {
8503 Err(anyhow!(ErrorCode::UnsharedItem))
8504 } else {
8505 Ok(proto::OpenBufferForSymbolResponse {
8506 buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx).into(),
8507 })
8508 }
8509 })?
8510 }
8511
8512 fn symbol_signature(&self, project_path: &ProjectPath) -> [u8; 32] {
8513 let mut hasher = Sha256::new();
8514 hasher.update(project_path.worktree_id.to_proto().to_be_bytes());
8515 hasher.update(project_path.path.to_string_lossy().as_bytes());
8516 hasher.update(self.nonce.to_be_bytes());
8517 hasher.finalize().as_slice().try_into().unwrap()
8518 }
8519
8520 async fn handle_open_buffer_by_id(
8521 this: Model<Self>,
8522 envelope: TypedEnvelope<proto::OpenBufferById>,
8523 _: Arc<Client>,
8524 mut cx: AsyncAppContext,
8525 ) -> Result<proto::OpenBufferResponse> {
8526 let peer_id = envelope.original_sender_id()?;
8527 let buffer_id = BufferId::new(envelope.payload.id)?;
8528 let buffer = this
8529 .update(&mut cx, |this, cx| this.open_buffer_by_id(buffer_id, cx))?
8530 .await?;
8531 Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
8532 }
8533
8534 async fn handle_open_buffer_by_path(
8535 this: Model<Self>,
8536 envelope: TypedEnvelope<proto::OpenBufferByPath>,
8537 _: Arc<Client>,
8538 mut cx: AsyncAppContext,
8539 ) -> Result<proto::OpenBufferResponse> {
8540 let peer_id = envelope.original_sender_id()?;
8541 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
8542 let open_buffer = this.update(&mut cx, |this, cx| {
8543 this.open_buffer(
8544 ProjectPath {
8545 worktree_id,
8546 path: PathBuf::from(envelope.payload.path).into(),
8547 },
8548 cx,
8549 )
8550 })?;
8551
8552 let buffer = open_buffer.await?;
8553 Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
8554 }
8555
8556 fn respond_to_open_buffer_request(
8557 this: Model<Self>,
8558 buffer: Model<Buffer>,
8559 peer_id: proto::PeerId,
8560 cx: &mut AsyncAppContext,
8561 ) -> Result<proto::OpenBufferResponse> {
8562 this.update(cx, |this, cx| {
8563 let is_private = buffer
8564 .read(cx)
8565 .file()
8566 .map(|f| f.is_private())
8567 .unwrap_or_default();
8568 if is_private {
8569 Err(anyhow!(ErrorCode::UnsharedItem))
8570 } else {
8571 Ok(proto::OpenBufferResponse {
8572 buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx).into(),
8573 })
8574 }
8575 })?
8576 }
8577
8578 fn serialize_project_transaction_for_peer(
8579 &mut self,
8580 project_transaction: ProjectTransaction,
8581 peer_id: proto::PeerId,
8582 cx: &mut AppContext,
8583 ) -> proto::ProjectTransaction {
8584 let mut serialized_transaction = proto::ProjectTransaction {
8585 buffer_ids: Default::default(),
8586 transactions: Default::default(),
8587 };
8588 for (buffer, transaction) in project_transaction.0 {
8589 serialized_transaction
8590 .buffer_ids
8591 .push(self.create_buffer_for_peer(&buffer, peer_id, cx).into());
8592 serialized_transaction
8593 .transactions
8594 .push(language::proto::serialize_transaction(&transaction));
8595 }
8596 serialized_transaction
8597 }
8598
8599 fn deserialize_project_transaction(
8600 &mut self,
8601 message: proto::ProjectTransaction,
8602 push_to_history: bool,
8603 cx: &mut ModelContext<Self>,
8604 ) -> Task<Result<ProjectTransaction>> {
8605 cx.spawn(move |this, mut cx| async move {
8606 let mut project_transaction = ProjectTransaction::default();
8607 for (buffer_id, transaction) in message.buffer_ids.into_iter().zip(message.transactions)
8608 {
8609 let buffer_id = BufferId::new(buffer_id)?;
8610 let buffer = this
8611 .update(&mut cx, |this, cx| {
8612 this.wait_for_remote_buffer(buffer_id, cx)
8613 })?
8614 .await?;
8615 let transaction = language::proto::deserialize_transaction(transaction)?;
8616 project_transaction.0.insert(buffer, transaction);
8617 }
8618
8619 for (buffer, transaction) in &project_transaction.0 {
8620 buffer
8621 .update(&mut cx, |buffer, _| {
8622 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
8623 })?
8624 .await?;
8625
8626 if push_to_history {
8627 buffer.update(&mut cx, |buffer, _| {
8628 buffer.push_transaction(transaction.clone(), Instant::now());
8629 })?;
8630 }
8631 }
8632
8633 Ok(project_transaction)
8634 })
8635 }
8636
8637 fn create_buffer_for_peer(
8638 &mut self,
8639 buffer: &Model<Buffer>,
8640 peer_id: proto::PeerId,
8641 cx: &mut AppContext,
8642 ) -> BufferId {
8643 let buffer_id = buffer.read(cx).remote_id();
8644 if let ProjectClientState::Shared { updates_tx, .. } = &self.client_state {
8645 updates_tx
8646 .unbounded_send(LocalProjectUpdate::CreateBufferForPeer { peer_id, buffer_id })
8647 .ok();
8648 }
8649 buffer_id
8650 }
8651
8652 fn wait_for_remote_buffer(
8653 &mut self,
8654 id: BufferId,
8655 cx: &mut ModelContext<Self>,
8656 ) -> Task<Result<Model<Buffer>>> {
8657 let buffer = self
8658 .opened_buffers
8659 .get(&id)
8660 .and_then(|buffer| buffer.upgrade());
8661
8662 if let Some(buffer) = buffer {
8663 return Task::ready(Ok(buffer));
8664 }
8665
8666 let (tx, rx) = oneshot::channel();
8667 self.loading_buffers.entry(id).or_default().push(tx);
8668
8669 cx.background_executor().spawn(async move { rx.await? })
8670 }
8671
8672 fn synchronize_remote_buffers(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
8673 let project_id = match self.client_state {
8674 ProjectClientState::Remote {
8675 sharing_has_stopped,
8676 remote_id,
8677 ..
8678 } => {
8679 if sharing_has_stopped {
8680 return Task::ready(Err(anyhow!(
8681 "can't synchronize remote buffers on a readonly project"
8682 )));
8683 } else {
8684 remote_id
8685 }
8686 }
8687 ProjectClientState::Shared { .. } | ProjectClientState::Local => {
8688 return Task::ready(Err(anyhow!(
8689 "can't synchronize remote buffers on a local project"
8690 )))
8691 }
8692 };
8693
8694 let client = self.client.clone();
8695 cx.spawn(move |this, mut cx| async move {
8696 let (buffers, incomplete_buffer_ids) = this.update(&mut cx, |this, cx| {
8697 let buffers = this
8698 .opened_buffers
8699 .iter()
8700 .filter_map(|(id, buffer)| {
8701 let buffer = buffer.upgrade()?;
8702 Some(proto::BufferVersion {
8703 id: (*id).into(),
8704 version: language::proto::serialize_version(&buffer.read(cx).version),
8705 })
8706 })
8707 .collect();
8708 let incomplete_buffer_ids = this
8709 .incomplete_remote_buffers
8710 .keys()
8711 .copied()
8712 .collect::<Vec<_>>();
8713
8714 (buffers, incomplete_buffer_ids)
8715 })?;
8716 let response = client
8717 .request(proto::SynchronizeBuffers {
8718 project_id,
8719 buffers,
8720 })
8721 .await?;
8722
8723 let send_updates_for_buffers = this.update(&mut cx, |this, cx| {
8724 response
8725 .buffers
8726 .into_iter()
8727 .map(|buffer| {
8728 let client = client.clone();
8729 let buffer_id = match BufferId::new(buffer.id) {
8730 Ok(id) => id,
8731 Err(e) => {
8732 return Task::ready(Err(e));
8733 }
8734 };
8735 let remote_version = language::proto::deserialize_version(&buffer.version);
8736 if let Some(buffer) = this.buffer_for_id(buffer_id) {
8737 let operations =
8738 buffer.read(cx).serialize_ops(Some(remote_version), cx);
8739 cx.background_executor().spawn(async move {
8740 let operations = operations.await;
8741 for chunk in split_operations(operations) {
8742 client
8743 .request(proto::UpdateBuffer {
8744 project_id,
8745 buffer_id: buffer_id.into(),
8746 operations: chunk,
8747 })
8748 .await?;
8749 }
8750 anyhow::Ok(())
8751 })
8752 } else {
8753 Task::ready(Ok(()))
8754 }
8755 })
8756 .collect::<Vec<_>>()
8757 })?;
8758
8759 // Any incomplete buffers have open requests waiting. Request that the host sends
8760 // creates these buffers for us again to unblock any waiting futures.
8761 for id in incomplete_buffer_ids {
8762 cx.background_executor()
8763 .spawn(client.request(proto::OpenBufferById {
8764 project_id,
8765 id: id.into(),
8766 }))
8767 .detach();
8768 }
8769
8770 futures::future::join_all(send_updates_for_buffers)
8771 .await
8772 .into_iter()
8773 .collect()
8774 })
8775 }
8776
8777 pub fn worktree_metadata_protos(&self, cx: &AppContext) -> Vec<proto::WorktreeMetadata> {
8778 self.worktrees()
8779 .map(|worktree| {
8780 let worktree = worktree.read(cx);
8781 proto::WorktreeMetadata {
8782 id: worktree.id().to_proto(),
8783 root_name: worktree.root_name().into(),
8784 visible: worktree.is_visible(),
8785 abs_path: worktree.abs_path().to_string_lossy().into(),
8786 }
8787 })
8788 .collect()
8789 }
8790
8791 fn set_worktrees_from_proto(
8792 &mut self,
8793 worktrees: Vec<proto::WorktreeMetadata>,
8794 cx: &mut ModelContext<Project>,
8795 ) -> Result<()> {
8796 let replica_id = self.replica_id();
8797 let remote_id = self.remote_id().ok_or_else(|| anyhow!("invalid project"))?;
8798
8799 let mut old_worktrees_by_id = self
8800 .worktrees
8801 .drain(..)
8802 .filter_map(|worktree| {
8803 let worktree = worktree.upgrade()?;
8804 Some((worktree.read(cx).id(), worktree))
8805 })
8806 .collect::<HashMap<_, _>>();
8807
8808 for worktree in worktrees {
8809 if let Some(old_worktree) =
8810 old_worktrees_by_id.remove(&WorktreeId::from_proto(worktree.id))
8811 {
8812 self.worktrees.push(WorktreeHandle::Strong(old_worktree));
8813 } else {
8814 let worktree =
8815 Worktree::remote(remote_id, replica_id, worktree, self.client.clone(), cx);
8816 let _ = self.add_worktree(&worktree, cx);
8817 }
8818 }
8819
8820 self.metadata_changed(cx);
8821 for id in old_worktrees_by_id.keys() {
8822 cx.emit(Event::WorktreeRemoved(*id));
8823 }
8824
8825 Ok(())
8826 }
8827
8828 fn set_collaborators_from_proto(
8829 &mut self,
8830 messages: Vec<proto::Collaborator>,
8831 cx: &mut ModelContext<Self>,
8832 ) -> Result<()> {
8833 let mut collaborators = HashMap::default();
8834 for message in messages {
8835 let collaborator = Collaborator::from_proto(message)?;
8836 collaborators.insert(collaborator.peer_id, collaborator);
8837 }
8838 for old_peer_id in self.collaborators.keys() {
8839 if !collaborators.contains_key(old_peer_id) {
8840 cx.emit(Event::CollaboratorLeft(*old_peer_id));
8841 }
8842 }
8843 self.collaborators = collaborators;
8844 Ok(())
8845 }
8846
8847 fn deserialize_symbol(
8848 &self,
8849 serialized_symbol: proto::Symbol,
8850 ) -> impl Future<Output = Result<Symbol>> {
8851 let languages = self.languages.clone();
8852 let source_worktree_id = WorktreeId::from_proto(serialized_symbol.source_worktree_id);
8853 let worktree_id = WorktreeId::from_proto(serialized_symbol.worktree_id);
8854 let kind = unsafe { mem::transmute(serialized_symbol.kind) };
8855 let path = ProjectPath {
8856 worktree_id,
8857 path: PathBuf::from(serialized_symbol.path).into(),
8858 };
8859 let language = languages.language_for_file_path(&path.path);
8860
8861 async move {
8862 let language = language.await.log_err();
8863 let adapter = language
8864 .as_ref()
8865 .and_then(|language| languages.lsp_adapters(language).first().cloned());
8866 let start = serialized_symbol
8867 .start
8868 .ok_or_else(|| anyhow!("invalid start"))?;
8869 let end = serialized_symbol
8870 .end
8871 .ok_or_else(|| anyhow!("invalid end"))?;
8872 Ok(Symbol {
8873 language_server_name: LanguageServerName(
8874 serialized_symbol.language_server_name.into(),
8875 ),
8876 source_worktree_id,
8877 path,
8878 label: {
8879 match language.as_ref().zip(adapter.as_ref()) {
8880 Some((language, adapter)) => {
8881 adapter
8882 .label_for_symbol(&serialized_symbol.name, kind, language)
8883 .await
8884 }
8885 None => None,
8886 }
8887 .unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None))
8888 },
8889
8890 name: serialized_symbol.name,
8891 range: Unclipped(PointUtf16::new(start.row, start.column))
8892 ..Unclipped(PointUtf16::new(end.row, end.column)),
8893 kind,
8894 signature: serialized_symbol
8895 .signature
8896 .try_into()
8897 .map_err(|_| anyhow!("invalid signature"))?,
8898 })
8899 }
8900 }
8901
8902 async fn handle_buffer_saved(
8903 this: Model<Self>,
8904 envelope: TypedEnvelope<proto::BufferSaved>,
8905 _: Arc<Client>,
8906 mut cx: AsyncAppContext,
8907 ) -> Result<()> {
8908 let fingerprint = Default::default();
8909 let version = deserialize_version(&envelope.payload.version);
8910 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
8911 let mtime = envelope.payload.mtime.map(|time| time.into());
8912
8913 this.update(&mut cx, |this, cx| {
8914 let buffer = this
8915 .opened_buffers
8916 .get(&buffer_id)
8917 .and_then(|buffer| buffer.upgrade())
8918 .or_else(|| this.incomplete_remote_buffers.get(&buffer_id).cloned());
8919 if let Some(buffer) = buffer {
8920 buffer.update(cx, |buffer, cx| {
8921 buffer.did_save(version, fingerprint, mtime, cx);
8922 });
8923 }
8924 Ok(())
8925 })?
8926 }
8927
8928 async fn handle_buffer_reloaded(
8929 this: Model<Self>,
8930 envelope: TypedEnvelope<proto::BufferReloaded>,
8931 _: Arc<Client>,
8932 mut cx: AsyncAppContext,
8933 ) -> Result<()> {
8934 let payload = envelope.payload;
8935 let version = deserialize_version(&payload.version);
8936 let fingerprint = RopeFingerprint::default();
8937 let line_ending = deserialize_line_ending(
8938 proto::LineEnding::from_i32(payload.line_ending)
8939 .ok_or_else(|| anyhow!("missing line ending"))?,
8940 );
8941 let mtime = payload.mtime.map(|time| time.into());
8942 let buffer_id = BufferId::new(payload.buffer_id)?;
8943 this.update(&mut cx, |this, cx| {
8944 let buffer = this
8945 .opened_buffers
8946 .get(&buffer_id)
8947 .and_then(|buffer| buffer.upgrade())
8948 .or_else(|| this.incomplete_remote_buffers.get(&buffer_id).cloned());
8949 if let Some(buffer) = buffer {
8950 buffer.update(cx, |buffer, cx| {
8951 buffer.did_reload(version, fingerprint, line_ending, mtime, cx);
8952 });
8953 }
8954 Ok(())
8955 })?
8956 }
8957
8958 #[allow(clippy::type_complexity)]
8959 fn edits_from_lsp(
8960 &mut self,
8961 buffer: &Model<Buffer>,
8962 lsp_edits: impl 'static + Send + IntoIterator<Item = lsp::TextEdit>,
8963 server_id: LanguageServerId,
8964 version: Option<i32>,
8965 cx: &mut ModelContext<Self>,
8966 ) -> Task<Result<Vec<(Range<Anchor>, String)>>> {
8967 let snapshot = self.buffer_snapshot_for_lsp_version(buffer, server_id, version, cx);
8968 cx.background_executor().spawn(async move {
8969 let snapshot = snapshot?;
8970 let mut lsp_edits = lsp_edits
8971 .into_iter()
8972 .map(|edit| (range_from_lsp(edit.range), edit.new_text))
8973 .collect::<Vec<_>>();
8974 lsp_edits.sort_by_key(|(range, _)| range.start);
8975
8976 let mut lsp_edits = lsp_edits.into_iter().peekable();
8977 let mut edits = Vec::new();
8978 while let Some((range, mut new_text)) = lsp_edits.next() {
8979 // Clip invalid ranges provided by the language server.
8980 let mut range = snapshot.clip_point_utf16(range.start, Bias::Left)
8981 ..snapshot.clip_point_utf16(range.end, Bias::Left);
8982
8983 // Combine any LSP edits that are adjacent.
8984 //
8985 // Also, combine LSP edits that are separated from each other by only
8986 // a newline. This is important because for some code actions,
8987 // Rust-analyzer rewrites the entire buffer via a series of edits that
8988 // are separated by unchanged newline characters.
8989 //
8990 // In order for the diffing logic below to work properly, any edits that
8991 // cancel each other out must be combined into one.
8992 while let Some((next_range, next_text)) = lsp_edits.peek() {
8993 if next_range.start.0 > range.end {
8994 if next_range.start.0.row > range.end.row + 1
8995 || next_range.start.0.column > 0
8996 || snapshot.clip_point_utf16(
8997 Unclipped(PointUtf16::new(range.end.row, u32::MAX)),
8998 Bias::Left,
8999 ) > range.end
9000 {
9001 break;
9002 }
9003 new_text.push('\n');
9004 }
9005 range.end = snapshot.clip_point_utf16(next_range.end, Bias::Left);
9006 new_text.push_str(next_text);
9007 lsp_edits.next();
9008 }
9009
9010 // For multiline edits, perform a diff of the old and new text so that
9011 // we can identify the changes more precisely, preserving the locations
9012 // of any anchors positioned in the unchanged regions.
9013 if range.end.row > range.start.row {
9014 let mut offset = range.start.to_offset(&snapshot);
9015 let old_text = snapshot.text_for_range(range).collect::<String>();
9016
9017 let diff = TextDiff::from_lines(old_text.as_str(), &new_text);
9018 let mut moved_since_edit = true;
9019 for change in diff.iter_all_changes() {
9020 let tag = change.tag();
9021 let value = change.value();
9022 match tag {
9023 ChangeTag::Equal => {
9024 offset += value.len();
9025 moved_since_edit = true;
9026 }
9027 ChangeTag::Delete => {
9028 let start = snapshot.anchor_after(offset);
9029 let end = snapshot.anchor_before(offset + value.len());
9030 if moved_since_edit {
9031 edits.push((start..end, String::new()));
9032 } else {
9033 edits.last_mut().unwrap().0.end = end;
9034 }
9035 offset += value.len();
9036 moved_since_edit = false;
9037 }
9038 ChangeTag::Insert => {
9039 if moved_since_edit {
9040 let anchor = snapshot.anchor_after(offset);
9041 edits.push((anchor..anchor, value.to_string()));
9042 } else {
9043 edits.last_mut().unwrap().1.push_str(value);
9044 }
9045 moved_since_edit = false;
9046 }
9047 }
9048 }
9049 } else if range.end == range.start {
9050 let anchor = snapshot.anchor_after(range.start);
9051 edits.push((anchor..anchor, new_text));
9052 } else {
9053 let edit_start = snapshot.anchor_after(range.start);
9054 let edit_end = snapshot.anchor_before(range.end);
9055 edits.push((edit_start..edit_end, new_text));
9056 }
9057 }
9058
9059 Ok(edits)
9060 })
9061 }
9062
9063 fn buffer_snapshot_for_lsp_version(
9064 &mut self,
9065 buffer: &Model<Buffer>,
9066 server_id: LanguageServerId,
9067 version: Option<i32>,
9068 cx: &AppContext,
9069 ) -> Result<TextBufferSnapshot> {
9070 const OLD_VERSIONS_TO_RETAIN: i32 = 10;
9071
9072 if let Some(version) = version {
9073 let buffer_id = buffer.read(cx).remote_id();
9074 let snapshots = self
9075 .buffer_snapshots
9076 .get_mut(&buffer_id)
9077 .and_then(|m| m.get_mut(&server_id))
9078 .ok_or_else(|| {
9079 anyhow!("no snapshots found for buffer {buffer_id} and server {server_id}")
9080 })?;
9081
9082 let found_snapshot = snapshots
9083 .binary_search_by_key(&version, |e| e.version)
9084 .map(|ix| snapshots[ix].snapshot.clone())
9085 .map_err(|_| {
9086 anyhow!("snapshot not found for buffer {buffer_id} server {server_id} at version {version}")
9087 })?;
9088
9089 snapshots.retain(|snapshot| snapshot.version + OLD_VERSIONS_TO_RETAIN >= version);
9090 Ok(found_snapshot)
9091 } else {
9092 Ok((buffer.read(cx)).text_snapshot())
9093 }
9094 }
9095
9096 pub fn language_servers(
9097 &self,
9098 ) -> impl '_ + Iterator<Item = (LanguageServerId, LanguageServerName, WorktreeId)> {
9099 self.language_server_ids
9100 .iter()
9101 .map(|((worktree_id, server_name), server_id)| {
9102 (*server_id, server_name.clone(), *worktree_id)
9103 })
9104 }
9105
9106 pub fn supplementary_language_servers(
9107 &self,
9108 ) -> impl '_
9109 + Iterator<
9110 Item = (
9111 &LanguageServerId,
9112 &(LanguageServerName, Arc<LanguageServer>),
9113 ),
9114 > {
9115 self.supplementary_language_servers.iter()
9116 }
9117
9118 pub fn language_server_adapter_for_id(
9119 &self,
9120 id: LanguageServerId,
9121 ) -> Option<Arc<CachedLspAdapter>> {
9122 if let Some(LanguageServerState::Running { adapter, .. }) = self.language_servers.get(&id) {
9123 Some(adapter.clone())
9124 } else {
9125 None
9126 }
9127 }
9128
9129 pub fn language_server_for_id(&self, id: LanguageServerId) -> Option<Arc<LanguageServer>> {
9130 if let Some(LanguageServerState::Running { server, .. }) = self.language_servers.get(&id) {
9131 Some(server.clone())
9132 } else if let Some((_, server)) = self.supplementary_language_servers.get(&id) {
9133 Some(Arc::clone(server))
9134 } else {
9135 None
9136 }
9137 }
9138
9139 pub fn language_servers_for_buffer(
9140 &self,
9141 buffer: &Buffer,
9142 cx: &AppContext,
9143 ) -> impl Iterator<Item = (&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
9144 self.language_server_ids_for_buffer(buffer, cx)
9145 .into_iter()
9146 .filter_map(|server_id| match self.language_servers.get(&server_id)? {
9147 LanguageServerState::Running {
9148 adapter, server, ..
9149 } => Some((adapter, server)),
9150 _ => None,
9151 })
9152 }
9153
9154 fn primary_language_server_for_buffer(
9155 &self,
9156 buffer: &Buffer,
9157 cx: &AppContext,
9158 ) -> Option<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
9159 self.language_servers_for_buffer(buffer, cx)
9160 .find(|s| s.0.is_primary)
9161 }
9162
9163 pub fn language_server_for_buffer(
9164 &self,
9165 buffer: &Buffer,
9166 server_id: LanguageServerId,
9167 cx: &AppContext,
9168 ) -> Option<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
9169 self.language_servers_for_buffer(buffer, cx)
9170 .find(|(_, s)| s.server_id() == server_id)
9171 }
9172
9173 fn language_server_ids_for_buffer(
9174 &self,
9175 buffer: &Buffer,
9176 cx: &AppContext,
9177 ) -> Vec<LanguageServerId> {
9178 if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
9179 let worktree_id = file.worktree_id(cx);
9180 self.languages
9181 .lsp_adapters(&language)
9182 .iter()
9183 .flat_map(|adapter| {
9184 let key = (worktree_id, adapter.name.clone());
9185 self.language_server_ids.get(&key).copied()
9186 })
9187 .collect()
9188 } else {
9189 Vec::new()
9190 }
9191 }
9192}
9193
9194#[allow(clippy::too_many_arguments)]
9195async fn search_snapshots(
9196 snapshots: &Vec<LocalSnapshot>,
9197 worker_start_ix: usize,
9198 worker_end_ix: usize,
9199 query: &SearchQuery,
9200 results_tx: &Sender<SearchMatchCandidate>,
9201 opened_buffers: &HashMap<Arc<Path>, (Model<Buffer>, BufferSnapshot)>,
9202 include_root: bool,
9203 fs: &Arc<dyn Fs>,
9204) {
9205 let mut snapshot_start_ix = 0;
9206 let mut abs_path = PathBuf::new();
9207
9208 for snapshot in snapshots {
9209 let snapshot_end_ix = snapshot_start_ix
9210 + if query.include_ignored() {
9211 snapshot.file_count()
9212 } else {
9213 snapshot.visible_file_count()
9214 };
9215 if worker_end_ix <= snapshot_start_ix {
9216 break;
9217 } else if worker_start_ix > snapshot_end_ix {
9218 snapshot_start_ix = snapshot_end_ix;
9219 continue;
9220 } else {
9221 let start_in_snapshot = worker_start_ix.saturating_sub(snapshot_start_ix);
9222 let end_in_snapshot = cmp::min(worker_end_ix, snapshot_end_ix) - snapshot_start_ix;
9223
9224 for entry in snapshot
9225 .files(false, start_in_snapshot)
9226 .take(end_in_snapshot - start_in_snapshot)
9227 {
9228 if results_tx.is_closed() {
9229 break;
9230 }
9231 if opened_buffers.contains_key(&entry.path) {
9232 continue;
9233 }
9234
9235 let matched_path = if include_root {
9236 let mut full_path = PathBuf::from(snapshot.root_name());
9237 full_path.push(&entry.path);
9238 query.file_matches(Some(&full_path))
9239 } else {
9240 query.file_matches(Some(&entry.path))
9241 };
9242
9243 let matches = if matched_path {
9244 abs_path.clear();
9245 abs_path.push(&snapshot.abs_path());
9246 abs_path.push(&entry.path);
9247 if let Some(file) = fs.open_sync(&abs_path).await.log_err() {
9248 query.detect(file).unwrap_or(false)
9249 } else {
9250 false
9251 }
9252 } else {
9253 false
9254 };
9255
9256 if matches {
9257 let project_path = SearchMatchCandidate::Path {
9258 worktree_id: snapshot.id(),
9259 path: entry.path.clone(),
9260 is_ignored: entry.is_ignored,
9261 };
9262 if results_tx.send(project_path).await.is_err() {
9263 return;
9264 }
9265 }
9266 }
9267
9268 snapshot_start_ix = snapshot_end_ix;
9269 }
9270 }
9271}
9272
9273async fn search_ignored_entry(
9274 snapshot: &LocalSnapshot,
9275 ignored_entry: &Entry,
9276 fs: &Arc<dyn Fs>,
9277 query: &SearchQuery,
9278 counter_tx: &Sender<SearchMatchCandidate>,
9279) {
9280 let mut ignored_paths_to_process =
9281 VecDeque::from([snapshot.abs_path().join(&ignored_entry.path)]);
9282
9283 while let Some(ignored_abs_path) = ignored_paths_to_process.pop_front() {
9284 let metadata = fs
9285 .metadata(&ignored_abs_path)
9286 .await
9287 .with_context(|| format!("fetching fs metadata for {ignored_abs_path:?}"))
9288 .log_err()
9289 .flatten();
9290
9291 if let Some(fs_metadata) = metadata {
9292 if fs_metadata.is_dir {
9293 let files = fs
9294 .read_dir(&ignored_abs_path)
9295 .await
9296 .with_context(|| format!("listing ignored path {ignored_abs_path:?}"))
9297 .log_err();
9298
9299 if let Some(mut subfiles) = files {
9300 while let Some(subfile) = subfiles.next().await {
9301 if let Some(subfile) = subfile.log_err() {
9302 ignored_paths_to_process.push_back(subfile);
9303 }
9304 }
9305 }
9306 } else if !fs_metadata.is_symlink {
9307 if !query.file_matches(Some(&ignored_abs_path))
9308 || snapshot.is_path_excluded(ignored_entry.path.to_path_buf())
9309 {
9310 continue;
9311 }
9312 let matches = if let Some(file) = fs
9313 .open_sync(&ignored_abs_path)
9314 .await
9315 .with_context(|| format!("Opening ignored path {ignored_abs_path:?}"))
9316 .log_err()
9317 {
9318 query.detect(file).unwrap_or(false)
9319 } else {
9320 false
9321 };
9322
9323 if matches {
9324 let project_path = SearchMatchCandidate::Path {
9325 worktree_id: snapshot.id(),
9326 path: Arc::from(
9327 ignored_abs_path
9328 .strip_prefix(snapshot.abs_path())
9329 .expect("scanning worktree-related files"),
9330 ),
9331 is_ignored: true,
9332 };
9333 if counter_tx.send(project_path).await.is_err() {
9334 return;
9335 }
9336 }
9337 }
9338 }
9339 }
9340}
9341
9342fn subscribe_for_copilot_events(
9343 copilot: &Model<Copilot>,
9344 cx: &mut ModelContext<'_, Project>,
9345) -> gpui::Subscription {
9346 cx.subscribe(
9347 copilot,
9348 |project, copilot, copilot_event, cx| match copilot_event {
9349 copilot::Event::CopilotLanguageServerStarted => {
9350 match copilot.read(cx).language_server() {
9351 Some((name, copilot_server)) => {
9352 // Another event wants to re-add the server that was already added and subscribed to, avoid doing it again.
9353 if !copilot_server.has_notification_handler::<copilot::request::LogMessage>() {
9354 let new_server_id = copilot_server.server_id();
9355 let weak_project = cx.weak_model();
9356 let copilot_log_subscription = copilot_server
9357 .on_notification::<copilot::request::LogMessage, _>(
9358 move |params, mut cx| {
9359 weak_project.update(&mut cx, |_, cx| {
9360 cx.emit(Event::LanguageServerLog(
9361 new_server_id,
9362 params.message,
9363 ));
9364 }).ok();
9365 },
9366 );
9367 project.supplementary_language_servers.insert(new_server_id, (name.clone(), Arc::clone(copilot_server)));
9368 project.copilot_log_subscription = Some(copilot_log_subscription);
9369 cx.emit(Event::LanguageServerAdded(new_server_id));
9370 }
9371 }
9372 None => debug_panic!("Received Copilot language server started event, but no language server is running"),
9373 }
9374 }
9375 },
9376 )
9377}
9378
9379fn glob_literal_prefix(glob: &str) -> &str {
9380 let mut literal_end = 0;
9381 for (i, part) in glob.split(path::MAIN_SEPARATOR).enumerate() {
9382 if part.contains(&['*', '?', '{', '}']) {
9383 break;
9384 } else {
9385 if i > 0 {
9386 // Account for separator prior to this part
9387 literal_end += path::MAIN_SEPARATOR.len_utf8();
9388 }
9389 literal_end += part.len();
9390 }
9391 }
9392 &glob[..literal_end]
9393}
9394
9395impl WorktreeHandle {
9396 pub fn upgrade(&self) -> Option<Model<Worktree>> {
9397 match self {
9398 WorktreeHandle::Strong(handle) => Some(handle.clone()),
9399 WorktreeHandle::Weak(handle) => handle.upgrade(),
9400 }
9401 }
9402
9403 pub fn handle_id(&self) -> usize {
9404 match self {
9405 WorktreeHandle::Strong(handle) => handle.entity_id().as_u64() as usize,
9406 WorktreeHandle::Weak(handle) => handle.entity_id().as_u64() as usize,
9407 }
9408 }
9409}
9410
9411impl OpenBuffer {
9412 pub fn upgrade(&self) -> Option<Model<Buffer>> {
9413 match self {
9414 OpenBuffer::Strong(handle) => Some(handle.clone()),
9415 OpenBuffer::Weak(handle) => handle.upgrade(),
9416 OpenBuffer::Operations(_) => None,
9417 }
9418 }
9419}
9420
9421pub struct PathMatchCandidateSet {
9422 pub snapshot: Snapshot,
9423 pub include_ignored: bool,
9424 pub include_root_name: bool,
9425}
9426
9427impl<'a> fuzzy::PathMatchCandidateSet<'a> for PathMatchCandidateSet {
9428 type Candidates = PathMatchCandidateSetIter<'a>;
9429
9430 fn id(&self) -> usize {
9431 self.snapshot.id().to_usize()
9432 }
9433
9434 fn len(&self) -> usize {
9435 if self.include_ignored {
9436 self.snapshot.file_count()
9437 } else {
9438 self.snapshot.visible_file_count()
9439 }
9440 }
9441
9442 fn prefix(&self) -> Arc<str> {
9443 if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
9444 self.snapshot.root_name().into()
9445 } else if self.include_root_name {
9446 format!("{}/", self.snapshot.root_name()).into()
9447 } else {
9448 "".into()
9449 }
9450 }
9451
9452 fn candidates(&'a self, start: usize) -> Self::Candidates {
9453 PathMatchCandidateSetIter {
9454 traversal: self.snapshot.files(self.include_ignored, start),
9455 }
9456 }
9457}
9458
9459pub struct PathMatchCandidateSetIter<'a> {
9460 traversal: Traversal<'a>,
9461}
9462
9463impl<'a> Iterator for PathMatchCandidateSetIter<'a> {
9464 type Item = fuzzy::PathMatchCandidate<'a>;
9465
9466 fn next(&mut self) -> Option<Self::Item> {
9467 self.traversal.next().map(|entry| {
9468 if let EntryKind::File(char_bag) = entry.kind {
9469 fuzzy::PathMatchCandidate {
9470 path: &entry.path,
9471 char_bag,
9472 }
9473 } else {
9474 unreachable!()
9475 }
9476 })
9477 }
9478}
9479
9480impl EventEmitter<Event> for Project {}
9481
9482impl<'a> Into<SettingsLocation<'a>> for &'a ProjectPath {
9483 fn into(self) -> SettingsLocation<'a> {
9484 SettingsLocation {
9485 worktree_id: self.worktree_id.to_usize(),
9486 path: self.path.as_ref(),
9487 }
9488 }
9489}
9490
9491impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
9492 fn from((worktree_id, path): (WorktreeId, P)) -> Self {
9493 Self {
9494 worktree_id,
9495 path: path.as_ref().into(),
9496 }
9497 }
9498}
9499
9500struct ProjectLspAdapterDelegate {
9501 project: WeakModel<Project>,
9502 worktree: worktree::Snapshot,
9503 fs: Arc<dyn Fs>,
9504 http_client: Arc<dyn HttpClient>,
9505 language_registry: Arc<LanguageRegistry>,
9506 shell_env: Mutex<Option<HashMap<String, String>>>,
9507}
9508
9509impl ProjectLspAdapterDelegate {
9510 fn new(project: &Project, worktree: &Model<Worktree>, cx: &ModelContext<Project>) -> Arc<Self> {
9511 Arc::new(Self {
9512 project: cx.weak_model(),
9513 worktree: worktree.read(cx).snapshot(),
9514 fs: project.fs.clone(),
9515 http_client: project.client.http_client(),
9516 language_registry: project.languages.clone(),
9517 shell_env: Default::default(),
9518 })
9519 }
9520
9521 async fn load_shell_env(&self) {
9522 let worktree_abs_path = self.worktree.abs_path();
9523 let shell_env = load_shell_environment(&worktree_abs_path)
9524 .await
9525 .with_context(|| {
9526 format!("failed to determine load login shell environment in {worktree_abs_path:?}")
9527 })
9528 .log_err()
9529 .unwrap_or_default();
9530 *self.shell_env.lock() = Some(shell_env);
9531 }
9532}
9533
9534#[async_trait]
9535impl LspAdapterDelegate for ProjectLspAdapterDelegate {
9536 fn show_notification(&self, message: &str, cx: &mut AppContext) {
9537 self.project
9538 .update(cx, |_, cx| cx.emit(Event::Notification(message.to_owned())))
9539 .ok();
9540 }
9541
9542 fn http_client(&self) -> Arc<dyn HttpClient> {
9543 self.http_client.clone()
9544 }
9545
9546 async fn shell_env(&self) -> HashMap<String, String> {
9547 self.load_shell_env().await;
9548 self.shell_env.lock().as_ref().cloned().unwrap_or_default()
9549 }
9550
9551 #[cfg(not(target_os = "windows"))]
9552 async fn which(&self, command: &OsStr) -> Option<PathBuf> {
9553 let worktree_abs_path = self.worktree.abs_path();
9554 self.load_shell_env().await;
9555 let shell_path = self
9556 .shell_env
9557 .lock()
9558 .as_ref()
9559 .and_then(|shell_env| shell_env.get("PATH").cloned());
9560 which::which_in(command, shell_path.as_ref(), &worktree_abs_path).ok()
9561 }
9562
9563 #[cfg(target_os = "windows")]
9564 async fn which(&self, command: &OsStr) -> Option<PathBuf> {
9565 // todo(windows) Getting the shell env variables in a current directory on Windows is more complicated than other platforms
9566 // there isn't a 'default shell' necessarily. The closest would be the default profile on the windows terminal
9567 // SEE: https://learn.microsoft.com/en-us/windows/terminal/customize-settings/startup
9568 which::which(command).ok()
9569 }
9570
9571 fn update_status(
9572 &self,
9573 server_name: LanguageServerName,
9574 status: language::LanguageServerBinaryStatus,
9575 ) {
9576 self.language_registry
9577 .update_lsp_status(server_name, status);
9578 }
9579
9580 async fn read_text_file(&self, path: PathBuf) -> Result<String> {
9581 if self.worktree.entry_for_path(&path).is_none() {
9582 return Err(anyhow!("no such path {path:?}"));
9583 }
9584 let path = self.worktree.absolutize(path.as_ref())?;
9585 let content = self.fs.load(&path).await?;
9586 Ok(content)
9587 }
9588}
9589
9590fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
9591 proto::Symbol {
9592 language_server_name: symbol.language_server_name.0.to_string(),
9593 source_worktree_id: symbol.source_worktree_id.to_proto(),
9594 worktree_id: symbol.path.worktree_id.to_proto(),
9595 path: symbol.path.path.to_string_lossy().to_string(),
9596 name: symbol.name.clone(),
9597 kind: unsafe { mem::transmute(symbol.kind) },
9598 start: Some(proto::PointUtf16 {
9599 row: symbol.range.start.0.row,
9600 column: symbol.range.start.0.column,
9601 }),
9602 end: Some(proto::PointUtf16 {
9603 row: symbol.range.end.0.row,
9604 column: symbol.range.end.0.column,
9605 }),
9606 signature: symbol.signature.to_vec(),
9607 }
9608}
9609
9610fn relativize_path(base: &Path, path: &Path) -> PathBuf {
9611 let mut path_components = path.components();
9612 let mut base_components = base.components();
9613 let mut components: Vec<Component> = Vec::new();
9614 loop {
9615 match (path_components.next(), base_components.next()) {
9616 (None, None) => break,
9617 (Some(a), None) => {
9618 components.push(a);
9619 components.extend(path_components.by_ref());
9620 break;
9621 }
9622 (None, _) => components.push(Component::ParentDir),
9623 (Some(a), Some(b)) if components.is_empty() && a == b => (),
9624 (Some(a), Some(Component::CurDir)) => components.push(a),
9625 (Some(a), Some(_)) => {
9626 components.push(Component::ParentDir);
9627 for _ in base_components {
9628 components.push(Component::ParentDir);
9629 }
9630 components.push(a);
9631 components.extend(path_components.by_ref());
9632 break;
9633 }
9634 }
9635 }
9636 components.iter().map(|c| c.as_os_str()).collect()
9637}
9638
9639fn resolve_path(base: &Path, path: &Path) -> PathBuf {
9640 let mut result = base.to_path_buf();
9641 for component in path.components() {
9642 match component {
9643 Component::ParentDir => {
9644 result.pop();
9645 }
9646 Component::CurDir => (),
9647 _ => result.push(component),
9648 }
9649 }
9650 result
9651}
9652
9653impl Item for Buffer {
9654 fn try_open(
9655 project: &Model<Project>,
9656 path: &ProjectPath,
9657 cx: &mut AppContext,
9658 ) -> Option<Task<Result<Model<Self>>>> {
9659 Some(project.update(cx, |project, cx| project.open_buffer(path.clone(), cx)))
9660 }
9661
9662 fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
9663 File::from_dyn(self.file()).and_then(|file| file.project_entry_id(cx))
9664 }
9665
9666 fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
9667 File::from_dyn(self.file()).map(|file| ProjectPath {
9668 worktree_id: file.worktree_id(cx),
9669 path: file.path().clone(),
9670 })
9671 }
9672}
9673
9674async fn wait_for_loading_buffer(
9675 mut receiver: postage::watch::Receiver<Option<Result<Model<Buffer>, Arc<anyhow::Error>>>>,
9676) -> Result<Model<Buffer>, Arc<anyhow::Error>> {
9677 loop {
9678 if let Some(result) = receiver.borrow().as_ref() {
9679 match result {
9680 Ok(buffer) => return Ok(buffer.to_owned()),
9681 Err(e) => return Err(e.to_owned()),
9682 }
9683 }
9684 receiver.next().await;
9685 }
9686}
9687
9688fn include_text(server: &lsp::LanguageServer) -> bool {
9689 server
9690 .capabilities()
9691 .text_document_sync
9692 .as_ref()
9693 .and_then(|sync| match sync {
9694 lsp::TextDocumentSyncCapability::Kind(_) => None,
9695 lsp::TextDocumentSyncCapability::Options(options) => options.save.as_ref(),
9696 })
9697 .and_then(|save_options| match save_options {
9698 lsp::TextDocumentSyncSaveOptions::Supported(_) => None,
9699 lsp::TextDocumentSyncSaveOptions::SaveOptions(options) => options.include_text,
9700 })
9701 .unwrap_or(false)
9702}
9703
9704async fn load_shell_environment(dir: &Path) -> Result<HashMap<String, String>> {
9705 let marker = "ZED_SHELL_START";
9706 let shell = env::var("SHELL").context(
9707 "SHELL environment variable is not assigned so we can't source login environment variables",
9708 )?;
9709
9710 // What we're doing here is to spawn a shell and then `cd` into
9711 // the project directory to get the env in there as if the user
9712 // `cd`'d into it. We do that because tools like direnv, asdf, ...
9713 // hook into `cd` and only set up the env after that.
9714 //
9715 // In certain shells we need to execute additional_command in order to
9716 // trigger the behavior of direnv, etc.
9717 //
9718 //
9719 // The `exit 0` is the result of hours of debugging, trying to find out
9720 // why running this command here, without `exit 0`, would mess
9721 // up signal process for our process so that `ctrl-c` doesn't work
9722 // anymore.
9723 //
9724 // We still don't know why `$SHELL -l -i -c '/usr/bin/env -0'` would
9725 // do that, but it does, and `exit 0` helps.
9726 let additional_command = PathBuf::from(&shell)
9727 .file_name()
9728 .and_then(|f| f.to_str())
9729 .and_then(|shell| match shell {
9730 "fish" => Some("emit fish_prompt;"),
9731 _ => None,
9732 });
9733
9734 let command = format!(
9735 "cd '{}';{} echo {marker}; /usr/bin/env -0; exit 0;",
9736 dir.display(),
9737 additional_command.unwrap_or("")
9738 );
9739
9740 let output = smol::process::Command::new(&shell)
9741 .args(["-i", "-c", &command])
9742 .output()
9743 .await
9744 .context("failed to spawn login shell to source login environment variables")?;
9745
9746 anyhow::ensure!(
9747 output.status.success(),
9748 "login shell exited with error {:?}",
9749 output.status
9750 );
9751
9752 let stdout = String::from_utf8_lossy(&output.stdout);
9753 let env_output_start = stdout.find(marker).ok_or_else(|| {
9754 anyhow!(
9755 "failed to parse output of `env` command in login shell: {}",
9756 stdout
9757 )
9758 })?;
9759
9760 let mut parsed_env = HashMap::default();
9761 let env_output = &stdout[env_output_start + marker.len()..];
9762 for line in env_output.split_terminator('\0') {
9763 if let Some(separator_index) = line.find('=') {
9764 let key = line[..separator_index].to_string();
9765 let value = line[separator_index + 1..].to_string();
9766 parsed_env.insert(key, value);
9767 }
9768 }
9769 Ok(parsed_env)
9770}