1mod ignore;
2mod lsp_command;
3pub mod project_settings;
4pub mod search;
5pub mod terminals;
6pub mod worktree;
7
8#[cfg(test)]
9mod project_tests;
10#[cfg(test)]
11mod worktree_tests;
12
13use anyhow::{anyhow, Context, Result};
14use client::{proto, Client, TypedEnvelope, UserStore};
15use clock::ReplicaId;
16use collections::{hash_map, BTreeMap, HashMap, HashSet};
17use copilot::Copilot;
18use futures::{
19 channel::{
20 mpsc::{self, UnboundedReceiver},
21 oneshot,
22 },
23 future::{try_join_all, Shared},
24 stream::FuturesUnordered,
25 AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt,
26};
27use globset::{Glob, GlobSet, GlobSetBuilder};
28use gpui::{
29 AnyModelHandle, AppContext, AsyncAppContext, BorrowAppContext, Entity, ModelContext,
30 ModelHandle, Task, WeakModelHandle,
31};
32use itertools::Itertools;
33use language::{
34 language_settings::{language_settings, FormatOnSave, Formatter},
35 point_to_lsp,
36 proto::{
37 deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
38 serialize_anchor, serialize_version,
39 },
40 range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CodeAction, CodeLabel,
41 Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, Event as BufferEvent, File as _,
42 Language, LanguageRegistry, LanguageServerName, LocalFile, LspAdapterDelegate, OffsetRangeExt,
43 Operation, Patch, PendingLanguageServer, PointUtf16, TextBufferSnapshot, ToOffset,
44 ToPointUtf16, Transaction, Unclipped,
45};
46use log::error;
47use lsp::{
48 DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions,
49 DocumentHighlightKind, LanguageServer, LanguageServerBinary, LanguageServerId, OneOf,
50};
51use lsp_command::*;
52use postage::watch;
53use project_settings::ProjectSettings;
54use rand::prelude::*;
55use search::SearchQuery;
56use serde::Serialize;
57use settings::SettingsStore;
58use sha2::{Digest, Sha256};
59use similar::{ChangeTag, TextDiff};
60use std::{
61 cell::RefCell,
62 cmp::{self, Ordering},
63 convert::TryInto,
64 hash::Hash,
65 mem,
66 num::NonZeroU32,
67 ops::Range,
68 path::{self, Component, Path, PathBuf},
69 process::Stdio,
70 rc::Rc,
71 str,
72 sync::{
73 atomic::{AtomicUsize, Ordering::SeqCst},
74 Arc,
75 },
76 time::{Duration, Instant},
77};
78use terminals::Terminals;
79use util::{
80 debug_panic, defer, http::HttpClient, merge_json_value_into,
81 paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc, ResultExt, TryFutureExt as _,
82};
83
84pub use fs::*;
85pub use worktree::*;
86
87pub trait Item {
88 fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
89 fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
90}
91
92// Language server state is stored across 3 collections:
93// language_servers =>
94// a mapping from unique server id to LanguageServerState which can either be a task for a
95// server in the process of starting, or a running server with adapter and language server arcs
96// language_server_ids => a mapping from worktreeId and server name to the unique server id
97// language_server_statuses => a mapping from unique server id to the current server status
98//
99// Multiple worktrees can map to the same language server for example when you jump to the definition
100// of a file in the standard library. So language_server_ids is used to look up which server is active
101// for a given worktree and language server name
102//
103// When starting a language server, first the id map is checked to make sure a server isn't already available
104// for that worktree. If there is one, it finishes early. Otherwise, a new id is allocated and and
105// the Starting variant of LanguageServerState is stored in the language_servers map.
106pub struct Project {
107 worktrees: Vec<WorktreeHandle>,
108 active_entry: Option<ProjectEntryId>,
109 buffer_ordered_messages_tx: mpsc::UnboundedSender<BufferOrderedMessage>,
110 languages: Arc<LanguageRegistry>,
111 language_servers: HashMap<LanguageServerId, LanguageServerState>,
112 language_server_ids: HashMap<(WorktreeId, LanguageServerName), LanguageServerId>,
113 language_server_statuses: BTreeMap<LanguageServerId, LanguageServerStatus>,
114 last_workspace_edits_by_language_server: HashMap<LanguageServerId, ProjectTransaction>,
115 client: Arc<client::Client>,
116 next_entry_id: Arc<AtomicUsize>,
117 join_project_response_message_id: u32,
118 next_diagnostic_group_id: usize,
119 user_store: ModelHandle<UserStore>,
120 fs: Arc<dyn Fs>,
121 client_state: Option<ProjectClientState>,
122 collaborators: HashMap<proto::PeerId, Collaborator>,
123 client_subscriptions: Vec<client::Subscription>,
124 _subscriptions: Vec<gpui::Subscription>,
125 next_buffer_id: u64,
126 opened_buffer: (watch::Sender<()>, watch::Receiver<()>),
127 shared_buffers: HashMap<proto::PeerId, HashSet<u64>>,
128 #[allow(clippy::type_complexity)]
129 loading_buffers_by_path: HashMap<
130 ProjectPath,
131 postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
132 >,
133 #[allow(clippy::type_complexity)]
134 loading_local_worktrees:
135 HashMap<Arc<Path>, Shared<Task<Result<ModelHandle<Worktree>, Arc<anyhow::Error>>>>>,
136 opened_buffers: HashMap<u64, OpenBuffer>,
137 local_buffer_ids_by_path: HashMap<ProjectPath, u64>,
138 local_buffer_ids_by_entry_id: HashMap<ProjectEntryId, u64>,
139 /// A mapping from a buffer ID to None means that we've started waiting for an ID but haven't finished loading it.
140 /// Used for re-issuing buffer requests when peers temporarily disconnect
141 incomplete_remote_buffers: HashMap<u64, Option<ModelHandle<Buffer>>>,
142 buffer_snapshots: HashMap<u64, HashMap<LanguageServerId, Vec<LspBufferSnapshot>>>, // buffer_id -> server_id -> vec of snapshots
143 buffers_being_formatted: HashSet<u64>,
144 buffers_needing_diff: HashSet<WeakModelHandle<Buffer>>,
145 git_diff_debouncer: DelayedDebounced,
146 nonce: u128,
147 _maintain_buffer_languages: Task<()>,
148 _maintain_workspace_config: Task<()>,
149 terminals: Terminals,
150 copilot_enabled: bool,
151}
152
153struct DelayedDebounced {
154 task: Option<Task<()>>,
155 cancel_channel: Option<oneshot::Sender<()>>,
156}
157
158impl DelayedDebounced {
159 fn new() -> DelayedDebounced {
160 DelayedDebounced {
161 task: None,
162 cancel_channel: None,
163 }
164 }
165
166 fn fire_new<F>(&mut self, delay: Duration, cx: &mut ModelContext<Project>, func: F)
167 where
168 F: 'static + FnOnce(&mut Project, &mut ModelContext<Project>) -> Task<()>,
169 {
170 if let Some(channel) = self.cancel_channel.take() {
171 _ = channel.send(());
172 }
173
174 let (sender, mut receiver) = oneshot::channel::<()>();
175 self.cancel_channel = Some(sender);
176
177 let previous_task = self.task.take();
178 self.task = Some(cx.spawn(|workspace, mut cx| async move {
179 let mut timer = cx.background().timer(delay).fuse();
180 if let Some(previous_task) = previous_task {
181 previous_task.await;
182 }
183
184 futures::select_biased! {
185 _ = receiver => return,
186 _ = timer => {}
187 }
188
189 workspace
190 .update(&mut cx, |workspace, cx| (func)(workspace, cx))
191 .await;
192 }));
193 }
194}
195
196struct LspBufferSnapshot {
197 version: i32,
198 snapshot: TextBufferSnapshot,
199}
200
201/// Message ordered with respect to buffer operations
202enum BufferOrderedMessage {
203 Operation {
204 buffer_id: u64,
205 operation: proto::Operation,
206 },
207 LanguageServerUpdate {
208 language_server_id: LanguageServerId,
209 message: proto::update_language_server::Variant,
210 },
211 Resync,
212}
213
214enum LocalProjectUpdate {
215 WorktreesChanged,
216 CreateBufferForPeer {
217 peer_id: proto::PeerId,
218 buffer_id: u64,
219 },
220}
221
222enum OpenBuffer {
223 Strong(ModelHandle<Buffer>),
224 Weak(WeakModelHandle<Buffer>),
225 Operations(Vec<Operation>),
226}
227
228#[derive(Clone)]
229enum WorktreeHandle {
230 Strong(ModelHandle<Worktree>),
231 Weak(WeakModelHandle<Worktree>),
232}
233
234enum ProjectClientState {
235 Local {
236 remote_id: u64,
237 updates_tx: mpsc::UnboundedSender<LocalProjectUpdate>,
238 _send_updates: Task<()>,
239 },
240 Remote {
241 sharing_has_stopped: bool,
242 remote_id: u64,
243 replica_id: ReplicaId,
244 },
245}
246
247#[derive(Clone, Debug)]
248pub struct Collaborator {
249 pub peer_id: proto::PeerId,
250 pub replica_id: ReplicaId,
251}
252
253#[derive(Clone, Debug, PartialEq)]
254pub enum Event {
255 LanguageServerAdded(LanguageServerId),
256 LanguageServerRemoved(LanguageServerId),
257 LanguageServerLog(LanguageServerId, String),
258 Notification(String),
259 ActiveEntryChanged(Option<ProjectEntryId>),
260 WorktreeAdded,
261 WorktreeRemoved(WorktreeId),
262 DiskBasedDiagnosticsStarted {
263 language_server_id: LanguageServerId,
264 },
265 DiskBasedDiagnosticsFinished {
266 language_server_id: LanguageServerId,
267 },
268 DiagnosticsUpdated {
269 path: ProjectPath,
270 language_server_id: LanguageServerId,
271 },
272 RemoteIdChanged(Option<u64>),
273 DisconnectedFromHost,
274 Closed,
275 DeletedEntry(ProjectEntryId),
276 CollaboratorUpdated {
277 old_peer_id: proto::PeerId,
278 new_peer_id: proto::PeerId,
279 },
280 CollaboratorLeft(proto::PeerId),
281 ReloadInlayHints,
282}
283
284pub enum LanguageServerState {
285 Starting(Task<Option<Arc<LanguageServer>>>),
286
287 Running {
288 language: Arc<Language>,
289 adapter: Arc<CachedLspAdapter>,
290 server: Arc<LanguageServer>,
291 watched_paths: HashMap<WorktreeId, GlobSet>,
292 simulate_disk_based_diagnostics_completion: Option<Task<()>>,
293 },
294}
295
296#[derive(Serialize)]
297pub struct LanguageServerStatus {
298 pub name: String,
299 pub pending_work: BTreeMap<String, LanguageServerProgress>,
300 pub has_pending_diagnostic_updates: bool,
301 progress_tokens: HashSet<String>,
302}
303
304#[derive(Clone, Debug, Serialize)]
305pub struct LanguageServerProgress {
306 pub message: Option<String>,
307 pub percentage: Option<usize>,
308 #[serde(skip_serializing)]
309 pub last_update_at: Instant,
310}
311
312#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
313pub struct ProjectPath {
314 pub worktree_id: WorktreeId,
315 pub path: Arc<Path>,
316}
317
318#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize)]
319pub struct DiagnosticSummary {
320 pub error_count: usize,
321 pub warning_count: usize,
322}
323
324#[derive(Debug, Clone, PartialEq, Eq)]
325pub struct Location {
326 pub buffer: ModelHandle<Buffer>,
327 pub range: Range<language::Anchor>,
328}
329
330#[derive(Debug, Clone, PartialEq, Eq)]
331pub struct InlayHint {
332 pub buffer_id: usize,
333 pub position: Anchor,
334 pub label: InlayHintLabel,
335 pub kind: Option<String>,
336 pub tooltip: Option<InlayHintTooltip>,
337}
338
339impl InlayHint {
340 pub fn text(&self) -> String {
341 match &self.label {
342 InlayHintLabel::String(s) => s.to_owned(),
343 InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &part.value).join(""),
344 }
345 }
346}
347
348#[derive(Debug, Clone, PartialEq, Eq)]
349pub enum InlayHintLabel {
350 String(String),
351 LabelParts(Vec<InlayHintLabelPart>),
352}
353
354#[derive(Debug, Clone, PartialEq, Eq)]
355pub struct InlayHintLabelPart {
356 pub value: String,
357 pub tooltip: Option<InlayHintLabelPartTooltip>,
358 pub location: Option<Location>,
359}
360
361#[derive(Debug, Clone, PartialEq, Eq)]
362pub enum InlayHintTooltip {
363 String(String),
364 MarkupContent(MarkupContent),
365}
366
367#[derive(Debug, Clone, PartialEq, Eq)]
368pub enum InlayHintLabelPartTooltip {
369 String(String),
370 MarkupContent(MarkupContent),
371}
372
373#[derive(Debug, Clone, PartialEq, Eq)]
374pub struct MarkupContent {
375 pub kind: String,
376 pub value: String,
377}
378
379#[derive(Debug, Clone)]
380pub struct LocationLink {
381 pub origin: Option<Location>,
382 pub target: Location,
383}
384
385#[derive(Debug)]
386pub struct DocumentHighlight {
387 pub range: Range<language::Anchor>,
388 pub kind: DocumentHighlightKind,
389}
390
391#[derive(Clone, Debug)]
392pub struct Symbol {
393 pub language_server_name: LanguageServerName,
394 pub source_worktree_id: WorktreeId,
395 pub path: ProjectPath,
396 pub label: CodeLabel,
397 pub name: String,
398 pub kind: lsp::SymbolKind,
399 pub range: Range<Unclipped<PointUtf16>>,
400 pub signature: [u8; 32],
401}
402
403#[derive(Clone, Debug, PartialEq)]
404pub struct HoverBlock {
405 pub text: String,
406 pub kind: HoverBlockKind,
407}
408
409#[derive(Clone, Debug, PartialEq)]
410pub enum HoverBlockKind {
411 PlainText,
412 Markdown,
413 Code { language: String },
414}
415
416#[derive(Debug)]
417pub struct Hover {
418 pub contents: Vec<HoverBlock>,
419 pub range: Option<Range<language::Anchor>>,
420 pub language: Option<Arc<Language>>,
421}
422
423#[derive(Default)]
424pub struct ProjectTransaction(pub HashMap<ModelHandle<Buffer>, language::Transaction>);
425
426impl DiagnosticSummary {
427 fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
428 let mut this = Self {
429 error_count: 0,
430 warning_count: 0,
431 };
432
433 for entry in diagnostics {
434 if entry.diagnostic.is_primary {
435 match entry.diagnostic.severity {
436 DiagnosticSeverity::ERROR => this.error_count += 1,
437 DiagnosticSeverity::WARNING => this.warning_count += 1,
438 _ => {}
439 }
440 }
441 }
442
443 this
444 }
445
446 pub fn is_empty(&self) -> bool {
447 self.error_count == 0 && self.warning_count == 0
448 }
449
450 pub fn to_proto(
451 &self,
452 language_server_id: LanguageServerId,
453 path: &Path,
454 ) -> proto::DiagnosticSummary {
455 proto::DiagnosticSummary {
456 path: path.to_string_lossy().to_string(),
457 language_server_id: language_server_id.0 as u64,
458 error_count: self.error_count as u32,
459 warning_count: self.warning_count as u32,
460 }
461 }
462}
463
464#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
465pub struct ProjectEntryId(usize);
466
467impl ProjectEntryId {
468 pub const MAX: Self = Self(usize::MAX);
469
470 pub fn new(counter: &AtomicUsize) -> Self {
471 Self(counter.fetch_add(1, SeqCst))
472 }
473
474 pub fn from_proto(id: u64) -> Self {
475 Self(id as usize)
476 }
477
478 pub fn to_proto(&self) -> u64 {
479 self.0 as u64
480 }
481
482 pub fn to_usize(&self) -> usize {
483 self.0
484 }
485}
486
487#[derive(Debug, Clone, Copy, PartialEq, Eq)]
488pub enum FormatTrigger {
489 Save,
490 Manual,
491}
492
493struct ProjectLspAdapterDelegate {
494 project: ModelHandle<Project>,
495 http_client: Arc<dyn HttpClient>,
496}
497
498impl FormatTrigger {
499 fn from_proto(value: i32) -> FormatTrigger {
500 match value {
501 0 => FormatTrigger::Save,
502 1 => FormatTrigger::Manual,
503 _ => FormatTrigger::Save,
504 }
505 }
506}
507
508impl Project {
509 pub fn init_settings(cx: &mut AppContext) {
510 settings::register::<ProjectSettings>(cx);
511 }
512
513 pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
514 Self::init_settings(cx);
515
516 client.add_model_message_handler(Self::handle_add_collaborator);
517 client.add_model_message_handler(Self::handle_update_project_collaborator);
518 client.add_model_message_handler(Self::handle_remove_collaborator);
519 client.add_model_message_handler(Self::handle_buffer_reloaded);
520 client.add_model_message_handler(Self::handle_buffer_saved);
521 client.add_model_message_handler(Self::handle_start_language_server);
522 client.add_model_message_handler(Self::handle_update_language_server);
523 client.add_model_message_handler(Self::handle_update_project);
524 client.add_model_message_handler(Self::handle_unshare_project);
525 client.add_model_message_handler(Self::handle_create_buffer_for_peer);
526 client.add_model_message_handler(Self::handle_update_buffer_file);
527 client.add_model_request_handler(Self::handle_update_buffer);
528 client.add_model_message_handler(Self::handle_update_diagnostic_summary);
529 client.add_model_message_handler(Self::handle_update_worktree);
530 client.add_model_message_handler(Self::handle_update_worktree_settings);
531 client.add_model_request_handler(Self::handle_create_project_entry);
532 client.add_model_request_handler(Self::handle_rename_project_entry);
533 client.add_model_request_handler(Self::handle_copy_project_entry);
534 client.add_model_request_handler(Self::handle_delete_project_entry);
535 client.add_model_request_handler(Self::handle_expand_project_entry);
536 client.add_model_request_handler(Self::handle_apply_additional_edits_for_completion);
537 client.add_model_request_handler(Self::handle_apply_code_action);
538 client.add_model_request_handler(Self::handle_on_type_formatting);
539 client.add_model_request_handler(Self::handle_inlay_hints);
540 client.add_model_request_handler(Self::handle_reload_buffers);
541 client.add_model_request_handler(Self::handle_synchronize_buffers);
542 client.add_model_request_handler(Self::handle_format_buffers);
543 client.add_model_request_handler(Self::handle_lsp_command::<GetCodeActions>);
544 client.add_model_request_handler(Self::handle_lsp_command::<GetCompletions>);
545 client.add_model_request_handler(Self::handle_lsp_command::<GetHover>);
546 client.add_model_request_handler(Self::handle_lsp_command::<GetDefinition>);
547 client.add_model_request_handler(Self::handle_lsp_command::<GetTypeDefinition>);
548 client.add_model_request_handler(Self::handle_lsp_command::<GetDocumentHighlights>);
549 client.add_model_request_handler(Self::handle_lsp_command::<GetReferences>);
550 client.add_model_request_handler(Self::handle_lsp_command::<PrepareRename>);
551 client.add_model_request_handler(Self::handle_lsp_command::<PerformRename>);
552 client.add_model_request_handler(Self::handle_search_project);
553 client.add_model_request_handler(Self::handle_get_project_symbols);
554 client.add_model_request_handler(Self::handle_open_buffer_for_symbol);
555 client.add_model_request_handler(Self::handle_open_buffer_by_id);
556 client.add_model_request_handler(Self::handle_open_buffer_by_path);
557 client.add_model_request_handler(Self::handle_save_buffer);
558 client.add_model_message_handler(Self::handle_update_diff_base);
559 }
560
561 pub fn local(
562 client: Arc<Client>,
563 user_store: ModelHandle<UserStore>,
564 languages: Arc<LanguageRegistry>,
565 fs: Arc<dyn Fs>,
566 cx: &mut AppContext,
567 ) -> ModelHandle<Self> {
568 cx.add_model(|cx: &mut ModelContext<Self>| {
569 let (tx, rx) = mpsc::unbounded();
570 cx.spawn_weak(|this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
571 .detach();
572 Self {
573 worktrees: Default::default(),
574 buffer_ordered_messages_tx: tx,
575 collaborators: Default::default(),
576 next_buffer_id: 0,
577 opened_buffers: Default::default(),
578 shared_buffers: Default::default(),
579 incomplete_remote_buffers: Default::default(),
580 loading_buffers_by_path: Default::default(),
581 loading_local_worktrees: Default::default(),
582 local_buffer_ids_by_path: Default::default(),
583 local_buffer_ids_by_entry_id: Default::default(),
584 buffer_snapshots: Default::default(),
585 join_project_response_message_id: 0,
586 client_state: None,
587 opened_buffer: watch::channel(),
588 client_subscriptions: Vec::new(),
589 _subscriptions: vec![
590 cx.observe_global::<SettingsStore, _>(Self::on_settings_changed)
591 ],
592 _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx),
593 _maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx),
594 active_entry: None,
595 languages,
596 client,
597 user_store,
598 fs,
599 next_entry_id: Default::default(),
600 next_diagnostic_group_id: Default::default(),
601 language_servers: Default::default(),
602 language_server_ids: Default::default(),
603 language_server_statuses: Default::default(),
604 last_workspace_edits_by_language_server: Default::default(),
605 buffers_being_formatted: Default::default(),
606 buffers_needing_diff: Default::default(),
607 git_diff_debouncer: DelayedDebounced::new(),
608 nonce: StdRng::from_entropy().gen(),
609 terminals: Terminals {
610 local_handles: Vec::new(),
611 },
612 copilot_enabled: Copilot::global(cx).is_some(),
613 }
614 })
615 }
616
617 pub async fn remote(
618 remote_id: u64,
619 client: Arc<Client>,
620 user_store: ModelHandle<UserStore>,
621 languages: Arc<LanguageRegistry>,
622 fs: Arc<dyn Fs>,
623 mut cx: AsyncAppContext,
624 ) -> Result<ModelHandle<Self>> {
625 client.authenticate_and_connect(true, &cx).await?;
626
627 let subscription = client.subscribe_to_entity(remote_id)?;
628 let response = client
629 .request_envelope(proto::JoinProject {
630 project_id: remote_id,
631 })
632 .await?;
633 let this = cx.add_model(|cx| {
634 let replica_id = response.payload.replica_id as ReplicaId;
635
636 let mut worktrees = Vec::new();
637 for worktree in response.payload.worktrees {
638 let worktree = cx.update(|cx| {
639 Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx)
640 });
641 worktrees.push(worktree);
642 }
643
644 let (tx, rx) = mpsc::unbounded();
645 cx.spawn_weak(|this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
646 .detach();
647 let mut this = Self {
648 worktrees: Vec::new(),
649 buffer_ordered_messages_tx: tx,
650 loading_buffers_by_path: Default::default(),
651 next_buffer_id: 0,
652 opened_buffer: watch::channel(),
653 shared_buffers: Default::default(),
654 incomplete_remote_buffers: Default::default(),
655 loading_local_worktrees: Default::default(),
656 local_buffer_ids_by_path: Default::default(),
657 local_buffer_ids_by_entry_id: Default::default(),
658 active_entry: None,
659 collaborators: Default::default(),
660 join_project_response_message_id: response.message_id,
661 _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx),
662 _maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx),
663 languages,
664 user_store: user_store.clone(),
665 fs,
666 next_entry_id: Default::default(),
667 next_diagnostic_group_id: Default::default(),
668 client_subscriptions: Default::default(),
669 _subscriptions: Default::default(),
670 client: client.clone(),
671 client_state: Some(ProjectClientState::Remote {
672 sharing_has_stopped: false,
673 remote_id,
674 replica_id,
675 }),
676 language_servers: Default::default(),
677 language_server_ids: Default::default(),
678 language_server_statuses: response
679 .payload
680 .language_servers
681 .into_iter()
682 .map(|server| {
683 (
684 LanguageServerId(server.id as usize),
685 LanguageServerStatus {
686 name: server.name,
687 pending_work: Default::default(),
688 has_pending_diagnostic_updates: false,
689 progress_tokens: Default::default(),
690 },
691 )
692 })
693 .collect(),
694 last_workspace_edits_by_language_server: Default::default(),
695 opened_buffers: Default::default(),
696 buffers_being_formatted: Default::default(),
697 buffers_needing_diff: Default::default(),
698 git_diff_debouncer: DelayedDebounced::new(),
699 buffer_snapshots: Default::default(),
700 nonce: StdRng::from_entropy().gen(),
701 terminals: Terminals {
702 local_handles: Vec::new(),
703 },
704 copilot_enabled: Copilot::global(cx).is_some(),
705 };
706 for worktree in worktrees {
707 let _ = this.add_worktree(&worktree, cx);
708 }
709 this
710 });
711 let subscription = subscription.set_model(&this, &mut cx);
712
713 let user_ids = response
714 .payload
715 .collaborators
716 .iter()
717 .map(|peer| peer.user_id)
718 .collect();
719 user_store
720 .update(&mut cx, |user_store, cx| user_store.get_users(user_ids, cx))
721 .await?;
722
723 this.update(&mut cx, |this, cx| {
724 this.set_collaborators_from_proto(response.payload.collaborators, cx)?;
725 this.client_subscriptions.push(subscription);
726 anyhow::Ok(())
727 })?;
728
729 Ok(this)
730 }
731
732 #[cfg(any(test, feature = "test-support"))]
733 pub async fn test(
734 fs: Arc<dyn Fs>,
735 root_paths: impl IntoIterator<Item = &Path>,
736 cx: &mut gpui::TestAppContext,
737 ) -> ModelHandle<Project> {
738 let mut languages = LanguageRegistry::test();
739 languages.set_executor(cx.background());
740 let http_client = util::http::FakeHttpClient::with_404_response();
741 let client = cx.update(|cx| client::Client::new(http_client.clone(), cx));
742 let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
743 let project =
744 cx.update(|cx| Project::local(client, user_store, Arc::new(languages), fs, cx));
745 for path in root_paths {
746 let (tree, _) = project
747 .update(cx, |project, cx| {
748 project.find_or_create_local_worktree(path, true, cx)
749 })
750 .await
751 .unwrap();
752 tree.read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
753 .await;
754 }
755 project
756 }
757
758 fn on_settings_changed(&mut self, cx: &mut ModelContext<Self>) {
759 let mut language_servers_to_start = Vec::new();
760 for buffer in self.opened_buffers.values() {
761 if let Some(buffer) = buffer.upgrade(cx) {
762 let buffer = buffer.read(cx);
763 if let Some((file, language)) = buffer.file().zip(buffer.language()) {
764 let settings = language_settings(Some(language), Some(file), cx);
765 if settings.enable_language_server {
766 if let Some(file) = File::from_dyn(Some(file)) {
767 language_servers_to_start
768 .push((file.worktree.clone(), language.clone()));
769 }
770 }
771 }
772 }
773 }
774
775 let mut language_servers_to_stop = Vec::new();
776 let languages = self.languages.to_vec();
777 for (worktree_id, started_lsp_name) in self.language_server_ids.keys() {
778 let language = languages.iter().find(|l| {
779 l.lsp_adapters()
780 .iter()
781 .any(|adapter| &adapter.name == started_lsp_name)
782 });
783 if let Some(language) = language {
784 let worktree = self.worktree_for_id(*worktree_id, cx);
785 let file = worktree.and_then(|tree| {
786 tree.update(cx, |tree, cx| tree.root_file(cx).map(|f| f as _))
787 });
788 if !language_settings(Some(language), file.as_ref(), cx).enable_language_server {
789 language_servers_to_stop.push((*worktree_id, started_lsp_name.clone()));
790 }
791 }
792 }
793
794 // Stop all newly-disabled language servers.
795 for (worktree_id, adapter_name) in language_servers_to_stop {
796 self.stop_language_server(worktree_id, adapter_name, cx)
797 .detach();
798 }
799
800 // Start all the newly-enabled language servers.
801 for (worktree, language) in language_servers_to_start {
802 let worktree_path = worktree.read(cx).abs_path();
803 self.start_language_servers(&worktree, worktree_path, language, cx);
804 }
805
806 if !self.copilot_enabled && Copilot::global(cx).is_some() {
807 self.copilot_enabled = true;
808 for buffer in self.opened_buffers.values() {
809 if let Some(buffer) = buffer.upgrade(cx) {
810 self.register_buffer_with_copilot(&buffer, cx);
811 }
812 }
813 }
814
815 cx.notify();
816 }
817
818 pub fn buffer_for_id(&self, remote_id: u64, cx: &AppContext) -> Option<ModelHandle<Buffer>> {
819 self.opened_buffers
820 .get(&remote_id)
821 .and_then(|buffer| buffer.upgrade(cx))
822 }
823
824 pub fn languages(&self) -> &Arc<LanguageRegistry> {
825 &self.languages
826 }
827
828 pub fn client(&self) -> Arc<Client> {
829 self.client.clone()
830 }
831
832 pub fn user_store(&self) -> ModelHandle<UserStore> {
833 self.user_store.clone()
834 }
835
836 #[cfg(any(test, feature = "test-support"))]
837 pub fn opened_buffers(&self, cx: &AppContext) -> Vec<ModelHandle<Buffer>> {
838 self.opened_buffers
839 .values()
840 .filter_map(|b| b.upgrade(cx))
841 .collect()
842 }
843
844 #[cfg(any(test, feature = "test-support"))]
845 pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &AppContext) -> bool {
846 let path = path.into();
847 if let Some(worktree) = self.worktree_for_id(path.worktree_id, cx) {
848 self.opened_buffers.iter().any(|(_, buffer)| {
849 if let Some(buffer) = buffer.upgrade(cx) {
850 if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
851 if file.worktree == worktree && file.path() == &path.path {
852 return true;
853 }
854 }
855 }
856 false
857 })
858 } else {
859 false
860 }
861 }
862
863 pub fn fs(&self) -> &Arc<dyn Fs> {
864 &self.fs
865 }
866
867 pub fn remote_id(&self) -> Option<u64> {
868 match self.client_state.as_ref()? {
869 ProjectClientState::Local { remote_id, .. }
870 | ProjectClientState::Remote { remote_id, .. } => Some(*remote_id),
871 }
872 }
873
874 pub fn replica_id(&self) -> ReplicaId {
875 match &self.client_state {
876 Some(ProjectClientState::Remote { replica_id, .. }) => *replica_id,
877 _ => 0,
878 }
879 }
880
881 fn metadata_changed(&mut self, cx: &mut ModelContext<Self>) {
882 if let Some(ProjectClientState::Local { updates_tx, .. }) = &mut self.client_state {
883 updates_tx
884 .unbounded_send(LocalProjectUpdate::WorktreesChanged)
885 .ok();
886 }
887 cx.notify();
888 }
889
890 pub fn collaborators(&self) -> &HashMap<proto::PeerId, Collaborator> {
891 &self.collaborators
892 }
893
894 /// Collect all worktrees, including ones that don't appear in the project panel
895 pub fn worktrees<'a>(
896 &'a self,
897 cx: &'a AppContext,
898 ) -> impl 'a + DoubleEndedIterator<Item = ModelHandle<Worktree>> {
899 self.worktrees
900 .iter()
901 .filter_map(move |worktree| worktree.upgrade(cx))
902 }
903
904 /// Collect all user-visible worktrees, the ones that appear in the project panel
905 pub fn visible_worktrees<'a>(
906 &'a self,
907 cx: &'a AppContext,
908 ) -> impl 'a + DoubleEndedIterator<Item = ModelHandle<Worktree>> {
909 self.worktrees.iter().filter_map(|worktree| {
910 worktree.upgrade(cx).and_then(|worktree| {
911 if worktree.read(cx).is_visible() {
912 Some(worktree)
913 } else {
914 None
915 }
916 })
917 })
918 }
919
920 pub fn worktree_root_names<'a>(&'a self, cx: &'a AppContext) -> impl Iterator<Item = &'a str> {
921 self.visible_worktrees(cx)
922 .map(|tree| tree.read(cx).root_name())
923 }
924
925 pub fn worktree_for_id(
926 &self,
927 id: WorktreeId,
928 cx: &AppContext,
929 ) -> Option<ModelHandle<Worktree>> {
930 self.worktrees(cx)
931 .find(|worktree| worktree.read(cx).id() == id)
932 }
933
934 pub fn worktree_for_entry(
935 &self,
936 entry_id: ProjectEntryId,
937 cx: &AppContext,
938 ) -> Option<ModelHandle<Worktree>> {
939 self.worktrees(cx)
940 .find(|worktree| worktree.read(cx).contains_entry(entry_id))
941 }
942
943 pub fn worktree_id_for_entry(
944 &self,
945 entry_id: ProjectEntryId,
946 cx: &AppContext,
947 ) -> Option<WorktreeId> {
948 self.worktree_for_entry(entry_id, cx)
949 .map(|worktree| worktree.read(cx).id())
950 }
951
952 pub fn contains_paths(&self, paths: &[PathBuf], cx: &AppContext) -> bool {
953 paths.iter().all(|path| self.contains_path(path, cx))
954 }
955
956 pub fn contains_path(&self, path: &Path, cx: &AppContext) -> bool {
957 for worktree in self.worktrees(cx) {
958 let worktree = worktree.read(cx).as_local();
959 if worktree.map_or(false, |w| w.contains_abs_path(path)) {
960 return true;
961 }
962 }
963 false
964 }
965
966 pub fn create_entry(
967 &mut self,
968 project_path: impl Into<ProjectPath>,
969 is_directory: bool,
970 cx: &mut ModelContext<Self>,
971 ) -> Option<Task<Result<Entry>>> {
972 let project_path = project_path.into();
973 let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
974 if self.is_local() {
975 Some(worktree.update(cx, |worktree, cx| {
976 worktree
977 .as_local_mut()
978 .unwrap()
979 .create_entry(project_path.path, is_directory, cx)
980 }))
981 } else {
982 let client = self.client.clone();
983 let project_id = self.remote_id().unwrap();
984 Some(cx.spawn_weak(|_, mut cx| async move {
985 let response = client
986 .request(proto::CreateProjectEntry {
987 worktree_id: project_path.worktree_id.to_proto(),
988 project_id,
989 path: project_path.path.to_string_lossy().into(),
990 is_directory,
991 })
992 .await?;
993 let entry = response
994 .entry
995 .ok_or_else(|| anyhow!("missing entry in response"))?;
996 worktree
997 .update(&mut cx, |worktree, cx| {
998 worktree.as_remote_mut().unwrap().insert_entry(
999 entry,
1000 response.worktree_scan_id as usize,
1001 cx,
1002 )
1003 })
1004 .await
1005 }))
1006 }
1007 }
1008
1009 pub fn copy_entry(
1010 &mut self,
1011 entry_id: ProjectEntryId,
1012 new_path: impl Into<Arc<Path>>,
1013 cx: &mut ModelContext<Self>,
1014 ) -> Option<Task<Result<Entry>>> {
1015 let worktree = self.worktree_for_entry(entry_id, cx)?;
1016 let new_path = new_path.into();
1017 if self.is_local() {
1018 worktree.update(cx, |worktree, cx| {
1019 worktree
1020 .as_local_mut()
1021 .unwrap()
1022 .copy_entry(entry_id, new_path, cx)
1023 })
1024 } else {
1025 let client = self.client.clone();
1026 let project_id = self.remote_id().unwrap();
1027
1028 Some(cx.spawn_weak(|_, mut cx| async move {
1029 let response = client
1030 .request(proto::CopyProjectEntry {
1031 project_id,
1032 entry_id: entry_id.to_proto(),
1033 new_path: new_path.to_string_lossy().into(),
1034 })
1035 .await?;
1036 let entry = response
1037 .entry
1038 .ok_or_else(|| anyhow!("missing entry in response"))?;
1039 worktree
1040 .update(&mut cx, |worktree, cx| {
1041 worktree.as_remote_mut().unwrap().insert_entry(
1042 entry,
1043 response.worktree_scan_id as usize,
1044 cx,
1045 )
1046 })
1047 .await
1048 }))
1049 }
1050 }
1051
1052 pub fn rename_entry(
1053 &mut self,
1054 entry_id: ProjectEntryId,
1055 new_path: impl Into<Arc<Path>>,
1056 cx: &mut ModelContext<Self>,
1057 ) -> Option<Task<Result<Entry>>> {
1058 let worktree = self.worktree_for_entry(entry_id, cx)?;
1059 let new_path = new_path.into();
1060 if self.is_local() {
1061 worktree.update(cx, |worktree, cx| {
1062 worktree
1063 .as_local_mut()
1064 .unwrap()
1065 .rename_entry(entry_id, new_path, cx)
1066 })
1067 } else {
1068 let client = self.client.clone();
1069 let project_id = self.remote_id().unwrap();
1070
1071 Some(cx.spawn_weak(|_, mut cx| async move {
1072 let response = client
1073 .request(proto::RenameProjectEntry {
1074 project_id,
1075 entry_id: entry_id.to_proto(),
1076 new_path: new_path.to_string_lossy().into(),
1077 })
1078 .await?;
1079 let entry = response
1080 .entry
1081 .ok_or_else(|| anyhow!("missing entry in response"))?;
1082 worktree
1083 .update(&mut cx, |worktree, cx| {
1084 worktree.as_remote_mut().unwrap().insert_entry(
1085 entry,
1086 response.worktree_scan_id as usize,
1087 cx,
1088 )
1089 })
1090 .await
1091 }))
1092 }
1093 }
1094
1095 pub fn delete_entry(
1096 &mut self,
1097 entry_id: ProjectEntryId,
1098 cx: &mut ModelContext<Self>,
1099 ) -> Option<Task<Result<()>>> {
1100 let worktree = self.worktree_for_entry(entry_id, cx)?;
1101
1102 cx.emit(Event::DeletedEntry(entry_id));
1103
1104 if self.is_local() {
1105 worktree.update(cx, |worktree, cx| {
1106 worktree.as_local_mut().unwrap().delete_entry(entry_id, cx)
1107 })
1108 } else {
1109 let client = self.client.clone();
1110 let project_id = self.remote_id().unwrap();
1111 Some(cx.spawn_weak(|_, mut cx| async move {
1112 let response = client
1113 .request(proto::DeleteProjectEntry {
1114 project_id,
1115 entry_id: entry_id.to_proto(),
1116 })
1117 .await?;
1118 worktree
1119 .update(&mut cx, move |worktree, cx| {
1120 worktree.as_remote_mut().unwrap().delete_entry(
1121 entry_id,
1122 response.worktree_scan_id as usize,
1123 cx,
1124 )
1125 })
1126 .await
1127 }))
1128 }
1129 }
1130
1131 pub fn expand_entry(
1132 &mut self,
1133 worktree_id: WorktreeId,
1134 entry_id: ProjectEntryId,
1135 cx: &mut ModelContext<Self>,
1136 ) -> Option<Task<Result<()>>> {
1137 let worktree = self.worktree_for_id(worktree_id, cx)?;
1138 if self.is_local() {
1139 worktree.update(cx, |worktree, cx| {
1140 worktree.as_local_mut().unwrap().expand_entry(entry_id, cx)
1141 })
1142 } else {
1143 let worktree = worktree.downgrade();
1144 let request = self.client.request(proto::ExpandProjectEntry {
1145 project_id: self.remote_id().unwrap(),
1146 entry_id: entry_id.to_proto(),
1147 });
1148 Some(cx.spawn_weak(|_, mut cx| async move {
1149 let response = request.await?;
1150 if let Some(worktree) = worktree.upgrade(&cx) {
1151 worktree
1152 .update(&mut cx, |worktree, _| {
1153 worktree
1154 .as_remote_mut()
1155 .unwrap()
1156 .wait_for_snapshot(response.worktree_scan_id as usize)
1157 })
1158 .await?;
1159 }
1160 Ok(())
1161 }))
1162 }
1163 }
1164
1165 pub fn shared(&mut self, project_id: u64, cx: &mut ModelContext<Self>) -> Result<()> {
1166 if self.client_state.is_some() {
1167 return Err(anyhow!("project was already shared"));
1168 }
1169 self.client_subscriptions.push(
1170 self.client
1171 .subscribe_to_entity(project_id)?
1172 .set_model(&cx.handle(), &mut cx.to_async()),
1173 );
1174
1175 for open_buffer in self.opened_buffers.values_mut() {
1176 match open_buffer {
1177 OpenBuffer::Strong(_) => {}
1178 OpenBuffer::Weak(buffer) => {
1179 if let Some(buffer) = buffer.upgrade(cx) {
1180 *open_buffer = OpenBuffer::Strong(buffer);
1181 }
1182 }
1183 OpenBuffer::Operations(_) => unreachable!(),
1184 }
1185 }
1186
1187 for worktree_handle in self.worktrees.iter_mut() {
1188 match worktree_handle {
1189 WorktreeHandle::Strong(_) => {}
1190 WorktreeHandle::Weak(worktree) => {
1191 if let Some(worktree) = worktree.upgrade(cx) {
1192 *worktree_handle = WorktreeHandle::Strong(worktree);
1193 }
1194 }
1195 }
1196 }
1197
1198 for (server_id, status) in &self.language_server_statuses {
1199 self.client
1200 .send(proto::StartLanguageServer {
1201 project_id,
1202 server: Some(proto::LanguageServer {
1203 id: server_id.0 as u64,
1204 name: status.name.clone(),
1205 }),
1206 })
1207 .log_err();
1208 }
1209
1210 let store = cx.global::<SettingsStore>();
1211 for worktree in self.worktrees(cx) {
1212 let worktree_id = worktree.read(cx).id().to_proto();
1213 for (path, content) in store.local_settings(worktree.id()) {
1214 self.client
1215 .send(proto::UpdateWorktreeSettings {
1216 project_id,
1217 worktree_id,
1218 path: path.to_string_lossy().into(),
1219 content: Some(content),
1220 })
1221 .log_err();
1222 }
1223 }
1224
1225 let (updates_tx, mut updates_rx) = mpsc::unbounded();
1226 let client = self.client.clone();
1227 self.client_state = Some(ProjectClientState::Local {
1228 remote_id: project_id,
1229 updates_tx,
1230 _send_updates: cx.spawn_weak(move |this, mut cx| async move {
1231 while let Some(update) = updates_rx.next().await {
1232 let Some(this) = this.upgrade(&cx) else { break };
1233
1234 match update {
1235 LocalProjectUpdate::WorktreesChanged => {
1236 let worktrees = this
1237 .read_with(&cx, |this, cx| this.worktrees(cx).collect::<Vec<_>>());
1238 let update_project = this
1239 .read_with(&cx, |this, cx| {
1240 this.client.request(proto::UpdateProject {
1241 project_id,
1242 worktrees: this.worktree_metadata_protos(cx),
1243 })
1244 })
1245 .await;
1246 if update_project.is_ok() {
1247 for worktree in worktrees {
1248 worktree.update(&mut cx, |worktree, cx| {
1249 let worktree = worktree.as_local_mut().unwrap();
1250 worktree.share(project_id, cx).detach_and_log_err(cx)
1251 });
1252 }
1253 }
1254 }
1255 LocalProjectUpdate::CreateBufferForPeer { peer_id, buffer_id } => {
1256 let buffer = this.update(&mut cx, |this, _| {
1257 let buffer = this.opened_buffers.get(&buffer_id).unwrap();
1258 let shared_buffers =
1259 this.shared_buffers.entry(peer_id).or_default();
1260 if shared_buffers.insert(buffer_id) {
1261 if let OpenBuffer::Strong(buffer) = buffer {
1262 Some(buffer.clone())
1263 } else {
1264 None
1265 }
1266 } else {
1267 None
1268 }
1269 });
1270
1271 let Some(buffer) = buffer else { continue };
1272 let operations =
1273 buffer.read_with(&cx, |b, cx| b.serialize_ops(None, cx));
1274 let operations = operations.await;
1275 let state = buffer.read_with(&cx, |buffer, _| buffer.to_proto());
1276
1277 let initial_state = proto::CreateBufferForPeer {
1278 project_id,
1279 peer_id: Some(peer_id),
1280 variant: Some(proto::create_buffer_for_peer::Variant::State(state)),
1281 };
1282 if client.send(initial_state).log_err().is_some() {
1283 let client = client.clone();
1284 cx.background()
1285 .spawn(async move {
1286 let mut chunks = split_operations(operations).peekable();
1287 while let Some(chunk) = chunks.next() {
1288 let is_last = chunks.peek().is_none();
1289 client.send(proto::CreateBufferForPeer {
1290 project_id,
1291 peer_id: Some(peer_id),
1292 variant: Some(
1293 proto::create_buffer_for_peer::Variant::Chunk(
1294 proto::BufferChunk {
1295 buffer_id,
1296 operations: chunk,
1297 is_last,
1298 },
1299 ),
1300 ),
1301 })?;
1302 }
1303 anyhow::Ok(())
1304 })
1305 .await
1306 .log_err();
1307 }
1308 }
1309 }
1310 }
1311 }),
1312 });
1313
1314 self.metadata_changed(cx);
1315 cx.emit(Event::RemoteIdChanged(Some(project_id)));
1316 cx.notify();
1317 Ok(())
1318 }
1319
1320 pub fn reshared(
1321 &mut self,
1322 message: proto::ResharedProject,
1323 cx: &mut ModelContext<Self>,
1324 ) -> Result<()> {
1325 self.shared_buffers.clear();
1326 self.set_collaborators_from_proto(message.collaborators, cx)?;
1327 self.metadata_changed(cx);
1328 Ok(())
1329 }
1330
1331 pub fn rejoined(
1332 &mut self,
1333 message: proto::RejoinedProject,
1334 message_id: u32,
1335 cx: &mut ModelContext<Self>,
1336 ) -> Result<()> {
1337 cx.update_global::<SettingsStore, _, _>(|store, cx| {
1338 for worktree in &self.worktrees {
1339 store
1340 .clear_local_settings(worktree.handle_id(), cx)
1341 .log_err();
1342 }
1343 });
1344
1345 self.join_project_response_message_id = message_id;
1346 self.set_worktrees_from_proto(message.worktrees, cx)?;
1347 self.set_collaborators_from_proto(message.collaborators, cx)?;
1348 self.language_server_statuses = message
1349 .language_servers
1350 .into_iter()
1351 .map(|server| {
1352 (
1353 LanguageServerId(server.id as usize),
1354 LanguageServerStatus {
1355 name: server.name,
1356 pending_work: Default::default(),
1357 has_pending_diagnostic_updates: false,
1358 progress_tokens: Default::default(),
1359 },
1360 )
1361 })
1362 .collect();
1363 self.buffer_ordered_messages_tx
1364 .unbounded_send(BufferOrderedMessage::Resync)
1365 .unwrap();
1366 cx.notify();
1367 Ok(())
1368 }
1369
1370 pub fn unshare(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
1371 self.unshare_internal(cx)?;
1372 self.metadata_changed(cx);
1373 cx.notify();
1374 Ok(())
1375 }
1376
1377 fn unshare_internal(&mut self, cx: &mut AppContext) -> Result<()> {
1378 if self.is_remote() {
1379 return Err(anyhow!("attempted to unshare a remote project"));
1380 }
1381
1382 if let Some(ProjectClientState::Local { remote_id, .. }) = self.client_state.take() {
1383 self.collaborators.clear();
1384 self.shared_buffers.clear();
1385 self.client_subscriptions.clear();
1386
1387 for worktree_handle in self.worktrees.iter_mut() {
1388 if let WorktreeHandle::Strong(worktree) = worktree_handle {
1389 let is_visible = worktree.update(cx, |worktree, _| {
1390 worktree.as_local_mut().unwrap().unshare();
1391 worktree.is_visible()
1392 });
1393 if !is_visible {
1394 *worktree_handle = WorktreeHandle::Weak(worktree.downgrade());
1395 }
1396 }
1397 }
1398
1399 for open_buffer in self.opened_buffers.values_mut() {
1400 // Wake up any tasks waiting for peers' edits to this buffer.
1401 if let Some(buffer) = open_buffer.upgrade(cx) {
1402 buffer.update(cx, |buffer, _| buffer.give_up_waiting());
1403 }
1404
1405 if let OpenBuffer::Strong(buffer) = open_buffer {
1406 *open_buffer = OpenBuffer::Weak(buffer.downgrade());
1407 }
1408 }
1409
1410 self.client.send(proto::UnshareProject {
1411 project_id: remote_id,
1412 })?;
1413
1414 Ok(())
1415 } else {
1416 Err(anyhow!("attempted to unshare an unshared project"))
1417 }
1418 }
1419
1420 pub fn disconnected_from_host(&mut self, cx: &mut ModelContext<Self>) {
1421 self.disconnected_from_host_internal(cx);
1422 cx.emit(Event::DisconnectedFromHost);
1423 cx.notify();
1424 }
1425
1426 fn disconnected_from_host_internal(&mut self, cx: &mut AppContext) {
1427 if let Some(ProjectClientState::Remote {
1428 sharing_has_stopped,
1429 ..
1430 }) = &mut self.client_state
1431 {
1432 *sharing_has_stopped = true;
1433
1434 self.collaborators.clear();
1435
1436 for worktree in &self.worktrees {
1437 if let Some(worktree) = worktree.upgrade(cx) {
1438 worktree.update(cx, |worktree, _| {
1439 if let Some(worktree) = worktree.as_remote_mut() {
1440 worktree.disconnected_from_host();
1441 }
1442 });
1443 }
1444 }
1445
1446 for open_buffer in self.opened_buffers.values_mut() {
1447 // Wake up any tasks waiting for peers' edits to this buffer.
1448 if let Some(buffer) = open_buffer.upgrade(cx) {
1449 buffer.update(cx, |buffer, _| buffer.give_up_waiting());
1450 }
1451
1452 if let OpenBuffer::Strong(buffer) = open_buffer {
1453 *open_buffer = OpenBuffer::Weak(buffer.downgrade());
1454 }
1455 }
1456
1457 // Wake up all futures currently waiting on a buffer to get opened,
1458 // to give them a chance to fail now that we've disconnected.
1459 *self.opened_buffer.0.borrow_mut() = ();
1460 }
1461 }
1462
1463 pub fn close(&mut self, cx: &mut ModelContext<Self>) {
1464 cx.emit(Event::Closed);
1465 }
1466
1467 pub fn is_read_only(&self) -> bool {
1468 match &self.client_state {
1469 Some(ProjectClientState::Remote {
1470 sharing_has_stopped,
1471 ..
1472 }) => *sharing_has_stopped,
1473 _ => false,
1474 }
1475 }
1476
1477 pub fn is_local(&self) -> bool {
1478 match &self.client_state {
1479 Some(ProjectClientState::Remote { .. }) => false,
1480 _ => true,
1481 }
1482 }
1483
1484 pub fn is_remote(&self) -> bool {
1485 !self.is_local()
1486 }
1487
1488 pub fn create_buffer(
1489 &mut self,
1490 text: &str,
1491 language: Option<Arc<Language>>,
1492 cx: &mut ModelContext<Self>,
1493 ) -> Result<ModelHandle<Buffer>> {
1494 if self.is_remote() {
1495 return Err(anyhow!("creating buffers as a guest is not supported yet"));
1496 }
1497
1498 let buffer = cx.add_model(|cx| {
1499 Buffer::new(self.replica_id(), text, cx)
1500 .with_language(language.unwrap_or_else(|| language::PLAIN_TEXT.clone()), cx)
1501 });
1502 self.register_buffer(&buffer, cx)?;
1503 Ok(buffer)
1504 }
1505
1506 pub fn open_path(
1507 &mut self,
1508 path: impl Into<ProjectPath>,
1509 cx: &mut ModelContext<Self>,
1510 ) -> Task<Result<(ProjectEntryId, AnyModelHandle)>> {
1511 let task = self.open_buffer(path, cx);
1512 cx.spawn_weak(|_, cx| async move {
1513 let buffer = task.await?;
1514 let project_entry_id = buffer
1515 .read_with(&cx, |buffer, cx| {
1516 File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
1517 })
1518 .ok_or_else(|| anyhow!("no project entry"))?;
1519
1520 let buffer: &AnyModelHandle = &buffer;
1521 Ok((project_entry_id, buffer.clone()))
1522 })
1523 }
1524
1525 pub fn open_local_buffer(
1526 &mut self,
1527 abs_path: impl AsRef<Path>,
1528 cx: &mut ModelContext<Self>,
1529 ) -> Task<Result<ModelHandle<Buffer>>> {
1530 if let Some((worktree, relative_path)) = self.find_local_worktree(abs_path.as_ref(), cx) {
1531 self.open_buffer((worktree.read(cx).id(), relative_path), cx)
1532 } else {
1533 Task::ready(Err(anyhow!("no such path")))
1534 }
1535 }
1536
1537 pub fn open_buffer(
1538 &mut self,
1539 path: impl Into<ProjectPath>,
1540 cx: &mut ModelContext<Self>,
1541 ) -> Task<Result<ModelHandle<Buffer>>> {
1542 let project_path = path.into();
1543 let worktree = if let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) {
1544 worktree
1545 } else {
1546 return Task::ready(Err(anyhow!("no such worktree")));
1547 };
1548
1549 // If there is already a buffer for the given path, then return it.
1550 let existing_buffer = self.get_open_buffer(&project_path, cx);
1551 if let Some(existing_buffer) = existing_buffer {
1552 return Task::ready(Ok(existing_buffer));
1553 }
1554
1555 let loading_watch = match self.loading_buffers_by_path.entry(project_path.clone()) {
1556 // If the given path is already being loaded, then wait for that existing
1557 // task to complete and return the same buffer.
1558 hash_map::Entry::Occupied(e) => e.get().clone(),
1559
1560 // Otherwise, record the fact that this path is now being loaded.
1561 hash_map::Entry::Vacant(entry) => {
1562 let (mut tx, rx) = postage::watch::channel();
1563 entry.insert(rx.clone());
1564
1565 let load_buffer = if worktree.read(cx).is_local() {
1566 self.open_local_buffer_internal(&project_path.path, &worktree, cx)
1567 } else {
1568 self.open_remote_buffer_internal(&project_path.path, &worktree, cx)
1569 };
1570
1571 cx.spawn(move |this, mut cx| async move {
1572 let load_result = load_buffer.await;
1573 *tx.borrow_mut() = Some(this.update(&mut cx, |this, _| {
1574 // Record the fact that the buffer is no longer loading.
1575 this.loading_buffers_by_path.remove(&project_path);
1576 let buffer = load_result.map_err(Arc::new)?;
1577 Ok(buffer)
1578 }));
1579 })
1580 .detach();
1581 rx
1582 }
1583 };
1584
1585 cx.foreground().spawn(async move {
1586 wait_for_loading_buffer(loading_watch)
1587 .await
1588 .map_err(|error| anyhow!("{}", error))
1589 })
1590 }
1591
1592 fn open_local_buffer_internal(
1593 &mut self,
1594 path: &Arc<Path>,
1595 worktree: &ModelHandle<Worktree>,
1596 cx: &mut ModelContext<Self>,
1597 ) -> Task<Result<ModelHandle<Buffer>>> {
1598 let buffer_id = post_inc(&mut self.next_buffer_id);
1599 let load_buffer = worktree.update(cx, |worktree, cx| {
1600 let worktree = worktree.as_local_mut().unwrap();
1601 worktree.load_buffer(buffer_id, path, cx)
1602 });
1603 cx.spawn(|this, mut cx| async move {
1604 let buffer = load_buffer.await?;
1605 this.update(&mut cx, |this, cx| this.register_buffer(&buffer, cx))?;
1606 Ok(buffer)
1607 })
1608 }
1609
1610 fn open_remote_buffer_internal(
1611 &mut self,
1612 path: &Arc<Path>,
1613 worktree: &ModelHandle<Worktree>,
1614 cx: &mut ModelContext<Self>,
1615 ) -> Task<Result<ModelHandle<Buffer>>> {
1616 let rpc = self.client.clone();
1617 let project_id = self.remote_id().unwrap();
1618 let remote_worktree_id = worktree.read(cx).id();
1619 let path = path.clone();
1620 let path_string = path.to_string_lossy().to_string();
1621 cx.spawn(|this, mut cx| async move {
1622 let response = rpc
1623 .request(proto::OpenBufferByPath {
1624 project_id,
1625 worktree_id: remote_worktree_id.to_proto(),
1626 path: path_string,
1627 })
1628 .await?;
1629 this.update(&mut cx, |this, cx| {
1630 this.wait_for_remote_buffer(response.buffer_id, cx)
1631 })
1632 .await
1633 })
1634 }
1635
1636 /// LanguageServerName is owned, because it is inserted into a map
1637 fn open_local_buffer_via_lsp(
1638 &mut self,
1639 abs_path: lsp::Url,
1640 language_server_id: LanguageServerId,
1641 language_server_name: LanguageServerName,
1642 cx: &mut ModelContext<Self>,
1643 ) -> Task<Result<ModelHandle<Buffer>>> {
1644 cx.spawn(|this, mut cx| async move {
1645 let abs_path = abs_path
1646 .to_file_path()
1647 .map_err(|_| anyhow!("can't convert URI to path"))?;
1648 let (worktree, relative_path) = if let Some(result) =
1649 this.read_with(&cx, |this, cx| this.find_local_worktree(&abs_path, cx))
1650 {
1651 result
1652 } else {
1653 let worktree = this
1654 .update(&mut cx, |this, cx| {
1655 this.create_local_worktree(&abs_path, false, cx)
1656 })
1657 .await?;
1658 this.update(&mut cx, |this, cx| {
1659 this.language_server_ids.insert(
1660 (worktree.read(cx).id(), language_server_name),
1661 language_server_id,
1662 );
1663 });
1664 (worktree, PathBuf::new())
1665 };
1666
1667 let project_path = ProjectPath {
1668 worktree_id: worktree.read_with(&cx, |worktree, _| worktree.id()),
1669 path: relative_path.into(),
1670 };
1671 this.update(&mut cx, |this, cx| this.open_buffer(project_path, cx))
1672 .await
1673 })
1674 }
1675
1676 pub fn open_buffer_by_id(
1677 &mut self,
1678 id: u64,
1679 cx: &mut ModelContext<Self>,
1680 ) -> Task<Result<ModelHandle<Buffer>>> {
1681 if let Some(buffer) = self.buffer_for_id(id, cx) {
1682 Task::ready(Ok(buffer))
1683 } else if self.is_local() {
1684 Task::ready(Err(anyhow!("buffer {} does not exist", id)))
1685 } else if let Some(project_id) = self.remote_id() {
1686 let request = self
1687 .client
1688 .request(proto::OpenBufferById { project_id, id });
1689 cx.spawn(|this, mut cx| async move {
1690 let buffer_id = request.await?.buffer_id;
1691 this.update(&mut cx, |this, cx| {
1692 this.wait_for_remote_buffer(buffer_id, cx)
1693 })
1694 .await
1695 })
1696 } else {
1697 Task::ready(Err(anyhow!("cannot open buffer while disconnected")))
1698 }
1699 }
1700
1701 pub fn save_buffers(
1702 &self,
1703 buffers: HashSet<ModelHandle<Buffer>>,
1704 cx: &mut ModelContext<Self>,
1705 ) -> Task<Result<()>> {
1706 cx.spawn(|this, mut cx| async move {
1707 let save_tasks = buffers
1708 .into_iter()
1709 .map(|buffer| this.update(&mut cx, |this, cx| this.save_buffer(buffer, cx)));
1710 try_join_all(save_tasks).await?;
1711 Ok(())
1712 })
1713 }
1714
1715 pub fn save_buffer(
1716 &self,
1717 buffer: ModelHandle<Buffer>,
1718 cx: &mut ModelContext<Self>,
1719 ) -> Task<Result<()>> {
1720 let Some(file) = File::from_dyn(buffer.read(cx).file()) else {
1721 return Task::ready(Err(anyhow!("buffer doesn't have a file")));
1722 };
1723 let worktree = file.worktree.clone();
1724 let path = file.path.clone();
1725 worktree.update(cx, |worktree, cx| match worktree {
1726 Worktree::Local(worktree) => worktree.save_buffer(buffer, path, false, cx),
1727 Worktree::Remote(worktree) => worktree.save_buffer(buffer, cx),
1728 })
1729 }
1730
1731 pub fn save_buffer_as(
1732 &mut self,
1733 buffer: ModelHandle<Buffer>,
1734 abs_path: PathBuf,
1735 cx: &mut ModelContext<Self>,
1736 ) -> Task<Result<()>> {
1737 let worktree_task = self.find_or_create_local_worktree(&abs_path, true, cx);
1738 let old_file = File::from_dyn(buffer.read(cx).file())
1739 .filter(|f| f.is_local())
1740 .cloned();
1741 cx.spawn(|this, mut cx| async move {
1742 if let Some(old_file) = &old_file {
1743 this.update(&mut cx, |this, cx| {
1744 this.unregister_buffer_from_language_servers(&buffer, old_file, cx);
1745 });
1746 }
1747 let (worktree, path) = worktree_task.await?;
1748 worktree
1749 .update(&mut cx, |worktree, cx| match worktree {
1750 Worktree::Local(worktree) => {
1751 worktree.save_buffer(buffer.clone(), path.into(), true, cx)
1752 }
1753 Worktree::Remote(_) => panic!("cannot remote buffers as new files"),
1754 })
1755 .await?;
1756 this.update(&mut cx, |this, cx| {
1757 this.detect_language_for_buffer(&buffer, cx);
1758 this.register_buffer_with_language_servers(&buffer, cx);
1759 });
1760 Ok(())
1761 })
1762 }
1763
1764 pub fn get_open_buffer(
1765 &mut self,
1766 path: &ProjectPath,
1767 cx: &mut ModelContext<Self>,
1768 ) -> Option<ModelHandle<Buffer>> {
1769 let worktree = self.worktree_for_id(path.worktree_id, cx)?;
1770 self.opened_buffers.values().find_map(|buffer| {
1771 let buffer = buffer.upgrade(cx)?;
1772 let file = File::from_dyn(buffer.read(cx).file())?;
1773 if file.worktree == worktree && file.path() == &path.path {
1774 Some(buffer)
1775 } else {
1776 None
1777 }
1778 })
1779 }
1780
1781 fn register_buffer(
1782 &mut self,
1783 buffer: &ModelHandle<Buffer>,
1784 cx: &mut ModelContext<Self>,
1785 ) -> Result<()> {
1786 self.request_buffer_diff_recalculation(buffer, cx);
1787 buffer.update(cx, |buffer, _| {
1788 buffer.set_language_registry(self.languages.clone())
1789 });
1790
1791 let remote_id = buffer.read(cx).remote_id();
1792 let is_remote = self.is_remote();
1793 let open_buffer = if is_remote || self.is_shared() {
1794 OpenBuffer::Strong(buffer.clone())
1795 } else {
1796 OpenBuffer::Weak(buffer.downgrade())
1797 };
1798
1799 match self.opened_buffers.entry(remote_id) {
1800 hash_map::Entry::Vacant(entry) => {
1801 entry.insert(open_buffer);
1802 }
1803 hash_map::Entry::Occupied(mut entry) => {
1804 if let OpenBuffer::Operations(operations) = entry.get_mut() {
1805 buffer.update(cx, |b, cx| b.apply_ops(operations.drain(..), cx))?;
1806 } else if entry.get().upgrade(cx).is_some() {
1807 if is_remote {
1808 return Ok(());
1809 } else {
1810 debug_panic!("buffer {} was already registered", remote_id);
1811 Err(anyhow!("buffer {} was already registered", remote_id))?;
1812 }
1813 }
1814 entry.insert(open_buffer);
1815 }
1816 }
1817 cx.subscribe(buffer, |this, buffer, event, cx| {
1818 this.on_buffer_event(buffer, event, cx);
1819 })
1820 .detach();
1821
1822 if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
1823 if file.is_local {
1824 self.local_buffer_ids_by_path.insert(
1825 ProjectPath {
1826 worktree_id: file.worktree_id(cx),
1827 path: file.path.clone(),
1828 },
1829 remote_id,
1830 );
1831
1832 self.local_buffer_ids_by_entry_id
1833 .insert(file.entry_id, remote_id);
1834 }
1835 }
1836
1837 self.detect_language_for_buffer(buffer, cx);
1838 self.register_buffer_with_language_servers(buffer, cx);
1839 self.register_buffer_with_copilot(buffer, cx);
1840 cx.observe_release(buffer, |this, buffer, cx| {
1841 if let Some(file) = File::from_dyn(buffer.file()) {
1842 if file.is_local() {
1843 let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
1844 for server in this.language_servers_for_buffer(buffer, cx) {
1845 server
1846 .1
1847 .notify::<lsp::notification::DidCloseTextDocument>(
1848 lsp::DidCloseTextDocumentParams {
1849 text_document: lsp::TextDocumentIdentifier::new(uri.clone()),
1850 },
1851 )
1852 .log_err();
1853 }
1854 }
1855 }
1856 })
1857 .detach();
1858
1859 *self.opened_buffer.0.borrow_mut() = ();
1860 Ok(())
1861 }
1862
1863 fn register_buffer_with_language_servers(
1864 &mut self,
1865 buffer_handle: &ModelHandle<Buffer>,
1866 cx: &mut ModelContext<Self>,
1867 ) {
1868 let buffer = buffer_handle.read(cx);
1869 let buffer_id = buffer.remote_id();
1870
1871 if let Some(file) = File::from_dyn(buffer.file()) {
1872 if !file.is_local() {
1873 return;
1874 }
1875
1876 let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
1877 let initial_snapshot = buffer.text_snapshot();
1878 let language = buffer.language().cloned();
1879 let worktree_id = file.worktree_id(cx);
1880
1881 if let Some(local_worktree) = file.worktree.read(cx).as_local() {
1882 for (server_id, diagnostics) in local_worktree.diagnostics_for_path(file.path()) {
1883 self.update_buffer_diagnostics(buffer_handle, server_id, None, diagnostics, cx)
1884 .log_err();
1885 }
1886 }
1887
1888 if let Some(language) = language {
1889 for adapter in language.lsp_adapters() {
1890 let language_id = adapter.language_ids.get(language.name().as_ref()).cloned();
1891 let server = self
1892 .language_server_ids
1893 .get(&(worktree_id, adapter.name.clone()))
1894 .and_then(|id| self.language_servers.get(id))
1895 .and_then(|server_state| {
1896 if let LanguageServerState::Running { server, .. } = server_state {
1897 Some(server.clone())
1898 } else {
1899 None
1900 }
1901 });
1902 let server = match server {
1903 Some(server) => server,
1904 None => continue,
1905 };
1906
1907 server
1908 .notify::<lsp::notification::DidOpenTextDocument>(
1909 lsp::DidOpenTextDocumentParams {
1910 text_document: lsp::TextDocumentItem::new(
1911 uri.clone(),
1912 language_id.unwrap_or_default(),
1913 0,
1914 initial_snapshot.text(),
1915 ),
1916 },
1917 )
1918 .log_err();
1919
1920 buffer_handle.update(cx, |buffer, cx| {
1921 buffer.set_completion_triggers(
1922 server
1923 .capabilities()
1924 .completion_provider
1925 .as_ref()
1926 .and_then(|provider| provider.trigger_characters.clone())
1927 .unwrap_or_default(),
1928 cx,
1929 );
1930 });
1931
1932 let snapshot = LspBufferSnapshot {
1933 version: 0,
1934 snapshot: initial_snapshot.clone(),
1935 };
1936 self.buffer_snapshots
1937 .entry(buffer_id)
1938 .or_default()
1939 .insert(server.server_id(), vec![snapshot]);
1940 }
1941 }
1942 }
1943 }
1944
1945 fn unregister_buffer_from_language_servers(
1946 &mut self,
1947 buffer: &ModelHandle<Buffer>,
1948 old_file: &File,
1949 cx: &mut ModelContext<Self>,
1950 ) {
1951 let old_path = match old_file.as_local() {
1952 Some(local) => local.abs_path(cx),
1953 None => return,
1954 };
1955
1956 buffer.update(cx, |buffer, cx| {
1957 let worktree_id = old_file.worktree_id(cx);
1958 let ids = &self.language_server_ids;
1959
1960 let language = buffer.language().cloned();
1961 let adapters = language.iter().flat_map(|language| language.lsp_adapters());
1962 for &server_id in adapters.flat_map(|a| ids.get(&(worktree_id, a.name.clone()))) {
1963 buffer.update_diagnostics(server_id, Default::default(), cx);
1964 }
1965
1966 self.buffer_snapshots.remove(&buffer.remote_id());
1967 let file_url = lsp::Url::from_file_path(old_path).unwrap();
1968 for (_, language_server) in self.language_servers_for_buffer(buffer, cx) {
1969 language_server
1970 .notify::<lsp::notification::DidCloseTextDocument>(
1971 lsp::DidCloseTextDocumentParams {
1972 text_document: lsp::TextDocumentIdentifier::new(file_url.clone()),
1973 },
1974 )
1975 .log_err();
1976 }
1977 });
1978 }
1979
1980 fn register_buffer_with_copilot(
1981 &self,
1982 buffer_handle: &ModelHandle<Buffer>,
1983 cx: &mut ModelContext<Self>,
1984 ) {
1985 if let Some(copilot) = Copilot::global(cx) {
1986 copilot.update(cx, |copilot, cx| copilot.register_buffer(buffer_handle, cx));
1987 }
1988 }
1989
1990 async fn send_buffer_ordered_messages(
1991 this: WeakModelHandle<Self>,
1992 rx: UnboundedReceiver<BufferOrderedMessage>,
1993 mut cx: AsyncAppContext,
1994 ) -> Option<()> {
1995 const MAX_BATCH_SIZE: usize = 128;
1996
1997 let mut operations_by_buffer_id = HashMap::default();
1998 async fn flush_operations(
1999 this: &ModelHandle<Project>,
2000 operations_by_buffer_id: &mut HashMap<u64, Vec<proto::Operation>>,
2001 needs_resync_with_host: &mut bool,
2002 is_local: bool,
2003 cx: &AsyncAppContext,
2004 ) {
2005 for (buffer_id, operations) in operations_by_buffer_id.drain() {
2006 let request = this.read_with(cx, |this, _| {
2007 let project_id = this.remote_id()?;
2008 Some(this.client.request(proto::UpdateBuffer {
2009 buffer_id,
2010 project_id,
2011 operations,
2012 }))
2013 });
2014 if let Some(request) = request {
2015 if request.await.is_err() && !is_local {
2016 *needs_resync_with_host = true;
2017 break;
2018 }
2019 }
2020 }
2021 }
2022
2023 let mut needs_resync_with_host = false;
2024 let mut changes = rx.ready_chunks(MAX_BATCH_SIZE);
2025
2026 while let Some(changes) = changes.next().await {
2027 let this = this.upgrade(&mut cx)?;
2028 let is_local = this.read_with(&cx, |this, _| this.is_local());
2029
2030 for change in changes {
2031 match change {
2032 BufferOrderedMessage::Operation {
2033 buffer_id,
2034 operation,
2035 } => {
2036 if needs_resync_with_host {
2037 continue;
2038 }
2039
2040 operations_by_buffer_id
2041 .entry(buffer_id)
2042 .or_insert(Vec::new())
2043 .push(operation);
2044 }
2045
2046 BufferOrderedMessage::Resync => {
2047 operations_by_buffer_id.clear();
2048 if this
2049 .update(&mut cx, |this, cx| this.synchronize_remote_buffers(cx))
2050 .await
2051 .is_ok()
2052 {
2053 needs_resync_with_host = false;
2054 }
2055 }
2056
2057 BufferOrderedMessage::LanguageServerUpdate {
2058 language_server_id,
2059 message,
2060 } => {
2061 flush_operations(
2062 &this,
2063 &mut operations_by_buffer_id,
2064 &mut needs_resync_with_host,
2065 is_local,
2066 &cx,
2067 )
2068 .await;
2069
2070 this.read_with(&cx, |this, _| {
2071 if let Some(project_id) = this.remote_id() {
2072 this.client
2073 .send(proto::UpdateLanguageServer {
2074 project_id,
2075 language_server_id: language_server_id.0 as u64,
2076 variant: Some(message),
2077 })
2078 .log_err();
2079 }
2080 });
2081 }
2082 }
2083 }
2084
2085 flush_operations(
2086 &this,
2087 &mut operations_by_buffer_id,
2088 &mut needs_resync_with_host,
2089 is_local,
2090 &cx,
2091 )
2092 .await;
2093 }
2094
2095 None
2096 }
2097
2098 fn on_buffer_event(
2099 &mut self,
2100 buffer: ModelHandle<Buffer>,
2101 event: &BufferEvent,
2102 cx: &mut ModelContext<Self>,
2103 ) -> Option<()> {
2104 if matches!(
2105 event,
2106 BufferEvent::Edited { .. } | BufferEvent::Reloaded | BufferEvent::DiffBaseChanged
2107 ) {
2108 self.request_buffer_diff_recalculation(&buffer, cx);
2109 }
2110
2111 match event {
2112 BufferEvent::Operation(operation) => {
2113 self.buffer_ordered_messages_tx
2114 .unbounded_send(BufferOrderedMessage::Operation {
2115 buffer_id: buffer.read(cx).remote_id(),
2116 operation: language::proto::serialize_operation(operation),
2117 })
2118 .ok();
2119 }
2120
2121 BufferEvent::Edited { .. } => {
2122 let buffer = buffer.read(cx);
2123 let file = File::from_dyn(buffer.file())?;
2124 let abs_path = file.as_local()?.abs_path(cx);
2125 let uri = lsp::Url::from_file_path(abs_path).unwrap();
2126 let next_snapshot = buffer.text_snapshot();
2127
2128 let language_servers: Vec<_> = self
2129 .language_servers_for_buffer(buffer, cx)
2130 .map(|i| i.1.clone())
2131 .collect();
2132
2133 for language_server in language_servers {
2134 let language_server = language_server.clone();
2135
2136 let buffer_snapshots = self
2137 .buffer_snapshots
2138 .get_mut(&buffer.remote_id())
2139 .and_then(|m| m.get_mut(&language_server.server_id()))?;
2140 let previous_snapshot = buffer_snapshots.last()?;
2141 let next_version = previous_snapshot.version + 1;
2142
2143 let content_changes = buffer
2144 .edits_since::<(PointUtf16, usize)>(previous_snapshot.snapshot.version())
2145 .map(|edit| {
2146 let edit_start = edit.new.start.0;
2147 let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0);
2148 let new_text = next_snapshot
2149 .text_for_range(edit.new.start.1..edit.new.end.1)
2150 .collect();
2151 lsp::TextDocumentContentChangeEvent {
2152 range: Some(lsp::Range::new(
2153 point_to_lsp(edit_start),
2154 point_to_lsp(edit_end),
2155 )),
2156 range_length: None,
2157 text: new_text,
2158 }
2159 })
2160 .collect();
2161
2162 buffer_snapshots.push(LspBufferSnapshot {
2163 version: next_version,
2164 snapshot: next_snapshot.clone(),
2165 });
2166
2167 language_server
2168 .notify::<lsp::notification::DidChangeTextDocument>(
2169 lsp::DidChangeTextDocumentParams {
2170 text_document: lsp::VersionedTextDocumentIdentifier::new(
2171 uri.clone(),
2172 next_version,
2173 ),
2174 content_changes,
2175 },
2176 )
2177 .log_err();
2178 }
2179 }
2180
2181 BufferEvent::Saved => {
2182 let file = File::from_dyn(buffer.read(cx).file())?;
2183 let worktree_id = file.worktree_id(cx);
2184 let abs_path = file.as_local()?.abs_path(cx);
2185 let text_document = lsp::TextDocumentIdentifier {
2186 uri: lsp::Url::from_file_path(abs_path).unwrap(),
2187 };
2188
2189 for (_, _, server) in self.language_servers_for_worktree(worktree_id) {
2190 server
2191 .notify::<lsp::notification::DidSaveTextDocument>(
2192 lsp::DidSaveTextDocumentParams {
2193 text_document: text_document.clone(),
2194 text: None,
2195 },
2196 )
2197 .log_err();
2198 }
2199
2200 let language_server_ids = self.language_server_ids_for_buffer(buffer.read(cx), cx);
2201 for language_server_id in language_server_ids {
2202 if let Some(LanguageServerState::Running {
2203 adapter,
2204 simulate_disk_based_diagnostics_completion,
2205 ..
2206 }) = self.language_servers.get_mut(&language_server_id)
2207 {
2208 // After saving a buffer using a language server that doesn't provide
2209 // a disk-based progress token, kick off a timer that will reset every
2210 // time the buffer is saved. If the timer eventually fires, simulate
2211 // disk-based diagnostics being finished so that other pieces of UI
2212 // (e.g., project diagnostics view, diagnostic status bar) can update.
2213 // We don't emit an event right away because the language server might take
2214 // some time to publish diagnostics.
2215 if adapter.disk_based_diagnostics_progress_token.is_none() {
2216 const DISK_BASED_DIAGNOSTICS_DEBOUNCE: Duration =
2217 Duration::from_secs(1);
2218
2219 let task = cx.spawn_weak(|this, mut cx| async move {
2220 cx.background().timer(DISK_BASED_DIAGNOSTICS_DEBOUNCE).await;
2221 if let Some(this) = this.upgrade(&cx) {
2222 this.update(&mut cx, |this, cx| {
2223 this.disk_based_diagnostics_finished(
2224 language_server_id,
2225 cx,
2226 );
2227 this.buffer_ordered_messages_tx
2228 .unbounded_send(
2229 BufferOrderedMessage::LanguageServerUpdate {
2230 language_server_id,
2231 message:proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(Default::default())
2232 },
2233 )
2234 .ok();
2235 });
2236 }
2237 });
2238 *simulate_disk_based_diagnostics_completion = Some(task);
2239 }
2240 }
2241 }
2242 }
2243
2244 _ => {}
2245 }
2246
2247 None
2248 }
2249
2250 fn request_buffer_diff_recalculation(
2251 &mut self,
2252 buffer: &ModelHandle<Buffer>,
2253 cx: &mut ModelContext<Self>,
2254 ) {
2255 self.buffers_needing_diff.insert(buffer.downgrade());
2256 let first_insertion = self.buffers_needing_diff.len() == 1;
2257
2258 let settings = settings::get::<ProjectSettings>(cx);
2259 let delay = if let Some(delay) = settings.git.gutter_debounce {
2260 delay
2261 } else {
2262 if first_insertion {
2263 let this = cx.weak_handle();
2264 cx.defer(move |cx| {
2265 if let Some(this) = this.upgrade(cx) {
2266 this.update(cx, |this, cx| {
2267 this.recalculate_buffer_diffs(cx).detach();
2268 });
2269 }
2270 });
2271 }
2272 return;
2273 };
2274
2275 const MIN_DELAY: u64 = 50;
2276 let delay = delay.max(MIN_DELAY);
2277 let duration = Duration::from_millis(delay);
2278
2279 self.git_diff_debouncer
2280 .fire_new(duration, cx, move |this, cx| {
2281 this.recalculate_buffer_diffs(cx)
2282 });
2283 }
2284
2285 fn recalculate_buffer_diffs(&mut self, cx: &mut ModelContext<Self>) -> Task<()> {
2286 cx.spawn(|this, mut cx| async move {
2287 let buffers: Vec<_> = this.update(&mut cx, |this, _| {
2288 this.buffers_needing_diff.drain().collect()
2289 });
2290
2291 let tasks: Vec<_> = this.update(&mut cx, |_, cx| {
2292 buffers
2293 .iter()
2294 .filter_map(|buffer| {
2295 let buffer = buffer.upgrade(cx)?;
2296 buffer.update(cx, |buffer, cx| buffer.git_diff_recalc(cx))
2297 })
2298 .collect()
2299 });
2300
2301 futures::future::join_all(tasks).await;
2302
2303 this.update(&mut cx, |this, cx| {
2304 if !this.buffers_needing_diff.is_empty() {
2305 this.recalculate_buffer_diffs(cx).detach();
2306 } else {
2307 // TODO: Would a `ModelContext<Project>.notify()` suffice here?
2308 for buffer in buffers {
2309 if let Some(buffer) = buffer.upgrade(cx) {
2310 buffer.update(cx, |_, cx| cx.notify());
2311 }
2312 }
2313 }
2314 });
2315 })
2316 }
2317
2318 fn language_servers_for_worktree(
2319 &self,
2320 worktree_id: WorktreeId,
2321 ) -> impl Iterator<Item = (&Arc<CachedLspAdapter>, &Arc<Language>, &Arc<LanguageServer>)> {
2322 self.language_server_ids
2323 .iter()
2324 .filter_map(move |((language_server_worktree_id, _), id)| {
2325 if *language_server_worktree_id == worktree_id {
2326 if let Some(LanguageServerState::Running {
2327 adapter,
2328 language,
2329 server,
2330 ..
2331 }) = self.language_servers.get(id)
2332 {
2333 return Some((adapter, language, server));
2334 }
2335 }
2336 None
2337 })
2338 }
2339
2340 fn maintain_buffer_languages(
2341 languages: Arc<LanguageRegistry>,
2342 cx: &mut ModelContext<Project>,
2343 ) -> Task<()> {
2344 let mut subscription = languages.subscribe();
2345 let mut prev_reload_count = languages.reload_count();
2346 cx.spawn_weak(|project, mut cx| async move {
2347 while let Some(()) = subscription.next().await {
2348 if let Some(project) = project.upgrade(&cx) {
2349 // If the language registry has been reloaded, then remove and
2350 // re-assign the languages on all open buffers.
2351 let reload_count = languages.reload_count();
2352 if reload_count > prev_reload_count {
2353 prev_reload_count = reload_count;
2354 project.update(&mut cx, |this, cx| {
2355 let buffers = this
2356 .opened_buffers
2357 .values()
2358 .filter_map(|b| b.upgrade(cx))
2359 .collect::<Vec<_>>();
2360 for buffer in buffers {
2361 if let Some(f) = File::from_dyn(buffer.read(cx).file()).cloned() {
2362 this.unregister_buffer_from_language_servers(&buffer, &f, cx);
2363 buffer.update(cx, |buffer, cx| buffer.set_language(None, cx));
2364 }
2365 }
2366 });
2367 }
2368
2369 project.update(&mut cx, |project, cx| {
2370 let mut plain_text_buffers = Vec::new();
2371 let mut buffers_with_unknown_injections = Vec::new();
2372 for buffer in project.opened_buffers.values() {
2373 if let Some(handle) = buffer.upgrade(cx) {
2374 let buffer = &handle.read(cx);
2375 if buffer.language().is_none()
2376 || buffer.language() == Some(&*language::PLAIN_TEXT)
2377 {
2378 plain_text_buffers.push(handle);
2379 } else if buffer.contains_unknown_injections() {
2380 buffers_with_unknown_injections.push(handle);
2381 }
2382 }
2383 }
2384
2385 for buffer in plain_text_buffers {
2386 project.detect_language_for_buffer(&buffer, cx);
2387 project.register_buffer_with_language_servers(&buffer, cx);
2388 }
2389
2390 for buffer in buffers_with_unknown_injections {
2391 buffer.update(cx, |buffer, cx| buffer.reparse(cx));
2392 }
2393 });
2394 }
2395 }
2396 })
2397 }
2398
2399 fn maintain_workspace_config(
2400 languages: Arc<LanguageRegistry>,
2401 cx: &mut ModelContext<Project>,
2402 ) -> Task<()> {
2403 let (mut settings_changed_tx, mut settings_changed_rx) = watch::channel();
2404 let _ = postage::stream::Stream::try_recv(&mut settings_changed_rx);
2405
2406 let settings_observation = cx.observe_global::<SettingsStore, _>(move |_, _| {
2407 *settings_changed_tx.borrow_mut() = ();
2408 });
2409 cx.spawn_weak(|this, mut cx| async move {
2410 while let Some(_) = settings_changed_rx.next().await {
2411 let workspace_config = cx.update(|cx| languages.workspace_configuration(cx)).await;
2412 if let Some(this) = this.upgrade(&cx) {
2413 this.read_with(&cx, |this, _| {
2414 for server_state in this.language_servers.values() {
2415 if let LanguageServerState::Running { server, .. } = server_state {
2416 server
2417 .notify::<lsp::notification::DidChangeConfiguration>(
2418 lsp::DidChangeConfigurationParams {
2419 settings: workspace_config.clone(),
2420 },
2421 )
2422 .ok();
2423 }
2424 }
2425 })
2426 } else {
2427 break;
2428 }
2429 }
2430
2431 drop(settings_observation);
2432 })
2433 }
2434
2435 fn detect_language_for_buffer(
2436 &mut self,
2437 buffer_handle: &ModelHandle<Buffer>,
2438 cx: &mut ModelContext<Self>,
2439 ) -> Option<()> {
2440 // If the buffer has a language, set it and start the language server if we haven't already.
2441 let buffer = buffer_handle.read(cx);
2442 let full_path = buffer.file()?.full_path(cx);
2443 let content = buffer.as_rope();
2444 let new_language = self
2445 .languages
2446 .language_for_file(&full_path, Some(content))
2447 .now_or_never()?
2448 .ok()?;
2449 self.set_language_for_buffer(buffer_handle, new_language, cx);
2450 None
2451 }
2452
2453 pub fn set_language_for_buffer(
2454 &mut self,
2455 buffer: &ModelHandle<Buffer>,
2456 new_language: Arc<Language>,
2457 cx: &mut ModelContext<Self>,
2458 ) {
2459 buffer.update(cx, |buffer, cx| {
2460 if buffer.language().map_or(true, |old_language| {
2461 !Arc::ptr_eq(old_language, &new_language)
2462 }) {
2463 buffer.set_language(Some(new_language.clone()), cx);
2464 }
2465 });
2466
2467 if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
2468 let worktree = file.worktree.clone();
2469 if let Some(tree) = worktree.read(cx).as_local() {
2470 self.start_language_servers(&worktree, tree.abs_path().clone(), new_language, cx);
2471 }
2472 }
2473 }
2474
2475 fn start_language_servers(
2476 &mut self,
2477 worktree: &ModelHandle<Worktree>,
2478 worktree_path: Arc<Path>,
2479 language: Arc<Language>,
2480 cx: &mut ModelContext<Self>,
2481 ) {
2482 let root_file = worktree.update(cx, |tree, cx| tree.root_file(cx));
2483 let settings = language_settings(Some(&language), root_file.map(|f| f as _).as_ref(), cx);
2484 if !settings.enable_language_server {
2485 return;
2486 }
2487
2488 let worktree_id = worktree.read(cx).id();
2489 for adapter in language.lsp_adapters() {
2490 self.start_language_server(
2491 worktree_id,
2492 worktree_path.clone(),
2493 adapter.clone(),
2494 language.clone(),
2495 cx,
2496 );
2497 }
2498 }
2499
2500 fn start_language_server(
2501 &mut self,
2502 worktree_id: WorktreeId,
2503 worktree_path: Arc<Path>,
2504 adapter: Arc<CachedLspAdapter>,
2505 language: Arc<Language>,
2506 cx: &mut ModelContext<Self>,
2507 ) {
2508 let key = (worktree_id, adapter.name.clone());
2509 if self.language_server_ids.contains_key(&key) {
2510 return;
2511 }
2512
2513 let pending_server = match self.languages.create_pending_language_server(
2514 language.clone(),
2515 adapter.clone(),
2516 worktree_path,
2517 ProjectLspAdapterDelegate::new(self, cx),
2518 cx,
2519 ) {
2520 Some(pending_server) => pending_server,
2521 None => return,
2522 };
2523
2524 let project_settings = settings::get::<ProjectSettings>(cx);
2525 let lsp = project_settings.lsp.get(&adapter.name.0);
2526 let override_options = lsp.map(|s| s.initialization_options.clone()).flatten();
2527
2528 let mut initialization_options = adapter.initialization_options.clone();
2529 match (&mut initialization_options, override_options) {
2530 (Some(initialization_options), Some(override_options)) => {
2531 merge_json_value_into(override_options, initialization_options);
2532 }
2533 (None, override_options) => initialization_options = override_options,
2534 _ => {}
2535 }
2536
2537 let server_id = pending_server.server_id;
2538 let container_dir = pending_server.container_dir.clone();
2539 let state = LanguageServerState::Starting({
2540 let adapter = adapter.clone();
2541 let server_name = adapter.name.0.clone();
2542 let languages = self.languages.clone();
2543 let language = language.clone();
2544 let key = key.clone();
2545
2546 cx.spawn_weak(|this, mut cx| async move {
2547 let result = Self::setup_and_insert_language_server(
2548 this,
2549 initialization_options,
2550 pending_server,
2551 adapter.clone(),
2552 languages,
2553 language.clone(),
2554 server_id,
2555 key,
2556 &mut cx,
2557 )
2558 .await;
2559
2560 match result {
2561 Ok(server) => server,
2562
2563 Err(err) => {
2564 log::error!("failed to start language server {:?}: {}", server_name, err);
2565
2566 if let Some(this) = this.upgrade(&cx) {
2567 if let Some(container_dir) = container_dir {
2568 let installation_test_binary = adapter
2569 .installation_test_binary(container_dir.to_path_buf())
2570 .await;
2571
2572 this.update(&mut cx, |_, cx| {
2573 Self::check_errored_server(
2574 language,
2575 adapter,
2576 server_id,
2577 installation_test_binary,
2578 cx,
2579 )
2580 });
2581 }
2582 }
2583
2584 None
2585 }
2586 }
2587 })
2588 });
2589
2590 self.language_servers.insert(server_id, state);
2591 self.language_server_ids.insert(key, server_id);
2592 }
2593
2594 fn reinstall_language_server(
2595 &mut self,
2596 language: Arc<Language>,
2597 adapter: Arc<CachedLspAdapter>,
2598 server_id: LanguageServerId,
2599 cx: &mut ModelContext<Self>,
2600 ) -> Option<Task<()>> {
2601 log::info!("beginning to reinstall server");
2602
2603 let existing_server = match self.language_servers.remove(&server_id) {
2604 Some(LanguageServerState::Running { server, .. }) => Some(server),
2605 _ => None,
2606 };
2607
2608 for worktree in &self.worktrees {
2609 if let Some(worktree) = worktree.upgrade(cx) {
2610 let key = (worktree.read(cx).id(), adapter.name.clone());
2611 self.language_server_ids.remove(&key);
2612 }
2613 }
2614
2615 Some(cx.spawn(move |this, mut cx| async move {
2616 if let Some(task) = existing_server.and_then(|server| server.shutdown()) {
2617 log::info!("shutting down existing server");
2618 task.await;
2619 }
2620
2621 // TODO: This is race-safe with regards to preventing new instances from
2622 // starting while deleting, but existing instances in other projects are going
2623 // to be very confused and messed up
2624 this.update(&mut cx, |this, cx| {
2625 this.languages.delete_server_container(adapter.clone(), cx)
2626 })
2627 .await;
2628
2629 this.update(&mut cx, |this, mut cx| {
2630 let worktrees = this.worktrees.clone();
2631 for worktree in worktrees {
2632 let worktree = match worktree.upgrade(cx) {
2633 Some(worktree) => worktree.read(cx),
2634 None => continue,
2635 };
2636 let worktree_id = worktree.id();
2637 let root_path = worktree.abs_path();
2638
2639 this.start_language_server(
2640 worktree_id,
2641 root_path,
2642 adapter.clone(),
2643 language.clone(),
2644 &mut cx,
2645 );
2646 }
2647 })
2648 }))
2649 }
2650
2651 async fn setup_and_insert_language_server(
2652 this: WeakModelHandle<Self>,
2653 initialization_options: Option<serde_json::Value>,
2654 pending_server: PendingLanguageServer,
2655 adapter: Arc<CachedLspAdapter>,
2656 languages: Arc<LanguageRegistry>,
2657 language: Arc<Language>,
2658 server_id: LanguageServerId,
2659 key: (WorktreeId, LanguageServerName),
2660 cx: &mut AsyncAppContext,
2661 ) -> Result<Option<Arc<LanguageServer>>> {
2662 let setup = Self::setup_pending_language_server(
2663 this,
2664 initialization_options,
2665 pending_server,
2666 adapter.clone(),
2667 languages,
2668 server_id,
2669 cx,
2670 );
2671
2672 let language_server = match setup.await? {
2673 Some(language_server) => language_server,
2674 None => return Ok(None),
2675 };
2676
2677 let this = match this.upgrade(cx) {
2678 Some(this) => this,
2679 None => return Err(anyhow!("failed to upgrade project handle")),
2680 };
2681
2682 this.update(cx, |this, cx| {
2683 this.insert_newly_running_language_server(
2684 language,
2685 adapter,
2686 language_server.clone(),
2687 server_id,
2688 key,
2689 cx,
2690 )
2691 })?;
2692
2693 Ok(Some(language_server))
2694 }
2695
2696 async fn setup_pending_language_server(
2697 this: WeakModelHandle<Self>,
2698 initialization_options: Option<serde_json::Value>,
2699 pending_server: PendingLanguageServer,
2700 adapter: Arc<CachedLspAdapter>,
2701 languages: Arc<LanguageRegistry>,
2702 server_id: LanguageServerId,
2703 cx: &mut AsyncAppContext,
2704 ) -> Result<Option<Arc<LanguageServer>>> {
2705 let workspace_config = cx.update(|cx| languages.workspace_configuration(cx)).await;
2706
2707 let language_server = match pending_server.task.await? {
2708 Some(server) => server.initialize(initialization_options).await?,
2709 None => return Ok(None),
2710 };
2711
2712 language_server
2713 .on_notification::<lsp::notification::LogMessage, _>({
2714 move |params, mut cx| {
2715 if let Some(this) = this.upgrade(&cx) {
2716 this.update(&mut cx, |_, cx| {
2717 cx.emit(Event::LanguageServerLog(server_id, params.message))
2718 });
2719 }
2720 }
2721 })
2722 .detach();
2723
2724 language_server
2725 .on_notification::<lsp::notification::PublishDiagnostics, _>({
2726 let adapter = adapter.clone();
2727 move |mut params, cx| {
2728 let this = this;
2729 let adapter = adapter.clone();
2730 cx.spawn(|mut cx| async move {
2731 adapter.process_diagnostics(&mut params).await;
2732 if let Some(this) = this.upgrade(&cx) {
2733 this.update(&mut cx, |this, cx| {
2734 this.update_diagnostics(
2735 server_id,
2736 params,
2737 &adapter.disk_based_diagnostic_sources,
2738 cx,
2739 )
2740 .log_err();
2741 });
2742 }
2743 })
2744 .detach();
2745 }
2746 })
2747 .detach();
2748
2749 language_server
2750 .on_request::<lsp::request::WorkspaceConfiguration, _, _>({
2751 let languages = languages.clone();
2752 move |params, mut cx| {
2753 let languages = languages.clone();
2754 async move {
2755 let workspace_config =
2756 cx.update(|cx| languages.workspace_configuration(cx)).await;
2757 Ok(params
2758 .items
2759 .into_iter()
2760 .map(|item| {
2761 if let Some(section) = &item.section {
2762 workspace_config
2763 .get(section)
2764 .cloned()
2765 .unwrap_or(serde_json::Value::Null)
2766 } else {
2767 workspace_config.clone()
2768 }
2769 })
2770 .collect())
2771 }
2772 }
2773 })
2774 .detach();
2775
2776 // Even though we don't have handling for these requests, respond to them to
2777 // avoid stalling any language server like `gopls` which waits for a response
2778 // to these requests when initializing.
2779 language_server
2780 .on_request::<lsp::request::WorkDoneProgressCreate, _, _>(
2781 move |params, mut cx| async move {
2782 if let Some(this) = this.upgrade(&cx) {
2783 this.update(&mut cx, |this, _| {
2784 if let Some(status) = this.language_server_statuses.get_mut(&server_id)
2785 {
2786 if let lsp::NumberOrString::String(token) = params.token {
2787 status.progress_tokens.insert(token);
2788 }
2789 }
2790 });
2791 }
2792 Ok(())
2793 },
2794 )
2795 .detach();
2796 language_server
2797 .on_request::<lsp::request::RegisterCapability, _, _>({
2798 move |params, mut cx| async move {
2799 let this = this
2800 .upgrade(&cx)
2801 .ok_or_else(|| anyhow!("project dropped"))?;
2802 for reg in params.registrations {
2803 if reg.method == "workspace/didChangeWatchedFiles" {
2804 if let Some(options) = reg.register_options {
2805 let options = serde_json::from_value(options)?;
2806 this.update(&mut cx, |this, cx| {
2807 this.on_lsp_did_change_watched_files(server_id, options, cx);
2808 });
2809 }
2810 }
2811 }
2812 Ok(())
2813 }
2814 })
2815 .detach();
2816
2817 language_server
2818 .on_request::<lsp::request::ApplyWorkspaceEdit, _, _>({
2819 let adapter = adapter.clone();
2820 move |params, cx| {
2821 Self::on_lsp_workspace_edit(this, params, server_id, adapter.clone(), cx)
2822 }
2823 })
2824 .detach();
2825
2826 language_server
2827 .on_request::<lsp::request::InlayHintRefreshRequest, _, _>({
2828 let this = this.downgrade();
2829 move |(), mut cx| async move {
2830 let this = this
2831 .upgrade(&cx)
2832 .ok_or_else(|| anyhow!("project dropped"))?;
2833 this.update(&mut cx, |_, cx| {
2834 cx.emit(Event::ReloadInlayHints);
2835 });
2836 Ok(())
2837 }
2838 })
2839 .detach();
2840
2841 let disk_based_diagnostics_progress_token =
2842 adapter.disk_based_diagnostics_progress_token.clone();
2843
2844 language_server
2845 .on_notification::<lsp::notification::Progress, _>(move |params, mut cx| {
2846 if let Some(this) = this.upgrade(&cx) {
2847 this.update(&mut cx, |this, cx| {
2848 this.on_lsp_progress(
2849 params,
2850 server_id,
2851 disk_based_diagnostics_progress_token.clone(),
2852 cx,
2853 );
2854 });
2855 }
2856 })
2857 .detach();
2858
2859 language_server
2860 .notify::<lsp::notification::DidChangeConfiguration>(
2861 lsp::DidChangeConfigurationParams {
2862 settings: workspace_config,
2863 },
2864 )
2865 .ok();
2866
2867 Ok(Some(language_server))
2868 }
2869
2870 fn insert_newly_running_language_server(
2871 &mut self,
2872 language: Arc<Language>,
2873 adapter: Arc<CachedLspAdapter>,
2874 language_server: Arc<LanguageServer>,
2875 server_id: LanguageServerId,
2876 key: (WorktreeId, LanguageServerName),
2877 cx: &mut ModelContext<Self>,
2878 ) -> Result<()> {
2879 // If the language server for this key doesn't match the server id, don't store the
2880 // server. Which will cause it to be dropped, killing the process
2881 if self
2882 .language_server_ids
2883 .get(&key)
2884 .map(|id| id != &server_id)
2885 .unwrap_or(false)
2886 {
2887 return Ok(());
2888 }
2889
2890 // Update language_servers collection with Running variant of LanguageServerState
2891 // indicating that the server is up and running and ready
2892 self.language_servers.insert(
2893 server_id,
2894 LanguageServerState::Running {
2895 adapter: adapter.clone(),
2896 language: language.clone(),
2897 watched_paths: Default::default(),
2898 server: language_server.clone(),
2899 simulate_disk_based_diagnostics_completion: None,
2900 },
2901 );
2902
2903 self.language_server_statuses.insert(
2904 server_id,
2905 LanguageServerStatus {
2906 name: language_server.name().to_string(),
2907 pending_work: Default::default(),
2908 has_pending_diagnostic_updates: false,
2909 progress_tokens: Default::default(),
2910 },
2911 );
2912
2913 cx.emit(Event::LanguageServerAdded(server_id));
2914
2915 if let Some(project_id) = self.remote_id() {
2916 self.client.send(proto::StartLanguageServer {
2917 project_id,
2918 server: Some(proto::LanguageServer {
2919 id: server_id.0 as u64,
2920 name: language_server.name().to_string(),
2921 }),
2922 })?;
2923 }
2924
2925 // Tell the language server about every open buffer in the worktree that matches the language.
2926 for buffer in self.opened_buffers.values() {
2927 if let Some(buffer_handle) = buffer.upgrade(cx) {
2928 let buffer = buffer_handle.read(cx);
2929 let file = match File::from_dyn(buffer.file()) {
2930 Some(file) => file,
2931 None => continue,
2932 };
2933 let language = match buffer.language() {
2934 Some(language) => language,
2935 None => continue,
2936 };
2937
2938 if file.worktree.read(cx).id() != key.0
2939 || !language.lsp_adapters().iter().any(|a| a.name == key.1)
2940 {
2941 continue;
2942 }
2943
2944 let file = match file.as_local() {
2945 Some(file) => file,
2946 None => continue,
2947 };
2948
2949 let versions = self
2950 .buffer_snapshots
2951 .entry(buffer.remote_id())
2952 .or_default()
2953 .entry(server_id)
2954 .or_insert_with(|| {
2955 vec![LspBufferSnapshot {
2956 version: 0,
2957 snapshot: buffer.text_snapshot(),
2958 }]
2959 });
2960
2961 let snapshot = versions.last().unwrap();
2962 let version = snapshot.version;
2963 let initial_snapshot = &snapshot.snapshot;
2964 let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
2965 language_server.notify::<lsp::notification::DidOpenTextDocument>(
2966 lsp::DidOpenTextDocumentParams {
2967 text_document: lsp::TextDocumentItem::new(
2968 uri,
2969 adapter
2970 .language_ids
2971 .get(language.name().as_ref())
2972 .cloned()
2973 .unwrap_or_default(),
2974 version,
2975 initial_snapshot.text(),
2976 ),
2977 },
2978 )?;
2979
2980 buffer_handle.update(cx, |buffer, cx| {
2981 buffer.set_completion_triggers(
2982 language_server
2983 .capabilities()
2984 .completion_provider
2985 .as_ref()
2986 .and_then(|provider| provider.trigger_characters.clone())
2987 .unwrap_or_default(),
2988 cx,
2989 )
2990 });
2991 }
2992 }
2993
2994 cx.notify();
2995 Ok(())
2996 }
2997
2998 // Returns a list of all of the worktrees which no longer have a language server and the root path
2999 // for the stopped server
3000 fn stop_language_server(
3001 &mut self,
3002 worktree_id: WorktreeId,
3003 adapter_name: LanguageServerName,
3004 cx: &mut ModelContext<Self>,
3005 ) -> Task<(Option<PathBuf>, Vec<WorktreeId>)> {
3006 let key = (worktree_id, adapter_name);
3007 if let Some(server_id) = self.language_server_ids.remove(&key) {
3008 // Remove other entries for this language server as well
3009 let mut orphaned_worktrees = vec![worktree_id];
3010 let other_keys = self.language_server_ids.keys().cloned().collect::<Vec<_>>();
3011 for other_key in other_keys {
3012 if self.language_server_ids.get(&other_key) == Some(&server_id) {
3013 self.language_server_ids.remove(&other_key);
3014 orphaned_worktrees.push(other_key.0);
3015 }
3016 }
3017
3018 for buffer in self.opened_buffers.values() {
3019 if let Some(buffer) = buffer.upgrade(cx) {
3020 buffer.update(cx, |buffer, cx| {
3021 buffer.update_diagnostics(server_id, Default::default(), cx);
3022 });
3023 }
3024 }
3025 for worktree in &self.worktrees {
3026 if let Some(worktree) = worktree.upgrade(cx) {
3027 worktree.update(cx, |worktree, cx| {
3028 if let Some(worktree) = worktree.as_local_mut() {
3029 worktree.clear_diagnostics_for_language_server(server_id, cx);
3030 }
3031 });
3032 }
3033 }
3034
3035 self.language_server_statuses.remove(&server_id);
3036 cx.notify();
3037
3038 let server_state = self.language_servers.remove(&server_id);
3039 cx.emit(Event::LanguageServerRemoved(server_id));
3040 cx.spawn_weak(|this, mut cx| async move {
3041 let mut root_path = None;
3042
3043 let server = match server_state {
3044 Some(LanguageServerState::Starting(task)) => task.await,
3045 Some(LanguageServerState::Running { server, .. }) => Some(server),
3046 None => None,
3047 };
3048
3049 if let Some(server) = server {
3050 root_path = Some(server.root_path().clone());
3051 if let Some(shutdown) = server.shutdown() {
3052 shutdown.await;
3053 }
3054 }
3055
3056 if let Some(this) = this.upgrade(&cx) {
3057 this.update(&mut cx, |this, cx| {
3058 this.language_server_statuses.remove(&server_id);
3059 cx.notify();
3060 });
3061 }
3062
3063 (root_path, orphaned_worktrees)
3064 })
3065 } else {
3066 Task::ready((None, Vec::new()))
3067 }
3068 }
3069
3070 pub fn restart_language_servers_for_buffers(
3071 &mut self,
3072 buffers: impl IntoIterator<Item = ModelHandle<Buffer>>,
3073 cx: &mut ModelContext<Self>,
3074 ) -> Option<()> {
3075 let language_server_lookup_info: HashSet<(ModelHandle<Worktree>, Arc<Language>)> = buffers
3076 .into_iter()
3077 .filter_map(|buffer| {
3078 let buffer = buffer.read(cx);
3079 let file = File::from_dyn(buffer.file())?;
3080 let full_path = file.full_path(cx);
3081 let language = self
3082 .languages
3083 .language_for_file(&full_path, Some(buffer.as_rope()))
3084 .now_or_never()?
3085 .ok()?;
3086 Some((file.worktree.clone(), language))
3087 })
3088 .collect();
3089 for (worktree, language) in language_server_lookup_info {
3090 self.restart_language_servers(worktree, language, cx);
3091 }
3092
3093 None
3094 }
3095
3096 // TODO This will break in the case where the adapter's root paths and worktrees are not equal
3097 fn restart_language_servers(
3098 &mut self,
3099 worktree: ModelHandle<Worktree>,
3100 language: Arc<Language>,
3101 cx: &mut ModelContext<Self>,
3102 ) {
3103 let worktree_id = worktree.read(cx).id();
3104 let fallback_path = worktree.read(cx).abs_path();
3105
3106 let mut stops = Vec::new();
3107 for adapter in language.lsp_adapters() {
3108 stops.push(self.stop_language_server(worktree_id, adapter.name.clone(), cx));
3109 }
3110
3111 if stops.is_empty() {
3112 return;
3113 }
3114 let mut stops = stops.into_iter();
3115
3116 cx.spawn_weak(|this, mut cx| async move {
3117 let (original_root_path, mut orphaned_worktrees) = stops.next().unwrap().await;
3118 for stop in stops {
3119 let (_, worktrees) = stop.await;
3120 orphaned_worktrees.extend_from_slice(&worktrees);
3121 }
3122
3123 let this = match this.upgrade(&cx) {
3124 Some(this) => this,
3125 None => return,
3126 };
3127
3128 this.update(&mut cx, |this, cx| {
3129 // Attempt to restart using original server path. Fallback to passed in
3130 // path if we could not retrieve the root path
3131 let root_path = original_root_path
3132 .map(|path_buf| Arc::from(path_buf.as_path()))
3133 .unwrap_or(fallback_path);
3134
3135 this.start_language_servers(&worktree, root_path, language.clone(), cx);
3136
3137 // Lookup new server ids and set them for each of the orphaned worktrees
3138 for adapter in language.lsp_adapters() {
3139 if let Some(new_server_id) = this
3140 .language_server_ids
3141 .get(&(worktree_id, adapter.name.clone()))
3142 .cloned()
3143 {
3144 for &orphaned_worktree in &orphaned_worktrees {
3145 this.language_server_ids
3146 .insert((orphaned_worktree, adapter.name.clone()), new_server_id);
3147 }
3148 }
3149 }
3150 });
3151 })
3152 .detach();
3153 }
3154
3155 fn check_errored_server(
3156 language: Arc<Language>,
3157 adapter: Arc<CachedLspAdapter>,
3158 server_id: LanguageServerId,
3159 installation_test_binary: Option<LanguageServerBinary>,
3160 cx: &mut ModelContext<Self>,
3161 ) {
3162 if !adapter.can_be_reinstalled() {
3163 log::info!(
3164 "Validation check requested for {:?} but it cannot be reinstalled",
3165 adapter.name.0
3166 );
3167 return;
3168 }
3169
3170 cx.spawn(|this, mut cx| async move {
3171 log::info!("About to spawn test binary");
3172
3173 // A lack of test binary counts as a failure
3174 let process = installation_test_binary.and_then(|binary| {
3175 smol::process::Command::new(&binary.path)
3176 .current_dir(&binary.path)
3177 .args(binary.arguments)
3178 .stdin(Stdio::piped())
3179 .stdout(Stdio::piped())
3180 .stderr(Stdio::inherit())
3181 .kill_on_drop(true)
3182 .spawn()
3183 .ok()
3184 });
3185
3186 const PROCESS_TIMEOUT: Duration = Duration::from_secs(5);
3187 let mut timeout = cx.background().timer(PROCESS_TIMEOUT).fuse();
3188
3189 let mut errored = false;
3190 if let Some(mut process) = process {
3191 futures::select! {
3192 status = process.status().fuse() => match status {
3193 Ok(status) => errored = !status.success(),
3194 Err(_) => errored = true,
3195 },
3196
3197 _ = timeout => {
3198 log::info!("test binary time-ed out, this counts as a success");
3199 _ = process.kill();
3200 }
3201 }
3202 } else {
3203 log::warn!("test binary failed to launch");
3204 errored = true;
3205 }
3206
3207 if errored {
3208 log::warn!("test binary check failed");
3209 let task = this.update(&mut cx, move |this, mut cx| {
3210 this.reinstall_language_server(language, adapter, server_id, &mut cx)
3211 });
3212
3213 if let Some(task) = task {
3214 task.await;
3215 }
3216 }
3217 })
3218 .detach();
3219 }
3220
3221 fn on_lsp_progress(
3222 &mut self,
3223 progress: lsp::ProgressParams,
3224 language_server_id: LanguageServerId,
3225 disk_based_diagnostics_progress_token: Option<String>,
3226 cx: &mut ModelContext<Self>,
3227 ) {
3228 let token = match progress.token {
3229 lsp::NumberOrString::String(token) => token,
3230 lsp::NumberOrString::Number(token) => {
3231 log::info!("skipping numeric progress token {}", token);
3232 return;
3233 }
3234 };
3235 let lsp::ProgressParamsValue::WorkDone(progress) = progress.value;
3236 let language_server_status =
3237 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
3238 status
3239 } else {
3240 return;
3241 };
3242
3243 if !language_server_status.progress_tokens.contains(&token) {
3244 return;
3245 }
3246
3247 let is_disk_based_diagnostics_progress = disk_based_diagnostics_progress_token
3248 .as_ref()
3249 .map_or(false, |disk_based_token| {
3250 token.starts_with(disk_based_token)
3251 });
3252
3253 match progress {
3254 lsp::WorkDoneProgress::Begin(report) => {
3255 if is_disk_based_diagnostics_progress {
3256 language_server_status.has_pending_diagnostic_updates = true;
3257 self.disk_based_diagnostics_started(language_server_id, cx);
3258 self.buffer_ordered_messages_tx
3259 .unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
3260 language_server_id,
3261 message: proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(Default::default())
3262 })
3263 .ok();
3264 } else {
3265 self.on_lsp_work_start(
3266 language_server_id,
3267 token.clone(),
3268 LanguageServerProgress {
3269 message: report.message.clone(),
3270 percentage: report.percentage.map(|p| p as usize),
3271 last_update_at: Instant::now(),
3272 },
3273 cx,
3274 );
3275 self.buffer_ordered_messages_tx
3276 .unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
3277 language_server_id,
3278 message: proto::update_language_server::Variant::WorkStart(
3279 proto::LspWorkStart {
3280 token,
3281 message: report.message,
3282 percentage: report.percentage.map(|p| p as u32),
3283 },
3284 ),
3285 })
3286 .ok();
3287 }
3288 }
3289 lsp::WorkDoneProgress::Report(report) => {
3290 if !is_disk_based_diagnostics_progress {
3291 self.on_lsp_work_progress(
3292 language_server_id,
3293 token.clone(),
3294 LanguageServerProgress {
3295 message: report.message.clone(),
3296 percentage: report.percentage.map(|p| p as usize),
3297 last_update_at: Instant::now(),
3298 },
3299 cx,
3300 );
3301 self.buffer_ordered_messages_tx
3302 .unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
3303 language_server_id,
3304 message: proto::update_language_server::Variant::WorkProgress(
3305 proto::LspWorkProgress {
3306 token,
3307 message: report.message,
3308 percentage: report.percentage.map(|p| p as u32),
3309 },
3310 ),
3311 })
3312 .ok();
3313 }
3314 }
3315 lsp::WorkDoneProgress::End(_) => {
3316 language_server_status.progress_tokens.remove(&token);
3317
3318 if is_disk_based_diagnostics_progress {
3319 language_server_status.has_pending_diagnostic_updates = false;
3320 self.disk_based_diagnostics_finished(language_server_id, cx);
3321 self.buffer_ordered_messages_tx
3322 .unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
3323 language_server_id,
3324 message:
3325 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
3326 Default::default(),
3327 ),
3328 })
3329 .ok();
3330 } else {
3331 self.on_lsp_work_end(language_server_id, token.clone(), cx);
3332 self.buffer_ordered_messages_tx
3333 .unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
3334 language_server_id,
3335 message: proto::update_language_server::Variant::WorkEnd(
3336 proto::LspWorkEnd { token },
3337 ),
3338 })
3339 .ok();
3340 }
3341 }
3342 }
3343 }
3344
3345 fn on_lsp_work_start(
3346 &mut self,
3347 language_server_id: LanguageServerId,
3348 token: String,
3349 progress: LanguageServerProgress,
3350 cx: &mut ModelContext<Self>,
3351 ) {
3352 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
3353 status.pending_work.insert(token, progress);
3354 cx.notify();
3355 }
3356 }
3357
3358 fn on_lsp_work_progress(
3359 &mut self,
3360 language_server_id: LanguageServerId,
3361 token: String,
3362 progress: LanguageServerProgress,
3363 cx: &mut ModelContext<Self>,
3364 ) {
3365 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
3366 let entry = status
3367 .pending_work
3368 .entry(token)
3369 .or_insert(LanguageServerProgress {
3370 message: Default::default(),
3371 percentage: Default::default(),
3372 last_update_at: progress.last_update_at,
3373 });
3374 if progress.message.is_some() {
3375 entry.message = progress.message;
3376 }
3377 if progress.percentage.is_some() {
3378 entry.percentage = progress.percentage;
3379 }
3380 entry.last_update_at = progress.last_update_at;
3381 cx.notify();
3382 }
3383 }
3384
3385 fn on_lsp_work_end(
3386 &mut self,
3387 language_server_id: LanguageServerId,
3388 token: String,
3389 cx: &mut ModelContext<Self>,
3390 ) {
3391 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
3392 status.pending_work.remove(&token);
3393 cx.notify();
3394 }
3395 }
3396
3397 fn on_lsp_did_change_watched_files(
3398 &mut self,
3399 language_server_id: LanguageServerId,
3400 params: DidChangeWatchedFilesRegistrationOptions,
3401 cx: &mut ModelContext<Self>,
3402 ) {
3403 if let Some(LanguageServerState::Running { watched_paths, .. }) =
3404 self.language_servers.get_mut(&language_server_id)
3405 {
3406 let mut builders = HashMap::default();
3407 for watcher in params.watchers {
3408 for worktree in &self.worktrees {
3409 if let Some(worktree) = worktree.upgrade(cx) {
3410 let glob_is_inside_worktree = worktree.update(cx, |tree, _| {
3411 if let Some(abs_path) = tree.abs_path().to_str() {
3412 let relative_glob_pattern = match &watcher.glob_pattern {
3413 lsp::GlobPattern::String(s) => s
3414 .strip_prefix(abs_path)
3415 .and_then(|s| s.strip_prefix(std::path::MAIN_SEPARATOR)),
3416 lsp::GlobPattern::Relative(rp) => {
3417 let base_uri = match &rp.base_uri {
3418 lsp::OneOf::Left(workspace_folder) => {
3419 &workspace_folder.uri
3420 }
3421 lsp::OneOf::Right(base_uri) => base_uri,
3422 };
3423 base_uri.to_file_path().ok().and_then(|file_path| {
3424 (file_path.to_str() == Some(abs_path))
3425 .then_some(rp.pattern.as_str())
3426 })
3427 }
3428 };
3429 if let Some(relative_glob_pattern) = relative_glob_pattern {
3430 let literal_prefix =
3431 glob_literal_prefix(&relative_glob_pattern);
3432 tree.as_local_mut()
3433 .unwrap()
3434 .add_path_prefix_to_scan(Path::new(literal_prefix).into());
3435 if let Some(glob) = Glob::new(relative_glob_pattern).log_err() {
3436 builders
3437 .entry(tree.id())
3438 .or_insert_with(|| GlobSetBuilder::new())
3439 .add(glob);
3440 }
3441 return true;
3442 }
3443 }
3444 false
3445 });
3446 if glob_is_inside_worktree {
3447 break;
3448 }
3449 }
3450 }
3451 }
3452
3453 watched_paths.clear();
3454 for (worktree_id, builder) in builders {
3455 if let Ok(globset) = builder.build() {
3456 watched_paths.insert(worktree_id, globset);
3457 }
3458 }
3459
3460 cx.notify();
3461 }
3462 }
3463
3464 async fn on_lsp_workspace_edit(
3465 this: WeakModelHandle<Self>,
3466 params: lsp::ApplyWorkspaceEditParams,
3467 server_id: LanguageServerId,
3468 adapter: Arc<CachedLspAdapter>,
3469 mut cx: AsyncAppContext,
3470 ) -> Result<lsp::ApplyWorkspaceEditResponse> {
3471 let this = this
3472 .upgrade(&cx)
3473 .ok_or_else(|| anyhow!("project project closed"))?;
3474 let language_server = this
3475 .read_with(&cx, |this, _| this.language_server_for_id(server_id))
3476 .ok_or_else(|| anyhow!("language server not found"))?;
3477 let transaction = Self::deserialize_workspace_edit(
3478 this.clone(),
3479 params.edit,
3480 true,
3481 adapter.clone(),
3482 language_server.clone(),
3483 &mut cx,
3484 )
3485 .await
3486 .log_err();
3487 this.update(&mut cx, |this, _| {
3488 if let Some(transaction) = transaction {
3489 this.last_workspace_edits_by_language_server
3490 .insert(server_id, transaction);
3491 }
3492 });
3493 Ok(lsp::ApplyWorkspaceEditResponse {
3494 applied: true,
3495 failed_change: None,
3496 failure_reason: None,
3497 })
3498 }
3499
3500 pub fn language_server_statuses(
3501 &self,
3502 ) -> impl DoubleEndedIterator<Item = &LanguageServerStatus> {
3503 self.language_server_statuses.values()
3504 }
3505
3506 pub fn update_diagnostics(
3507 &mut self,
3508 language_server_id: LanguageServerId,
3509 mut params: lsp::PublishDiagnosticsParams,
3510 disk_based_sources: &[String],
3511 cx: &mut ModelContext<Self>,
3512 ) -> Result<()> {
3513 let abs_path = params
3514 .uri
3515 .to_file_path()
3516 .map_err(|_| anyhow!("URI is not a file"))?;
3517 let mut diagnostics = Vec::default();
3518 let mut primary_diagnostic_group_ids = HashMap::default();
3519 let mut sources_by_group_id = HashMap::default();
3520 let mut supporting_diagnostics = HashMap::default();
3521
3522 // Ensure that primary diagnostics are always the most severe
3523 params.diagnostics.sort_by_key(|item| item.severity);
3524
3525 for diagnostic in ¶ms.diagnostics {
3526 let source = diagnostic.source.as_ref();
3527 let code = diagnostic.code.as_ref().map(|code| match code {
3528 lsp::NumberOrString::Number(code) => code.to_string(),
3529 lsp::NumberOrString::String(code) => code.clone(),
3530 });
3531 let range = range_from_lsp(diagnostic.range);
3532 let is_supporting = diagnostic
3533 .related_information
3534 .as_ref()
3535 .map_or(false, |infos| {
3536 infos.iter().any(|info| {
3537 primary_diagnostic_group_ids.contains_key(&(
3538 source,
3539 code.clone(),
3540 range_from_lsp(info.location.range),
3541 ))
3542 })
3543 });
3544
3545 let is_unnecessary = diagnostic.tags.as_ref().map_or(false, |tags| {
3546 tags.iter().any(|tag| *tag == DiagnosticTag::UNNECESSARY)
3547 });
3548
3549 if is_supporting {
3550 supporting_diagnostics.insert(
3551 (source, code.clone(), range),
3552 (diagnostic.severity, is_unnecessary),
3553 );
3554 } else {
3555 let group_id = post_inc(&mut self.next_diagnostic_group_id);
3556 let is_disk_based =
3557 source.map_or(false, |source| disk_based_sources.contains(source));
3558
3559 sources_by_group_id.insert(group_id, source);
3560 primary_diagnostic_group_ids
3561 .insert((source, code.clone(), range.clone()), group_id);
3562
3563 diagnostics.push(DiagnosticEntry {
3564 range,
3565 diagnostic: Diagnostic {
3566 source: diagnostic.source.clone(),
3567 code: code.clone(),
3568 severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
3569 message: diagnostic.message.clone(),
3570 group_id,
3571 is_primary: true,
3572 is_valid: true,
3573 is_disk_based,
3574 is_unnecessary,
3575 },
3576 });
3577 if let Some(infos) = &diagnostic.related_information {
3578 for info in infos {
3579 if info.location.uri == params.uri && !info.message.is_empty() {
3580 let range = range_from_lsp(info.location.range);
3581 diagnostics.push(DiagnosticEntry {
3582 range,
3583 diagnostic: Diagnostic {
3584 source: diagnostic.source.clone(),
3585 code: code.clone(),
3586 severity: DiagnosticSeverity::INFORMATION,
3587 message: info.message.clone(),
3588 group_id,
3589 is_primary: false,
3590 is_valid: true,
3591 is_disk_based,
3592 is_unnecessary: false,
3593 },
3594 });
3595 }
3596 }
3597 }
3598 }
3599 }
3600
3601 for entry in &mut diagnostics {
3602 let diagnostic = &mut entry.diagnostic;
3603 if !diagnostic.is_primary {
3604 let source = *sources_by_group_id.get(&diagnostic.group_id).unwrap();
3605 if let Some(&(severity, is_unnecessary)) = supporting_diagnostics.get(&(
3606 source,
3607 diagnostic.code.clone(),
3608 entry.range.clone(),
3609 )) {
3610 if let Some(severity) = severity {
3611 diagnostic.severity = severity;
3612 }
3613 diagnostic.is_unnecessary = is_unnecessary;
3614 }
3615 }
3616 }
3617
3618 self.update_diagnostic_entries(
3619 language_server_id,
3620 abs_path,
3621 params.version,
3622 diagnostics,
3623 cx,
3624 )?;
3625 Ok(())
3626 }
3627
3628 pub fn update_diagnostic_entries(
3629 &mut self,
3630 server_id: LanguageServerId,
3631 abs_path: PathBuf,
3632 version: Option<i32>,
3633 diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
3634 cx: &mut ModelContext<Project>,
3635 ) -> Result<(), anyhow::Error> {
3636 let (worktree, relative_path) = self
3637 .find_local_worktree(&abs_path, cx)
3638 .ok_or_else(|| anyhow!("no worktree found for diagnostics path {abs_path:?}"))?;
3639
3640 let project_path = ProjectPath {
3641 worktree_id: worktree.read(cx).id(),
3642 path: relative_path.into(),
3643 };
3644
3645 if let Some(buffer) = self.get_open_buffer(&project_path, cx) {
3646 self.update_buffer_diagnostics(&buffer, server_id, version, diagnostics.clone(), cx)?;
3647 }
3648
3649 let updated = worktree.update(cx, |worktree, cx| {
3650 worktree
3651 .as_local_mut()
3652 .ok_or_else(|| anyhow!("not a local worktree"))?
3653 .update_diagnostics(server_id, project_path.path.clone(), diagnostics, cx)
3654 })?;
3655 if updated {
3656 cx.emit(Event::DiagnosticsUpdated {
3657 language_server_id: server_id,
3658 path: project_path,
3659 });
3660 }
3661 Ok(())
3662 }
3663
3664 fn update_buffer_diagnostics(
3665 &mut self,
3666 buffer: &ModelHandle<Buffer>,
3667 server_id: LanguageServerId,
3668 version: Option<i32>,
3669 mut diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
3670 cx: &mut ModelContext<Self>,
3671 ) -> Result<()> {
3672 fn compare_diagnostics(a: &Diagnostic, b: &Diagnostic) -> Ordering {
3673 Ordering::Equal
3674 .then_with(|| b.is_primary.cmp(&a.is_primary))
3675 .then_with(|| a.is_disk_based.cmp(&b.is_disk_based))
3676 .then_with(|| a.severity.cmp(&b.severity))
3677 .then_with(|| a.message.cmp(&b.message))
3678 }
3679
3680 let snapshot = self.buffer_snapshot_for_lsp_version(buffer, server_id, version, cx)?;
3681
3682 diagnostics.sort_unstable_by(|a, b| {
3683 Ordering::Equal
3684 .then_with(|| a.range.start.cmp(&b.range.start))
3685 .then_with(|| b.range.end.cmp(&a.range.end))
3686 .then_with(|| compare_diagnostics(&a.diagnostic, &b.diagnostic))
3687 });
3688
3689 let mut sanitized_diagnostics = Vec::new();
3690 let edits_since_save = Patch::new(
3691 snapshot
3692 .edits_since::<Unclipped<PointUtf16>>(buffer.read(cx).saved_version())
3693 .collect(),
3694 );
3695 for entry in diagnostics {
3696 let start;
3697 let end;
3698 if entry.diagnostic.is_disk_based {
3699 // Some diagnostics are based on files on disk instead of buffers'
3700 // current contents. Adjust these diagnostics' ranges to reflect
3701 // any unsaved edits.
3702 start = edits_since_save.old_to_new(entry.range.start);
3703 end = edits_since_save.old_to_new(entry.range.end);
3704 } else {
3705 start = entry.range.start;
3706 end = entry.range.end;
3707 }
3708
3709 let mut range = snapshot.clip_point_utf16(start, Bias::Left)
3710 ..snapshot.clip_point_utf16(end, Bias::Right);
3711
3712 // Expand empty ranges by one codepoint
3713 if range.start == range.end {
3714 // This will be go to the next boundary when being clipped
3715 range.end.column += 1;
3716 range.end = snapshot.clip_point_utf16(Unclipped(range.end), Bias::Right);
3717 if range.start == range.end && range.end.column > 0 {
3718 range.start.column -= 1;
3719 range.end = snapshot.clip_point_utf16(Unclipped(range.end), Bias::Left);
3720 }
3721 }
3722
3723 sanitized_diagnostics.push(DiagnosticEntry {
3724 range,
3725 diagnostic: entry.diagnostic,
3726 });
3727 }
3728 drop(edits_since_save);
3729
3730 let set = DiagnosticSet::new(sanitized_diagnostics, &snapshot);
3731 buffer.update(cx, |buffer, cx| {
3732 buffer.update_diagnostics(server_id, set, cx)
3733 });
3734 Ok(())
3735 }
3736
3737 pub fn reload_buffers(
3738 &self,
3739 buffers: HashSet<ModelHandle<Buffer>>,
3740 push_to_history: bool,
3741 cx: &mut ModelContext<Self>,
3742 ) -> Task<Result<ProjectTransaction>> {
3743 let mut local_buffers = Vec::new();
3744 let mut remote_buffers = None;
3745 for buffer_handle in buffers {
3746 let buffer = buffer_handle.read(cx);
3747 if buffer.is_dirty() {
3748 if let Some(file) = File::from_dyn(buffer.file()) {
3749 if file.is_local() {
3750 local_buffers.push(buffer_handle);
3751 } else {
3752 remote_buffers.get_or_insert(Vec::new()).push(buffer_handle);
3753 }
3754 }
3755 }
3756 }
3757
3758 let remote_buffers = self.remote_id().zip(remote_buffers);
3759 let client = self.client.clone();
3760
3761 cx.spawn(|this, mut cx| async move {
3762 let mut project_transaction = ProjectTransaction::default();
3763
3764 if let Some((project_id, remote_buffers)) = remote_buffers {
3765 let response = client
3766 .request(proto::ReloadBuffers {
3767 project_id,
3768 buffer_ids: remote_buffers
3769 .iter()
3770 .map(|buffer| buffer.read_with(&cx, |buffer, _| buffer.remote_id()))
3771 .collect(),
3772 })
3773 .await?
3774 .transaction
3775 .ok_or_else(|| anyhow!("missing transaction"))?;
3776 project_transaction = this
3777 .update(&mut cx, |this, cx| {
3778 this.deserialize_project_transaction(response, push_to_history, cx)
3779 })
3780 .await?;
3781 }
3782
3783 for buffer in local_buffers {
3784 let transaction = buffer
3785 .update(&mut cx, |buffer, cx| buffer.reload(cx))
3786 .await?;
3787 buffer.update(&mut cx, |buffer, cx| {
3788 if let Some(transaction) = transaction {
3789 if !push_to_history {
3790 buffer.forget_transaction(transaction.id);
3791 }
3792 project_transaction.0.insert(cx.handle(), transaction);
3793 }
3794 });
3795 }
3796
3797 Ok(project_transaction)
3798 })
3799 }
3800
3801 pub fn format(
3802 &self,
3803 buffers: HashSet<ModelHandle<Buffer>>,
3804 push_to_history: bool,
3805 trigger: FormatTrigger,
3806 cx: &mut ModelContext<Project>,
3807 ) -> Task<Result<ProjectTransaction>> {
3808 if self.is_local() {
3809 let mut buffers_with_paths_and_servers = buffers
3810 .into_iter()
3811 .filter_map(|buffer_handle| {
3812 let buffer = buffer_handle.read(cx);
3813 let file = File::from_dyn(buffer.file())?;
3814 let buffer_abs_path = file.as_local().map(|f| f.abs_path(cx));
3815 let server = self
3816 .primary_language_servers_for_buffer(buffer, cx)
3817 .map(|s| s.1.clone());
3818 Some((buffer_handle, buffer_abs_path, server))
3819 })
3820 .collect::<Vec<_>>();
3821
3822 cx.spawn(|this, mut cx| async move {
3823 // Do not allow multiple concurrent formatting requests for the
3824 // same buffer.
3825 this.update(&mut cx, |this, cx| {
3826 buffers_with_paths_and_servers.retain(|(buffer, _, _)| {
3827 this.buffers_being_formatted
3828 .insert(buffer.read(cx).remote_id())
3829 });
3830 });
3831
3832 let _cleanup = defer({
3833 let this = this.clone();
3834 let mut cx = cx.clone();
3835 let buffers = &buffers_with_paths_and_servers;
3836 move || {
3837 this.update(&mut cx, |this, cx| {
3838 for (buffer, _, _) in buffers {
3839 this.buffers_being_formatted
3840 .remove(&buffer.read(cx).remote_id());
3841 }
3842 });
3843 }
3844 });
3845
3846 let mut project_transaction = ProjectTransaction::default();
3847 for (buffer, buffer_abs_path, language_server) in &buffers_with_paths_and_servers {
3848 let settings = buffer.read_with(&cx, |buffer, cx| {
3849 language_settings(buffer.language(), buffer.file(), cx).clone()
3850 });
3851
3852 let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save;
3853 let ensure_final_newline = settings.ensure_final_newline_on_save;
3854 let format_on_save = settings.format_on_save.clone();
3855 let formatter = settings.formatter.clone();
3856 let tab_size = settings.tab_size;
3857
3858 // First, format buffer's whitespace according to the settings.
3859 let trailing_whitespace_diff = if remove_trailing_whitespace {
3860 Some(
3861 buffer
3862 .read_with(&cx, |b, cx| b.remove_trailing_whitespace(cx))
3863 .await,
3864 )
3865 } else {
3866 None
3867 };
3868 let whitespace_transaction_id = buffer.update(&mut cx, |buffer, cx| {
3869 buffer.finalize_last_transaction();
3870 buffer.start_transaction();
3871 if let Some(diff) = trailing_whitespace_diff {
3872 buffer.apply_diff(diff, cx);
3873 }
3874 if ensure_final_newline {
3875 buffer.ensure_final_newline(cx);
3876 }
3877 buffer.end_transaction(cx)
3878 });
3879
3880 // Currently, formatting operations are represented differently depending on
3881 // whether they come from a language server or an external command.
3882 enum FormatOperation {
3883 Lsp(Vec<(Range<Anchor>, String)>),
3884 External(Diff),
3885 }
3886
3887 // Apply language-specific formatting using either a language server
3888 // or external command.
3889 let mut format_operation = None;
3890 match (formatter, format_on_save) {
3891 (_, FormatOnSave::Off) if trigger == FormatTrigger::Save => {}
3892
3893 (Formatter::LanguageServer, FormatOnSave::On | FormatOnSave::Off)
3894 | (_, FormatOnSave::LanguageServer) => {
3895 if let Some((language_server, buffer_abs_path)) =
3896 language_server.as_ref().zip(buffer_abs_path.as_ref())
3897 {
3898 format_operation = Some(FormatOperation::Lsp(
3899 Self::format_via_lsp(
3900 &this,
3901 &buffer,
3902 buffer_abs_path,
3903 &language_server,
3904 tab_size,
3905 &mut cx,
3906 )
3907 .await
3908 .context("failed to format via language server")?,
3909 ));
3910 }
3911 }
3912
3913 (
3914 Formatter::External { command, arguments },
3915 FormatOnSave::On | FormatOnSave::Off,
3916 )
3917 | (_, FormatOnSave::External { command, arguments }) => {
3918 if let Some(buffer_abs_path) = buffer_abs_path {
3919 format_operation = Self::format_via_external_command(
3920 &buffer,
3921 &buffer_abs_path,
3922 &command,
3923 &arguments,
3924 &mut cx,
3925 )
3926 .await
3927 .context(format!(
3928 "failed to format via external command {:?}",
3929 command
3930 ))?
3931 .map(FormatOperation::External);
3932 }
3933 }
3934 };
3935
3936 buffer.update(&mut cx, |b, cx| {
3937 // If the buffer had its whitespace formatted and was edited while the language-specific
3938 // formatting was being computed, avoid applying the language-specific formatting, because
3939 // it can't be grouped with the whitespace formatting in the undo history.
3940 if let Some(transaction_id) = whitespace_transaction_id {
3941 if b.peek_undo_stack()
3942 .map_or(true, |e| e.transaction_id() != transaction_id)
3943 {
3944 format_operation.take();
3945 }
3946 }
3947
3948 // Apply any language-specific formatting, and group the two formatting operations
3949 // in the buffer's undo history.
3950 if let Some(operation) = format_operation {
3951 match operation {
3952 FormatOperation::Lsp(edits) => {
3953 b.edit(edits, None, cx);
3954 }
3955 FormatOperation::External(diff) => {
3956 b.apply_diff(diff, cx);
3957 }
3958 }
3959
3960 if let Some(transaction_id) = whitespace_transaction_id {
3961 b.group_until_transaction(transaction_id);
3962 }
3963 }
3964
3965 if let Some(transaction) = b.finalize_last_transaction().cloned() {
3966 if !push_to_history {
3967 b.forget_transaction(transaction.id);
3968 }
3969 project_transaction.0.insert(buffer.clone(), transaction);
3970 }
3971 });
3972 }
3973
3974 Ok(project_transaction)
3975 })
3976 } else {
3977 let remote_id = self.remote_id();
3978 let client = self.client.clone();
3979 cx.spawn(|this, mut cx| async move {
3980 let mut project_transaction = ProjectTransaction::default();
3981 if let Some(project_id) = remote_id {
3982 let response = client
3983 .request(proto::FormatBuffers {
3984 project_id,
3985 trigger: trigger as i32,
3986 buffer_ids: buffers
3987 .iter()
3988 .map(|buffer| buffer.read_with(&cx, |buffer, _| buffer.remote_id()))
3989 .collect(),
3990 })
3991 .await?
3992 .transaction
3993 .ok_or_else(|| anyhow!("missing transaction"))?;
3994 project_transaction = this
3995 .update(&mut cx, |this, cx| {
3996 this.deserialize_project_transaction(response, push_to_history, cx)
3997 })
3998 .await?;
3999 }
4000 Ok(project_transaction)
4001 })
4002 }
4003 }
4004
4005 async fn format_via_lsp(
4006 this: &ModelHandle<Self>,
4007 buffer: &ModelHandle<Buffer>,
4008 abs_path: &Path,
4009 language_server: &Arc<LanguageServer>,
4010 tab_size: NonZeroU32,
4011 cx: &mut AsyncAppContext,
4012 ) -> Result<Vec<(Range<Anchor>, String)>> {
4013 let uri = lsp::Url::from_file_path(abs_path)
4014 .map_err(|_| anyhow!("failed to convert abs path to uri"))?;
4015 let text_document = lsp::TextDocumentIdentifier::new(uri);
4016 let capabilities = &language_server.capabilities();
4017
4018 let formatting_provider = capabilities.document_formatting_provider.as_ref();
4019 let range_formatting_provider = capabilities.document_range_formatting_provider.as_ref();
4020
4021 let lsp_edits = if matches!(formatting_provider, Some(p) if *p != OneOf::Left(false)) {
4022 language_server
4023 .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
4024 text_document,
4025 options: lsp_command::lsp_formatting_options(tab_size.get()),
4026 work_done_progress_params: Default::default(),
4027 })
4028 .await?
4029 } else if matches!(range_formatting_provider, Some(p) if *p != OneOf::Left(false)) {
4030 let buffer_start = lsp::Position::new(0, 0);
4031 let buffer_end = buffer.read_with(cx, |b, _| point_to_lsp(b.max_point_utf16()));
4032
4033 language_server
4034 .request::<lsp::request::RangeFormatting>(lsp::DocumentRangeFormattingParams {
4035 text_document,
4036 range: lsp::Range::new(buffer_start, buffer_end),
4037 options: lsp_command::lsp_formatting_options(tab_size.get()),
4038 work_done_progress_params: Default::default(),
4039 })
4040 .await?
4041 } else {
4042 None
4043 };
4044
4045 if let Some(lsp_edits) = lsp_edits {
4046 this.update(cx, |this, cx| {
4047 this.edits_from_lsp(buffer, lsp_edits, language_server.server_id(), None, cx)
4048 })
4049 .await
4050 } else {
4051 Ok(Vec::new())
4052 }
4053 }
4054
4055 async fn format_via_external_command(
4056 buffer: &ModelHandle<Buffer>,
4057 buffer_abs_path: &Path,
4058 command: &str,
4059 arguments: &[String],
4060 cx: &mut AsyncAppContext,
4061 ) -> Result<Option<Diff>> {
4062 let working_dir_path = buffer.read_with(cx, |buffer, cx| {
4063 let file = File::from_dyn(buffer.file())?;
4064 let worktree = file.worktree.read(cx).as_local()?;
4065 let mut worktree_path = worktree.abs_path().to_path_buf();
4066 if worktree.root_entry()?.is_file() {
4067 worktree_path.pop();
4068 }
4069 Some(worktree_path)
4070 });
4071
4072 if let Some(working_dir_path) = working_dir_path {
4073 let mut child =
4074 smol::process::Command::new(command)
4075 .args(arguments.iter().map(|arg| {
4076 arg.replace("{buffer_path}", &buffer_abs_path.to_string_lossy())
4077 }))
4078 .current_dir(&working_dir_path)
4079 .stdin(smol::process::Stdio::piped())
4080 .stdout(smol::process::Stdio::piped())
4081 .stderr(smol::process::Stdio::piped())
4082 .spawn()?;
4083 let stdin = child
4084 .stdin
4085 .as_mut()
4086 .ok_or_else(|| anyhow!("failed to acquire stdin"))?;
4087 let text = buffer.read_with(cx, |buffer, _| buffer.as_rope().clone());
4088 for chunk in text.chunks() {
4089 stdin.write_all(chunk.as_bytes()).await?;
4090 }
4091 stdin.flush().await?;
4092
4093 let output = child.output().await?;
4094 if !output.status.success() {
4095 return Err(anyhow!(
4096 "command failed with exit code {:?}:\nstdout: {}\nstderr: {}",
4097 output.status.code(),
4098 String::from_utf8_lossy(&output.stdout),
4099 String::from_utf8_lossy(&output.stderr),
4100 ));
4101 }
4102
4103 let stdout = String::from_utf8(output.stdout)?;
4104 Ok(Some(
4105 buffer
4106 .read_with(cx, |buffer, cx| buffer.diff(stdout, cx))
4107 .await,
4108 ))
4109 } else {
4110 Ok(None)
4111 }
4112 }
4113
4114 pub fn definition<T: ToPointUtf16>(
4115 &self,
4116 buffer: &ModelHandle<Buffer>,
4117 position: T,
4118 cx: &mut ModelContext<Self>,
4119 ) -> Task<Result<Vec<LocationLink>>> {
4120 let position = position.to_point_utf16(buffer.read(cx));
4121 self.request_lsp(buffer.clone(), GetDefinition { position }, cx)
4122 }
4123
4124 pub fn type_definition<T: ToPointUtf16>(
4125 &self,
4126 buffer: &ModelHandle<Buffer>,
4127 position: T,
4128 cx: &mut ModelContext<Self>,
4129 ) -> Task<Result<Vec<LocationLink>>> {
4130 let position = position.to_point_utf16(buffer.read(cx));
4131 self.request_lsp(buffer.clone(), GetTypeDefinition { position }, cx)
4132 }
4133
4134 pub fn references<T: ToPointUtf16>(
4135 &self,
4136 buffer: &ModelHandle<Buffer>,
4137 position: T,
4138 cx: &mut ModelContext<Self>,
4139 ) -> Task<Result<Vec<Location>>> {
4140 let position = position.to_point_utf16(buffer.read(cx));
4141 self.request_lsp(buffer.clone(), GetReferences { position }, cx)
4142 }
4143
4144 pub fn document_highlights<T: ToPointUtf16>(
4145 &self,
4146 buffer: &ModelHandle<Buffer>,
4147 position: T,
4148 cx: &mut ModelContext<Self>,
4149 ) -> Task<Result<Vec<DocumentHighlight>>> {
4150 let position = position.to_point_utf16(buffer.read(cx));
4151 self.request_lsp(buffer.clone(), GetDocumentHighlights { position }, cx)
4152 }
4153
4154 pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
4155 if self.is_local() {
4156 let mut requests = Vec::new();
4157 for ((worktree_id, _), server_id) in self.language_server_ids.iter() {
4158 let worktree_id = *worktree_id;
4159 let worktree_handle = self.worktree_for_id(worktree_id, cx);
4160 let worktree = match worktree_handle.and_then(|tree| tree.read(cx).as_local()) {
4161 Some(worktree) => worktree,
4162 None => continue,
4163 };
4164 let worktree_abs_path = worktree.abs_path().clone();
4165
4166 let (adapter, language, server) = match self.language_servers.get(server_id) {
4167 Some(LanguageServerState::Running {
4168 adapter,
4169 language,
4170 server,
4171 ..
4172 }) => (adapter.clone(), language.clone(), server),
4173
4174 _ => continue,
4175 };
4176
4177 requests.push(
4178 server
4179 .request::<lsp::request::WorkspaceSymbolRequest>(
4180 lsp::WorkspaceSymbolParams {
4181 query: query.to_string(),
4182 ..Default::default()
4183 },
4184 )
4185 .log_err()
4186 .map(move |response| {
4187 let lsp_symbols = response.flatten().map(|symbol_response| match symbol_response {
4188 lsp::WorkspaceSymbolResponse::Flat(flat_responses) => {
4189 flat_responses.into_iter().map(|lsp_symbol| {
4190 (lsp_symbol.name, lsp_symbol.kind, lsp_symbol.location)
4191 }).collect::<Vec<_>>()
4192 }
4193 lsp::WorkspaceSymbolResponse::Nested(nested_responses) => {
4194 nested_responses.into_iter().filter_map(|lsp_symbol| {
4195 let location = match lsp_symbol.location {
4196 OneOf::Left(location) => location,
4197 OneOf::Right(_) => {
4198 error!("Unexpected: client capabilities forbid symbol resolutions in workspace.symbol.resolveSupport");
4199 return None
4200 }
4201 };
4202 Some((lsp_symbol.name, lsp_symbol.kind, location))
4203 }).collect::<Vec<_>>()
4204 }
4205 }).unwrap_or_default();
4206
4207 (
4208 adapter,
4209 language,
4210 worktree_id,
4211 worktree_abs_path,
4212 lsp_symbols,
4213 )
4214 }),
4215 );
4216 }
4217
4218 cx.spawn_weak(|this, cx| async move {
4219 let responses = futures::future::join_all(requests).await;
4220 let this = match this.upgrade(&cx) {
4221 Some(this) => this,
4222 None => return Ok(Vec::new()),
4223 };
4224
4225 let symbols = this.read_with(&cx, |this, cx| {
4226 let mut symbols = Vec::new();
4227 for (
4228 adapter,
4229 adapter_language,
4230 source_worktree_id,
4231 worktree_abs_path,
4232 lsp_symbols,
4233 ) in responses
4234 {
4235 symbols.extend(lsp_symbols.into_iter().filter_map(
4236 |(symbol_name, symbol_kind, symbol_location)| {
4237 let abs_path = symbol_location.uri.to_file_path().ok()?;
4238 let mut worktree_id = source_worktree_id;
4239 let path;
4240 if let Some((worktree, rel_path)) =
4241 this.find_local_worktree(&abs_path, cx)
4242 {
4243 worktree_id = worktree.read(cx).id();
4244 path = rel_path;
4245 } else {
4246 path = relativize_path(&worktree_abs_path, &abs_path);
4247 }
4248
4249 let project_path = ProjectPath {
4250 worktree_id,
4251 path: path.into(),
4252 };
4253 let signature = this.symbol_signature(&project_path);
4254 let adapter_language = adapter_language.clone();
4255 let language = this
4256 .languages
4257 .language_for_file(&project_path.path, None)
4258 .unwrap_or_else(move |_| adapter_language);
4259 let language_server_name = adapter.name.clone();
4260 Some(async move {
4261 let language = language.await;
4262 let label =
4263 language.label_for_symbol(&symbol_name, symbol_kind).await;
4264
4265 Symbol {
4266 language_server_name,
4267 source_worktree_id,
4268 path: project_path,
4269 label: label.unwrap_or_else(|| {
4270 CodeLabel::plain(symbol_name.clone(), None)
4271 }),
4272 kind: symbol_kind,
4273 name: symbol_name,
4274 range: range_from_lsp(symbol_location.range),
4275 signature,
4276 }
4277 })
4278 },
4279 ));
4280 }
4281
4282 symbols
4283 });
4284
4285 Ok(futures::future::join_all(symbols).await)
4286 })
4287 } else if let Some(project_id) = self.remote_id() {
4288 let request = self.client.request(proto::GetProjectSymbols {
4289 project_id,
4290 query: query.to_string(),
4291 });
4292 cx.spawn_weak(|this, cx| async move {
4293 let response = request.await?;
4294 let mut symbols = Vec::new();
4295 if let Some(this) = this.upgrade(&cx) {
4296 let new_symbols = this.read_with(&cx, |this, _| {
4297 response
4298 .symbols
4299 .into_iter()
4300 .map(|symbol| this.deserialize_symbol(symbol))
4301 .collect::<Vec<_>>()
4302 });
4303 symbols = futures::future::join_all(new_symbols)
4304 .await
4305 .into_iter()
4306 .filter_map(|symbol| symbol.log_err())
4307 .collect::<Vec<_>>();
4308 }
4309 Ok(symbols)
4310 })
4311 } else {
4312 Task::ready(Ok(Default::default()))
4313 }
4314 }
4315
4316 pub fn open_buffer_for_symbol(
4317 &mut self,
4318 symbol: &Symbol,
4319 cx: &mut ModelContext<Self>,
4320 ) -> Task<Result<ModelHandle<Buffer>>> {
4321 if self.is_local() {
4322 let language_server_id = if let Some(id) = self.language_server_ids.get(&(
4323 symbol.source_worktree_id,
4324 symbol.language_server_name.clone(),
4325 )) {
4326 *id
4327 } else {
4328 return Task::ready(Err(anyhow!(
4329 "language server for worktree and language not found"
4330 )));
4331 };
4332
4333 let worktree_abs_path = if let Some(worktree_abs_path) = self
4334 .worktree_for_id(symbol.path.worktree_id, cx)
4335 .and_then(|worktree| worktree.read(cx).as_local())
4336 .map(|local_worktree| local_worktree.abs_path())
4337 {
4338 worktree_abs_path
4339 } else {
4340 return Task::ready(Err(anyhow!("worktree not found for symbol")));
4341 };
4342 let symbol_abs_path = worktree_abs_path.join(&symbol.path.path);
4343 let symbol_uri = if let Ok(uri) = lsp::Url::from_file_path(symbol_abs_path) {
4344 uri
4345 } else {
4346 return Task::ready(Err(anyhow!("invalid symbol path")));
4347 };
4348
4349 self.open_local_buffer_via_lsp(
4350 symbol_uri,
4351 language_server_id,
4352 symbol.language_server_name.clone(),
4353 cx,
4354 )
4355 } else if let Some(project_id) = self.remote_id() {
4356 let request = self.client.request(proto::OpenBufferForSymbol {
4357 project_id,
4358 symbol: Some(serialize_symbol(symbol)),
4359 });
4360 cx.spawn(|this, mut cx| async move {
4361 let response = request.await?;
4362 this.update(&mut cx, |this, cx| {
4363 this.wait_for_remote_buffer(response.buffer_id, cx)
4364 })
4365 .await
4366 })
4367 } else {
4368 Task::ready(Err(anyhow!("project does not have a remote id")))
4369 }
4370 }
4371
4372 pub fn hover<T: ToPointUtf16>(
4373 &self,
4374 buffer: &ModelHandle<Buffer>,
4375 position: T,
4376 cx: &mut ModelContext<Self>,
4377 ) -> Task<Result<Option<Hover>>> {
4378 let position = position.to_point_utf16(buffer.read(cx));
4379 self.request_lsp(buffer.clone(), GetHover { position }, cx)
4380 }
4381
4382 pub fn completions<T: ToPointUtf16>(
4383 &self,
4384 buffer: &ModelHandle<Buffer>,
4385 position: T,
4386 cx: &mut ModelContext<Self>,
4387 ) -> Task<Result<Vec<Completion>>> {
4388 let position = position.to_point_utf16(buffer.read(cx));
4389 self.request_lsp(buffer.clone(), GetCompletions { position }, cx)
4390 }
4391
4392 pub fn apply_additional_edits_for_completion(
4393 &self,
4394 buffer_handle: ModelHandle<Buffer>,
4395 completion: Completion,
4396 push_to_history: bool,
4397 cx: &mut ModelContext<Self>,
4398 ) -> Task<Result<Option<Transaction>>> {
4399 let buffer = buffer_handle.read(cx);
4400 let buffer_id = buffer.remote_id();
4401
4402 if self.is_local() {
4403 let lang_server = match self.primary_language_servers_for_buffer(buffer, cx) {
4404 Some((_, server)) => server.clone(),
4405 _ => return Task::ready(Ok(Default::default())),
4406 };
4407
4408 cx.spawn(|this, mut cx| async move {
4409 let resolved_completion = lang_server
4410 .request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion)
4411 .await?;
4412
4413 if let Some(edits) = resolved_completion.additional_text_edits {
4414 let edits = this
4415 .update(&mut cx, |this, cx| {
4416 this.edits_from_lsp(
4417 &buffer_handle,
4418 edits,
4419 lang_server.server_id(),
4420 None,
4421 cx,
4422 )
4423 })
4424 .await?;
4425
4426 buffer_handle.update(&mut cx, |buffer, cx| {
4427 buffer.finalize_last_transaction();
4428 buffer.start_transaction();
4429
4430 for (range, text) in edits {
4431 let primary = &completion.old_range;
4432 let start_within = primary.start.cmp(&range.start, buffer).is_le()
4433 && primary.end.cmp(&range.start, buffer).is_ge();
4434 let end_within = range.start.cmp(&primary.end, buffer).is_le()
4435 && range.end.cmp(&primary.end, buffer).is_ge();
4436
4437 //Skip additional edits which overlap with the primary completion edit
4438 //https://github.com/zed-industries/zed/pull/1871
4439 if !start_within && !end_within {
4440 buffer.edit([(range, text)], None, cx);
4441 }
4442 }
4443
4444 let transaction = if buffer.end_transaction(cx).is_some() {
4445 let transaction = buffer.finalize_last_transaction().unwrap().clone();
4446 if !push_to_history {
4447 buffer.forget_transaction(transaction.id);
4448 }
4449 Some(transaction)
4450 } else {
4451 None
4452 };
4453 Ok(transaction)
4454 })
4455 } else {
4456 Ok(None)
4457 }
4458 })
4459 } else if let Some(project_id) = self.remote_id() {
4460 let client = self.client.clone();
4461 cx.spawn(|_, mut cx| async move {
4462 let response = client
4463 .request(proto::ApplyCompletionAdditionalEdits {
4464 project_id,
4465 buffer_id,
4466 completion: Some(language::proto::serialize_completion(&completion)),
4467 })
4468 .await?;
4469
4470 if let Some(transaction) = response.transaction {
4471 let transaction = language::proto::deserialize_transaction(transaction)?;
4472 buffer_handle
4473 .update(&mut cx, |buffer, _| {
4474 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
4475 })
4476 .await?;
4477 if push_to_history {
4478 buffer_handle.update(&mut cx, |buffer, _| {
4479 buffer.push_transaction(transaction.clone(), Instant::now());
4480 });
4481 }
4482 Ok(Some(transaction))
4483 } else {
4484 Ok(None)
4485 }
4486 })
4487 } else {
4488 Task::ready(Err(anyhow!("project does not have a remote id")))
4489 }
4490 }
4491
4492 pub fn code_actions<T: Clone + ToOffset>(
4493 &self,
4494 buffer_handle: &ModelHandle<Buffer>,
4495 range: Range<T>,
4496 cx: &mut ModelContext<Self>,
4497 ) -> Task<Result<Vec<CodeAction>>> {
4498 let buffer = buffer_handle.read(cx);
4499 let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
4500 self.request_lsp(buffer_handle.clone(), GetCodeActions { range }, cx)
4501 }
4502
4503 pub fn apply_code_action(
4504 &self,
4505 buffer_handle: ModelHandle<Buffer>,
4506 mut action: CodeAction,
4507 push_to_history: bool,
4508 cx: &mut ModelContext<Self>,
4509 ) -> Task<Result<ProjectTransaction>> {
4510 if self.is_local() {
4511 let buffer = buffer_handle.read(cx);
4512 let (lsp_adapter, lang_server) = if let Some((adapter, server)) =
4513 self.language_server_for_buffer(buffer, action.server_id, cx)
4514 {
4515 (adapter.clone(), server.clone())
4516 } else {
4517 return Task::ready(Ok(Default::default()));
4518 };
4519 let range = action.range.to_point_utf16(buffer);
4520
4521 cx.spawn(|this, mut cx| async move {
4522 if let Some(lsp_range) = action
4523 .lsp_action
4524 .data
4525 .as_mut()
4526 .and_then(|d| d.get_mut("codeActionParams"))
4527 .and_then(|d| d.get_mut("range"))
4528 {
4529 *lsp_range = serde_json::to_value(&range_to_lsp(range)).unwrap();
4530 action.lsp_action = lang_server
4531 .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
4532 .await?;
4533 } else {
4534 let actions = this
4535 .update(&mut cx, |this, cx| {
4536 this.code_actions(&buffer_handle, action.range, cx)
4537 })
4538 .await?;
4539 action.lsp_action = actions
4540 .into_iter()
4541 .find(|a| a.lsp_action.title == action.lsp_action.title)
4542 .ok_or_else(|| anyhow!("code action is outdated"))?
4543 .lsp_action;
4544 }
4545
4546 if let Some(edit) = action.lsp_action.edit {
4547 if edit.changes.is_some() || edit.document_changes.is_some() {
4548 return Self::deserialize_workspace_edit(
4549 this,
4550 edit,
4551 push_to_history,
4552 lsp_adapter.clone(),
4553 lang_server.clone(),
4554 &mut cx,
4555 )
4556 .await;
4557 }
4558 }
4559
4560 if let Some(command) = action.lsp_action.command {
4561 this.update(&mut cx, |this, _| {
4562 this.last_workspace_edits_by_language_server
4563 .remove(&lang_server.server_id());
4564 });
4565
4566 let result = lang_server
4567 .request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
4568 command: command.command,
4569 arguments: command.arguments.unwrap_or_default(),
4570 ..Default::default()
4571 })
4572 .await;
4573
4574 if let Err(err) = result {
4575 // TODO: LSP ERROR
4576 return Err(err);
4577 }
4578
4579 return Ok(this.update(&mut cx, |this, _| {
4580 this.last_workspace_edits_by_language_server
4581 .remove(&lang_server.server_id())
4582 .unwrap_or_default()
4583 }));
4584 }
4585
4586 Ok(ProjectTransaction::default())
4587 })
4588 } else if let Some(project_id) = self.remote_id() {
4589 let client = self.client.clone();
4590 let request = proto::ApplyCodeAction {
4591 project_id,
4592 buffer_id: buffer_handle.read(cx).remote_id(),
4593 action: Some(language::proto::serialize_code_action(&action)),
4594 };
4595 cx.spawn(|this, mut cx| async move {
4596 let response = client
4597 .request(request)
4598 .await?
4599 .transaction
4600 .ok_or_else(|| anyhow!("missing transaction"))?;
4601 this.update(&mut cx, |this, cx| {
4602 this.deserialize_project_transaction(response, push_to_history, cx)
4603 })
4604 .await
4605 })
4606 } else {
4607 Task::ready(Err(anyhow!("project does not have a remote id")))
4608 }
4609 }
4610
4611 fn apply_on_type_formatting(
4612 &self,
4613 buffer: ModelHandle<Buffer>,
4614 position: Anchor,
4615 trigger: String,
4616 cx: &mut ModelContext<Self>,
4617 ) -> Task<Result<Option<Transaction>>> {
4618 if self.is_local() {
4619 cx.spawn(|this, mut cx| async move {
4620 // Do not allow multiple concurrent formatting requests for the
4621 // same buffer.
4622 this.update(&mut cx, |this, cx| {
4623 this.buffers_being_formatted
4624 .insert(buffer.read(cx).remote_id())
4625 });
4626
4627 let _cleanup = defer({
4628 let this = this.clone();
4629 let mut cx = cx.clone();
4630 let closure_buffer = buffer.clone();
4631 move || {
4632 this.update(&mut cx, |this, cx| {
4633 this.buffers_being_formatted
4634 .remove(&closure_buffer.read(cx).remote_id());
4635 });
4636 }
4637 });
4638
4639 buffer
4640 .update(&mut cx, |buffer, _| {
4641 buffer.wait_for_edits(Some(position.timestamp))
4642 })
4643 .await?;
4644 this.update(&mut cx, |this, cx| {
4645 let position = position.to_point_utf16(buffer.read(cx));
4646 this.on_type_format(buffer, position, trigger, false, cx)
4647 })
4648 .await
4649 })
4650 } else if let Some(project_id) = self.remote_id() {
4651 let client = self.client.clone();
4652 let request = proto::OnTypeFormatting {
4653 project_id,
4654 buffer_id: buffer.read(cx).remote_id(),
4655 position: Some(serialize_anchor(&position)),
4656 trigger,
4657 version: serialize_version(&buffer.read(cx).version()),
4658 };
4659 cx.spawn(|_, _| async move {
4660 client
4661 .request(request)
4662 .await?
4663 .transaction
4664 .map(language::proto::deserialize_transaction)
4665 .transpose()
4666 })
4667 } else {
4668 Task::ready(Err(anyhow!("project does not have a remote id")))
4669 }
4670 }
4671
4672 async fn deserialize_edits(
4673 this: ModelHandle<Self>,
4674 buffer_to_edit: ModelHandle<Buffer>,
4675 edits: Vec<lsp::TextEdit>,
4676 push_to_history: bool,
4677 _: Arc<CachedLspAdapter>,
4678 language_server: Arc<LanguageServer>,
4679 cx: &mut AsyncAppContext,
4680 ) -> Result<Option<Transaction>> {
4681 let edits = this
4682 .update(cx, |this, cx| {
4683 this.edits_from_lsp(
4684 &buffer_to_edit,
4685 edits,
4686 language_server.server_id(),
4687 None,
4688 cx,
4689 )
4690 })
4691 .await?;
4692
4693 let transaction = buffer_to_edit.update(cx, |buffer, cx| {
4694 buffer.finalize_last_transaction();
4695 buffer.start_transaction();
4696 for (range, text) in edits {
4697 buffer.edit([(range, text)], None, cx);
4698 }
4699
4700 if buffer.end_transaction(cx).is_some() {
4701 let transaction = buffer.finalize_last_transaction().unwrap().clone();
4702 if !push_to_history {
4703 buffer.forget_transaction(transaction.id);
4704 }
4705 Some(transaction)
4706 } else {
4707 None
4708 }
4709 });
4710
4711 Ok(transaction)
4712 }
4713
4714 async fn deserialize_workspace_edit(
4715 this: ModelHandle<Self>,
4716 edit: lsp::WorkspaceEdit,
4717 push_to_history: bool,
4718 lsp_adapter: Arc<CachedLspAdapter>,
4719 language_server: Arc<LanguageServer>,
4720 cx: &mut AsyncAppContext,
4721 ) -> Result<ProjectTransaction> {
4722 let fs = this.read_with(cx, |this, _| this.fs.clone());
4723 let mut operations = Vec::new();
4724 if let Some(document_changes) = edit.document_changes {
4725 match document_changes {
4726 lsp::DocumentChanges::Edits(edits) => {
4727 operations.extend(edits.into_iter().map(lsp::DocumentChangeOperation::Edit))
4728 }
4729 lsp::DocumentChanges::Operations(ops) => operations = ops,
4730 }
4731 } else if let Some(changes) = edit.changes {
4732 operations.extend(changes.into_iter().map(|(uri, edits)| {
4733 lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
4734 text_document: lsp::OptionalVersionedTextDocumentIdentifier {
4735 uri,
4736 version: None,
4737 },
4738 edits: edits.into_iter().map(OneOf::Left).collect(),
4739 })
4740 }));
4741 }
4742
4743 let mut project_transaction = ProjectTransaction::default();
4744 for operation in operations {
4745 match operation {
4746 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(op)) => {
4747 let abs_path = op
4748 .uri
4749 .to_file_path()
4750 .map_err(|_| anyhow!("can't convert URI to path"))?;
4751
4752 if let Some(parent_path) = abs_path.parent() {
4753 fs.create_dir(parent_path).await?;
4754 }
4755 if abs_path.ends_with("/") {
4756 fs.create_dir(&abs_path).await?;
4757 } else {
4758 fs.create_file(&abs_path, op.options.map(Into::into).unwrap_or_default())
4759 .await?;
4760 }
4761 }
4762
4763 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Rename(op)) => {
4764 let source_abs_path = op
4765 .old_uri
4766 .to_file_path()
4767 .map_err(|_| anyhow!("can't convert URI to path"))?;
4768 let target_abs_path = op
4769 .new_uri
4770 .to_file_path()
4771 .map_err(|_| anyhow!("can't convert URI to path"))?;
4772 fs.rename(
4773 &source_abs_path,
4774 &target_abs_path,
4775 op.options.map(Into::into).unwrap_or_default(),
4776 )
4777 .await?;
4778 }
4779
4780 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Delete(op)) => {
4781 let abs_path = op
4782 .uri
4783 .to_file_path()
4784 .map_err(|_| anyhow!("can't convert URI to path"))?;
4785 let options = op.options.map(Into::into).unwrap_or_default();
4786 if abs_path.ends_with("/") {
4787 fs.remove_dir(&abs_path, options).await?;
4788 } else {
4789 fs.remove_file(&abs_path, options).await?;
4790 }
4791 }
4792
4793 lsp::DocumentChangeOperation::Edit(op) => {
4794 let buffer_to_edit = this
4795 .update(cx, |this, cx| {
4796 this.open_local_buffer_via_lsp(
4797 op.text_document.uri,
4798 language_server.server_id(),
4799 lsp_adapter.name.clone(),
4800 cx,
4801 )
4802 })
4803 .await?;
4804
4805 let edits = this
4806 .update(cx, |this, cx| {
4807 let edits = op.edits.into_iter().map(|edit| match edit {
4808 OneOf::Left(edit) => edit,
4809 OneOf::Right(edit) => edit.text_edit,
4810 });
4811 this.edits_from_lsp(
4812 &buffer_to_edit,
4813 edits,
4814 language_server.server_id(),
4815 op.text_document.version,
4816 cx,
4817 )
4818 })
4819 .await?;
4820
4821 let transaction = buffer_to_edit.update(cx, |buffer, cx| {
4822 buffer.finalize_last_transaction();
4823 buffer.start_transaction();
4824 for (range, text) in edits {
4825 buffer.edit([(range, text)], None, cx);
4826 }
4827 let transaction = if buffer.end_transaction(cx).is_some() {
4828 let transaction = buffer.finalize_last_transaction().unwrap().clone();
4829 if !push_to_history {
4830 buffer.forget_transaction(transaction.id);
4831 }
4832 Some(transaction)
4833 } else {
4834 None
4835 };
4836
4837 transaction
4838 });
4839 if let Some(transaction) = transaction {
4840 project_transaction.0.insert(buffer_to_edit, transaction);
4841 }
4842 }
4843 }
4844 }
4845
4846 Ok(project_transaction)
4847 }
4848
4849 pub fn prepare_rename<T: ToPointUtf16>(
4850 &self,
4851 buffer: ModelHandle<Buffer>,
4852 position: T,
4853 cx: &mut ModelContext<Self>,
4854 ) -> Task<Result<Option<Range<Anchor>>>> {
4855 let position = position.to_point_utf16(buffer.read(cx));
4856 self.request_lsp(buffer, PrepareRename { position }, cx)
4857 }
4858
4859 pub fn perform_rename<T: ToPointUtf16>(
4860 &self,
4861 buffer: ModelHandle<Buffer>,
4862 position: T,
4863 new_name: String,
4864 push_to_history: bool,
4865 cx: &mut ModelContext<Self>,
4866 ) -> Task<Result<ProjectTransaction>> {
4867 let position = position.to_point_utf16(buffer.read(cx));
4868 self.request_lsp(
4869 buffer,
4870 PerformRename {
4871 position,
4872 new_name,
4873 push_to_history,
4874 },
4875 cx,
4876 )
4877 }
4878
4879 pub fn on_type_format<T: ToPointUtf16>(
4880 &self,
4881 buffer: ModelHandle<Buffer>,
4882 position: T,
4883 trigger: String,
4884 push_to_history: bool,
4885 cx: &mut ModelContext<Self>,
4886 ) -> Task<Result<Option<Transaction>>> {
4887 let (position, tab_size) = buffer.read_with(cx, |buffer, cx| {
4888 let position = position.to_point_utf16(buffer);
4889 (
4890 position,
4891 language_settings(buffer.language_at(position).as_ref(), buffer.file(), cx)
4892 .tab_size,
4893 )
4894 });
4895 self.request_lsp(
4896 buffer.clone(),
4897 OnTypeFormatting {
4898 position,
4899 trigger,
4900 options: lsp_command::lsp_formatting_options(tab_size.get()).into(),
4901 push_to_history,
4902 },
4903 cx,
4904 )
4905 }
4906
4907 pub fn inlay_hints_for_buffer<T: ToOffset>(
4908 &self,
4909 buffer_handle: ModelHandle<Buffer>,
4910 range: Range<T>,
4911 cx: &mut ModelContext<Self>,
4912 ) -> Task<Result<Vec<InlayHint>>> {
4913 let buffer = buffer_handle.read(cx);
4914 let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
4915 self.request_lsp(buffer_handle, InlayHints { range }, cx)
4916 }
4917
4918 #[allow(clippy::type_complexity)]
4919 pub fn search(
4920 &self,
4921 query: SearchQuery,
4922 cx: &mut ModelContext<Self>,
4923 ) -> Task<Result<HashMap<ModelHandle<Buffer>, Vec<Range<Anchor>>>>> {
4924 if self.is_local() {
4925 let snapshots = self
4926 .visible_worktrees(cx)
4927 .filter_map(|tree| {
4928 let tree = tree.read(cx).as_local()?;
4929 Some(tree.snapshot())
4930 })
4931 .collect::<Vec<_>>();
4932
4933 let background = cx.background().clone();
4934 let path_count: usize = snapshots.iter().map(|s| s.visible_file_count()).sum();
4935 if path_count == 0 {
4936 return Task::ready(Ok(Default::default()));
4937 }
4938 let workers = background.num_cpus().min(path_count);
4939 let (matching_paths_tx, mut matching_paths_rx) = smol::channel::bounded(1024);
4940 cx.background()
4941 .spawn({
4942 let fs = self.fs.clone();
4943 let background = cx.background().clone();
4944 let query = query.clone();
4945 async move {
4946 let fs = &fs;
4947 let query = &query;
4948 let matching_paths_tx = &matching_paths_tx;
4949 let paths_per_worker = (path_count + workers - 1) / workers;
4950 let snapshots = &snapshots;
4951 background
4952 .scoped(|scope| {
4953 for worker_ix in 0..workers {
4954 let worker_start_ix = worker_ix * paths_per_worker;
4955 let worker_end_ix = worker_start_ix + paths_per_worker;
4956 scope.spawn(async move {
4957 let mut snapshot_start_ix = 0;
4958 let mut abs_path = PathBuf::new();
4959 for snapshot in snapshots {
4960 let snapshot_end_ix =
4961 snapshot_start_ix + snapshot.visible_file_count();
4962 if worker_end_ix <= snapshot_start_ix {
4963 break;
4964 } else if worker_start_ix > snapshot_end_ix {
4965 snapshot_start_ix = snapshot_end_ix;
4966 continue;
4967 } else {
4968 let start_in_snapshot = worker_start_ix
4969 .saturating_sub(snapshot_start_ix);
4970 let end_in_snapshot =
4971 cmp::min(worker_end_ix, snapshot_end_ix)
4972 - snapshot_start_ix;
4973
4974 for entry in snapshot
4975 .files(false, start_in_snapshot)
4976 .take(end_in_snapshot - start_in_snapshot)
4977 {
4978 if matching_paths_tx.is_closed() {
4979 break;
4980 }
4981 let matches = if query
4982 .file_matches(Some(&entry.path))
4983 {
4984 abs_path.clear();
4985 abs_path.push(&snapshot.abs_path());
4986 abs_path.push(&entry.path);
4987 if let Some(file) =
4988 fs.open_sync(&abs_path).await.log_err()
4989 {
4990 query.detect(file).unwrap_or(false)
4991 } else {
4992 false
4993 }
4994 } else {
4995 false
4996 };
4997
4998 if matches {
4999 let project_path =
5000 (snapshot.id(), entry.path.clone());
5001 if matching_paths_tx
5002 .send(project_path)
5003 .await
5004 .is_err()
5005 {
5006 break;
5007 }
5008 }
5009 }
5010
5011 snapshot_start_ix = snapshot_end_ix;
5012 }
5013 }
5014 });
5015 }
5016 })
5017 .await;
5018 }
5019 })
5020 .detach();
5021
5022 let (buffers_tx, buffers_rx) = smol::channel::bounded(1024);
5023 let open_buffers = self
5024 .opened_buffers
5025 .values()
5026 .filter_map(|b| b.upgrade(cx))
5027 .collect::<HashSet<_>>();
5028 cx.spawn(|this, cx| async move {
5029 for buffer in &open_buffers {
5030 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
5031 buffers_tx.send((buffer.clone(), snapshot)).await?;
5032 }
5033
5034 let open_buffers = Rc::new(RefCell::new(open_buffers));
5035 while let Some(project_path) = matching_paths_rx.next().await {
5036 if buffers_tx.is_closed() {
5037 break;
5038 }
5039
5040 let this = this.clone();
5041 let open_buffers = open_buffers.clone();
5042 let buffers_tx = buffers_tx.clone();
5043 cx.spawn(|mut cx| async move {
5044 if let Some(buffer) = this
5045 .update(&mut cx, |this, cx| this.open_buffer(project_path, cx))
5046 .await
5047 .log_err()
5048 {
5049 if open_buffers.borrow_mut().insert(buffer.clone()) {
5050 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
5051 buffers_tx.send((buffer, snapshot)).await?;
5052 }
5053 }
5054
5055 Ok::<_, anyhow::Error>(())
5056 })
5057 .detach();
5058 }
5059
5060 Ok::<_, anyhow::Error>(())
5061 })
5062 .detach_and_log_err(cx);
5063
5064 let background = cx.background().clone();
5065 cx.background().spawn(async move {
5066 let query = &query;
5067 let mut matched_buffers = Vec::new();
5068 for _ in 0..workers {
5069 matched_buffers.push(HashMap::default());
5070 }
5071 background
5072 .scoped(|scope| {
5073 for worker_matched_buffers in matched_buffers.iter_mut() {
5074 let mut buffers_rx = buffers_rx.clone();
5075 scope.spawn(async move {
5076 while let Some((buffer, snapshot)) = buffers_rx.next().await {
5077 let buffer_matches = if query.file_matches(
5078 snapshot.file().map(|file| file.path().as_ref()),
5079 ) {
5080 query
5081 .search(snapshot.as_rope())
5082 .await
5083 .iter()
5084 .map(|range| {
5085 snapshot.anchor_before(range.start)
5086 ..snapshot.anchor_after(range.end)
5087 })
5088 .collect()
5089 } else {
5090 Vec::new()
5091 };
5092 if !buffer_matches.is_empty() {
5093 worker_matched_buffers
5094 .insert(buffer.clone(), buffer_matches);
5095 }
5096 }
5097 });
5098 }
5099 })
5100 .await;
5101 Ok(matched_buffers.into_iter().flatten().collect())
5102 })
5103 } else if let Some(project_id) = self.remote_id() {
5104 let request = self.client.request(query.to_proto(project_id));
5105 cx.spawn(|this, mut cx| async move {
5106 let response = request.await?;
5107 let mut result = HashMap::default();
5108 for location in response.locations {
5109 let target_buffer = this
5110 .update(&mut cx, |this, cx| {
5111 this.wait_for_remote_buffer(location.buffer_id, cx)
5112 })
5113 .await?;
5114 let start = location
5115 .start
5116 .and_then(deserialize_anchor)
5117 .ok_or_else(|| anyhow!("missing target start"))?;
5118 let end = location
5119 .end
5120 .and_then(deserialize_anchor)
5121 .ok_or_else(|| anyhow!("missing target end"))?;
5122 result
5123 .entry(target_buffer)
5124 .or_insert(Vec::new())
5125 .push(start..end)
5126 }
5127 Ok(result)
5128 })
5129 } else {
5130 Task::ready(Ok(Default::default()))
5131 }
5132 }
5133
5134 // TODO: Wire this up to allow selecting a server?
5135 fn request_lsp<R: LspCommand>(
5136 &self,
5137 buffer_handle: ModelHandle<Buffer>,
5138 request: R,
5139 cx: &mut ModelContext<Self>,
5140 ) -> Task<Result<R::Response>>
5141 where
5142 <R::LspRequest as lsp::request::Request>::Result: Send,
5143 {
5144 let buffer = buffer_handle.read(cx);
5145 if self.is_local() {
5146 let file = File::from_dyn(buffer.file()).and_then(File::as_local);
5147 if let Some((file, language_server)) = file.zip(
5148 self.primary_language_servers_for_buffer(buffer, cx)
5149 .map(|(_, server)| server.clone()),
5150 ) {
5151 let lsp_params = request.to_lsp(&file.abs_path(cx), buffer, &language_server, cx);
5152 return cx.spawn(|this, cx| async move {
5153 if !request.check_capabilities(language_server.capabilities()) {
5154 return Ok(Default::default());
5155 }
5156
5157 let result = language_server.request::<R::LspRequest>(lsp_params).await;
5158 let response = match result {
5159 Ok(response) => response,
5160
5161 Err(err) => {
5162 log::warn!(
5163 "Generic lsp request to {} failed: {}",
5164 language_server.name(),
5165 err
5166 );
5167 return Err(err);
5168 }
5169 };
5170
5171 request
5172 .response_from_lsp(
5173 response,
5174 this,
5175 buffer_handle,
5176 language_server.server_id(),
5177 cx,
5178 )
5179 .await
5180 });
5181 }
5182 } else if let Some(project_id) = self.remote_id() {
5183 let rpc = self.client.clone();
5184 let message = request.to_proto(project_id, buffer);
5185 return cx.spawn_weak(|this, cx| async move {
5186 // Ensure the project is still alive by the time the task
5187 // is scheduled.
5188 this.upgrade(&cx)
5189 .ok_or_else(|| anyhow!("project dropped"))?;
5190
5191 let response = rpc.request(message).await?;
5192
5193 let this = this
5194 .upgrade(&cx)
5195 .ok_or_else(|| anyhow!("project dropped"))?;
5196 if this.read_with(&cx, |this, _| this.is_read_only()) {
5197 Err(anyhow!("disconnected before completing request"))
5198 } else {
5199 request
5200 .response_from_proto(response, this, buffer_handle, cx)
5201 .await
5202 }
5203 });
5204 }
5205 Task::ready(Ok(Default::default()))
5206 }
5207
5208 pub fn find_or_create_local_worktree(
5209 &mut self,
5210 abs_path: impl AsRef<Path>,
5211 visible: bool,
5212 cx: &mut ModelContext<Self>,
5213 ) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
5214 let abs_path = abs_path.as_ref();
5215 if let Some((tree, relative_path)) = self.find_local_worktree(abs_path, cx) {
5216 Task::ready(Ok((tree, relative_path)))
5217 } else {
5218 let worktree = self.create_local_worktree(abs_path, visible, cx);
5219 cx.foreground()
5220 .spawn(async move { Ok((worktree.await?, PathBuf::new())) })
5221 }
5222 }
5223
5224 pub fn find_local_worktree(
5225 &self,
5226 abs_path: &Path,
5227 cx: &AppContext,
5228 ) -> Option<(ModelHandle<Worktree>, PathBuf)> {
5229 for tree in &self.worktrees {
5230 if let Some(tree) = tree.upgrade(cx) {
5231 if let Some(relative_path) = tree
5232 .read(cx)
5233 .as_local()
5234 .and_then(|t| abs_path.strip_prefix(t.abs_path()).ok())
5235 {
5236 return Some((tree.clone(), relative_path.into()));
5237 }
5238 }
5239 }
5240 None
5241 }
5242
5243 pub fn is_shared(&self) -> bool {
5244 match &self.client_state {
5245 Some(ProjectClientState::Local { .. }) => true,
5246 _ => false,
5247 }
5248 }
5249
5250 fn create_local_worktree(
5251 &mut self,
5252 abs_path: impl AsRef<Path>,
5253 visible: bool,
5254 cx: &mut ModelContext<Self>,
5255 ) -> Task<Result<ModelHandle<Worktree>>> {
5256 let fs = self.fs.clone();
5257 let client = self.client.clone();
5258 let next_entry_id = self.next_entry_id.clone();
5259 let path: Arc<Path> = abs_path.as_ref().into();
5260 let task = self
5261 .loading_local_worktrees
5262 .entry(path.clone())
5263 .or_insert_with(|| {
5264 cx.spawn(|project, mut cx| {
5265 async move {
5266 let worktree = Worktree::local(
5267 client.clone(),
5268 path.clone(),
5269 visible,
5270 fs,
5271 next_entry_id,
5272 &mut cx,
5273 )
5274 .await;
5275
5276 project.update(&mut cx, |project, _| {
5277 project.loading_local_worktrees.remove(&path);
5278 });
5279
5280 let worktree = worktree?;
5281 project.update(&mut cx, |project, cx| project.add_worktree(&worktree, cx));
5282 Ok(worktree)
5283 }
5284 .map_err(Arc::new)
5285 })
5286 .shared()
5287 })
5288 .clone();
5289 cx.foreground().spawn(async move {
5290 match task.await {
5291 Ok(worktree) => Ok(worktree),
5292 Err(err) => Err(anyhow!("{}", err)),
5293 }
5294 })
5295 }
5296
5297 pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
5298 self.worktrees.retain(|worktree| {
5299 if let Some(worktree) = worktree.upgrade(cx) {
5300 let id = worktree.read(cx).id();
5301 if id == id_to_remove {
5302 cx.emit(Event::WorktreeRemoved(id));
5303 false
5304 } else {
5305 true
5306 }
5307 } else {
5308 false
5309 }
5310 });
5311 self.metadata_changed(cx);
5312 }
5313
5314 fn add_worktree(&mut self, worktree: &ModelHandle<Worktree>, cx: &mut ModelContext<Self>) {
5315 cx.observe(worktree, |_, _, cx| cx.notify()).detach();
5316 if worktree.read(cx).is_local() {
5317 cx.subscribe(worktree, |this, worktree, event, cx| match event {
5318 worktree::Event::UpdatedEntries(changes) => {
5319 this.update_local_worktree_buffers(&worktree, changes, cx);
5320 this.update_local_worktree_language_servers(&worktree, changes, cx);
5321 this.update_local_worktree_settings(&worktree, changes, cx);
5322 }
5323 worktree::Event::UpdatedGitRepositories(updated_repos) => {
5324 this.update_local_worktree_buffers_git_repos(worktree, updated_repos, cx)
5325 }
5326 })
5327 .detach();
5328 }
5329
5330 let push_strong_handle = {
5331 let worktree = worktree.read(cx);
5332 self.is_shared() || worktree.is_visible() || worktree.is_remote()
5333 };
5334 if push_strong_handle {
5335 self.worktrees
5336 .push(WorktreeHandle::Strong(worktree.clone()));
5337 } else {
5338 self.worktrees
5339 .push(WorktreeHandle::Weak(worktree.downgrade()));
5340 }
5341
5342 let handle_id = worktree.id();
5343 cx.observe_release(worktree, move |this, worktree, cx| {
5344 let _ = this.remove_worktree(worktree.id(), cx);
5345 cx.update_global::<SettingsStore, _, _>(|store, cx| {
5346 store.clear_local_settings(handle_id, cx).log_err()
5347 });
5348 })
5349 .detach();
5350
5351 cx.emit(Event::WorktreeAdded);
5352 self.metadata_changed(cx);
5353 }
5354
5355 fn update_local_worktree_buffers(
5356 &mut self,
5357 worktree_handle: &ModelHandle<Worktree>,
5358 changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
5359 cx: &mut ModelContext<Self>,
5360 ) {
5361 let snapshot = worktree_handle.read(cx).snapshot();
5362
5363 let mut renamed_buffers = Vec::new();
5364 for (path, entry_id, _) in changes {
5365 let worktree_id = worktree_handle.read(cx).id();
5366 let project_path = ProjectPath {
5367 worktree_id,
5368 path: path.clone(),
5369 };
5370
5371 let buffer_id = match self.local_buffer_ids_by_entry_id.get(entry_id) {
5372 Some(&buffer_id) => buffer_id,
5373 None => match self.local_buffer_ids_by_path.get(&project_path) {
5374 Some(&buffer_id) => buffer_id,
5375 None => continue,
5376 },
5377 };
5378
5379 let open_buffer = self.opened_buffers.get(&buffer_id);
5380 let buffer = if let Some(buffer) = open_buffer.and_then(|buffer| buffer.upgrade(cx)) {
5381 buffer
5382 } else {
5383 self.opened_buffers.remove(&buffer_id);
5384 self.local_buffer_ids_by_path.remove(&project_path);
5385 self.local_buffer_ids_by_entry_id.remove(entry_id);
5386 continue;
5387 };
5388
5389 buffer.update(cx, |buffer, cx| {
5390 if let Some(old_file) = File::from_dyn(buffer.file()) {
5391 if old_file.worktree != *worktree_handle {
5392 return;
5393 }
5394
5395 let new_file = if let Some(entry) = snapshot.entry_for_id(old_file.entry_id) {
5396 File {
5397 is_local: true,
5398 entry_id: entry.id,
5399 mtime: entry.mtime,
5400 path: entry.path.clone(),
5401 worktree: worktree_handle.clone(),
5402 is_deleted: false,
5403 }
5404 } else if let Some(entry) = snapshot.entry_for_path(old_file.path().as_ref()) {
5405 File {
5406 is_local: true,
5407 entry_id: entry.id,
5408 mtime: entry.mtime,
5409 path: entry.path.clone(),
5410 worktree: worktree_handle.clone(),
5411 is_deleted: false,
5412 }
5413 } else {
5414 File {
5415 is_local: true,
5416 entry_id: old_file.entry_id,
5417 path: old_file.path().clone(),
5418 mtime: old_file.mtime(),
5419 worktree: worktree_handle.clone(),
5420 is_deleted: true,
5421 }
5422 };
5423
5424 let old_path = old_file.abs_path(cx);
5425 if new_file.abs_path(cx) != old_path {
5426 renamed_buffers.push((cx.handle(), old_file.clone()));
5427 self.local_buffer_ids_by_path.remove(&project_path);
5428 self.local_buffer_ids_by_path.insert(
5429 ProjectPath {
5430 worktree_id,
5431 path: path.clone(),
5432 },
5433 buffer_id,
5434 );
5435 }
5436
5437 if new_file.entry_id != *entry_id {
5438 self.local_buffer_ids_by_entry_id.remove(entry_id);
5439 self.local_buffer_ids_by_entry_id
5440 .insert(new_file.entry_id, buffer_id);
5441 }
5442
5443 if new_file != *old_file {
5444 if let Some(project_id) = self.remote_id() {
5445 self.client
5446 .send(proto::UpdateBufferFile {
5447 project_id,
5448 buffer_id: buffer_id as u64,
5449 file: Some(new_file.to_proto()),
5450 })
5451 .log_err();
5452 }
5453
5454 buffer.file_updated(Arc::new(new_file), cx).detach();
5455 }
5456 }
5457 });
5458 }
5459
5460 for (buffer, old_file) in renamed_buffers {
5461 self.unregister_buffer_from_language_servers(&buffer, &old_file, cx);
5462 self.detect_language_for_buffer(&buffer, cx);
5463 self.register_buffer_with_language_servers(&buffer, cx);
5464 }
5465 }
5466
5467 fn update_local_worktree_language_servers(
5468 &mut self,
5469 worktree_handle: &ModelHandle<Worktree>,
5470 changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
5471 cx: &mut ModelContext<Self>,
5472 ) {
5473 if changes.is_empty() {
5474 return;
5475 }
5476
5477 let worktree_id = worktree_handle.read(cx).id();
5478 let mut language_server_ids = self
5479 .language_server_ids
5480 .iter()
5481 .filter_map(|((server_worktree_id, _), server_id)| {
5482 (*server_worktree_id == worktree_id).then_some(*server_id)
5483 })
5484 .collect::<Vec<_>>();
5485 language_server_ids.sort();
5486 language_server_ids.dedup();
5487
5488 let abs_path = worktree_handle.read(cx).abs_path();
5489 for server_id in &language_server_ids {
5490 if let Some(LanguageServerState::Running {
5491 server,
5492 watched_paths,
5493 ..
5494 }) = self.language_servers.get(server_id)
5495 {
5496 if let Some(watched_paths) = watched_paths.get(&worktree_id) {
5497 let params = lsp::DidChangeWatchedFilesParams {
5498 changes: changes
5499 .iter()
5500 .filter_map(|(path, _, change)| {
5501 if !watched_paths.is_match(&path) {
5502 return None;
5503 }
5504 let typ = match change {
5505 PathChange::Loaded => return None,
5506 PathChange::Added => lsp::FileChangeType::CREATED,
5507 PathChange::Removed => lsp::FileChangeType::DELETED,
5508 PathChange::Updated => lsp::FileChangeType::CHANGED,
5509 PathChange::AddedOrUpdated => lsp::FileChangeType::CHANGED,
5510 };
5511 Some(lsp::FileEvent {
5512 uri: lsp::Url::from_file_path(abs_path.join(path)).unwrap(),
5513 typ,
5514 })
5515 })
5516 .collect(),
5517 };
5518
5519 if !params.changes.is_empty() {
5520 server
5521 .notify::<lsp::notification::DidChangeWatchedFiles>(params)
5522 .log_err();
5523 }
5524 }
5525 }
5526 }
5527 }
5528
5529 fn update_local_worktree_buffers_git_repos(
5530 &mut self,
5531 worktree_handle: ModelHandle<Worktree>,
5532 changed_repos: &UpdatedGitRepositoriesSet,
5533 cx: &mut ModelContext<Self>,
5534 ) {
5535 debug_assert!(worktree_handle.read(cx).is_local());
5536
5537 // Identify the loading buffers whose containing repository that has changed.
5538 let future_buffers = self
5539 .loading_buffers_by_path
5540 .iter()
5541 .filter_map(|(project_path, receiver)| {
5542 if project_path.worktree_id != worktree_handle.read(cx).id() {
5543 return None;
5544 }
5545 let path = &project_path.path;
5546 changed_repos
5547 .iter()
5548 .find(|(work_dir, _)| path.starts_with(work_dir))?;
5549 let receiver = receiver.clone();
5550 let path = path.clone();
5551 Some(async move {
5552 wait_for_loading_buffer(receiver)
5553 .await
5554 .ok()
5555 .map(|buffer| (buffer, path))
5556 })
5557 })
5558 .collect::<FuturesUnordered<_>>();
5559
5560 // Identify the current buffers whose containing repository has changed.
5561 let current_buffers = self
5562 .opened_buffers
5563 .values()
5564 .filter_map(|buffer| {
5565 let buffer = buffer.upgrade(cx)?;
5566 let file = File::from_dyn(buffer.read(cx).file())?;
5567 if file.worktree != worktree_handle {
5568 return None;
5569 }
5570 let path = file.path();
5571 changed_repos
5572 .iter()
5573 .find(|(work_dir, _)| path.starts_with(work_dir))?;
5574 Some((buffer, path.clone()))
5575 })
5576 .collect::<Vec<_>>();
5577
5578 if future_buffers.len() + current_buffers.len() == 0 {
5579 return;
5580 }
5581
5582 let remote_id = self.remote_id();
5583 let client = self.client.clone();
5584 cx.spawn_weak(move |_, mut cx| async move {
5585 // Wait for all of the buffers to load.
5586 let future_buffers = future_buffers.collect::<Vec<_>>().await;
5587
5588 // Reload the diff base for every buffer whose containing git repository has changed.
5589 let snapshot =
5590 worktree_handle.read_with(&cx, |tree, _| tree.as_local().unwrap().snapshot());
5591 let diff_bases_by_buffer = cx
5592 .background()
5593 .spawn(async move {
5594 future_buffers
5595 .into_iter()
5596 .filter_map(|e| e)
5597 .chain(current_buffers)
5598 .filter_map(|(buffer, path)| {
5599 let (work_directory, repo) =
5600 snapshot.repository_and_work_directory_for_path(&path)?;
5601 let repo = snapshot.get_local_repo(&repo)?;
5602 let relative_path = path.strip_prefix(&work_directory).ok()?;
5603 let base_text = repo.repo_ptr.lock().load_index_text(&relative_path);
5604 Some((buffer, base_text))
5605 })
5606 .collect::<Vec<_>>()
5607 })
5608 .await;
5609
5610 // Assign the new diff bases on all of the buffers.
5611 for (buffer, diff_base) in diff_bases_by_buffer {
5612 let buffer_id = buffer.update(&mut cx, |buffer, cx| {
5613 buffer.set_diff_base(diff_base.clone(), cx);
5614 buffer.remote_id()
5615 });
5616 if let Some(project_id) = remote_id {
5617 client
5618 .send(proto::UpdateDiffBase {
5619 project_id,
5620 buffer_id,
5621 diff_base,
5622 })
5623 .log_err();
5624 }
5625 }
5626 })
5627 .detach();
5628 }
5629
5630 fn update_local_worktree_settings(
5631 &mut self,
5632 worktree: &ModelHandle<Worktree>,
5633 changes: &UpdatedEntriesSet,
5634 cx: &mut ModelContext<Self>,
5635 ) {
5636 let project_id = self.remote_id();
5637 let worktree_id = worktree.id();
5638 let worktree = worktree.read(cx).as_local().unwrap();
5639 let remote_worktree_id = worktree.id();
5640
5641 let mut settings_contents = Vec::new();
5642 for (path, _, change) in changes.iter() {
5643 if path.ends_with(&*LOCAL_SETTINGS_RELATIVE_PATH) {
5644 let settings_dir = Arc::from(
5645 path.ancestors()
5646 .nth(LOCAL_SETTINGS_RELATIVE_PATH.components().count())
5647 .unwrap(),
5648 );
5649 let fs = self.fs.clone();
5650 let removed = *change == PathChange::Removed;
5651 let abs_path = worktree.absolutize(path);
5652 settings_contents.push(async move {
5653 (settings_dir, (!removed).then_some(fs.load(&abs_path).await))
5654 });
5655 }
5656 }
5657
5658 if settings_contents.is_empty() {
5659 return;
5660 }
5661
5662 let client = self.client.clone();
5663 cx.spawn_weak(move |_, mut cx| async move {
5664 let settings_contents: Vec<(Arc<Path>, _)> =
5665 futures::future::join_all(settings_contents).await;
5666 cx.update(|cx| {
5667 cx.update_global::<SettingsStore, _, _>(|store, cx| {
5668 for (directory, file_content) in settings_contents {
5669 let file_content = file_content.and_then(|content| content.log_err());
5670 store
5671 .set_local_settings(
5672 worktree_id,
5673 directory.clone(),
5674 file_content.as_ref().map(String::as_str),
5675 cx,
5676 )
5677 .log_err();
5678 if let Some(remote_id) = project_id {
5679 client
5680 .send(proto::UpdateWorktreeSettings {
5681 project_id: remote_id,
5682 worktree_id: remote_worktree_id.to_proto(),
5683 path: directory.to_string_lossy().into_owned(),
5684 content: file_content,
5685 })
5686 .log_err();
5687 }
5688 }
5689 });
5690 });
5691 })
5692 .detach();
5693 }
5694
5695 pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
5696 let new_active_entry = entry.and_then(|project_path| {
5697 let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
5698 let entry = worktree.read(cx).entry_for_path(project_path.path)?;
5699 Some(entry.id)
5700 });
5701 if new_active_entry != self.active_entry {
5702 self.active_entry = new_active_entry;
5703 cx.emit(Event::ActiveEntryChanged(new_active_entry));
5704 }
5705 }
5706
5707 pub fn language_servers_running_disk_based_diagnostics(
5708 &self,
5709 ) -> impl Iterator<Item = LanguageServerId> + '_ {
5710 self.language_server_statuses
5711 .iter()
5712 .filter_map(|(id, status)| {
5713 if status.has_pending_diagnostic_updates {
5714 Some(*id)
5715 } else {
5716 None
5717 }
5718 })
5719 }
5720
5721 pub fn diagnostic_summary(&self, cx: &AppContext) -> DiagnosticSummary {
5722 let mut summary = DiagnosticSummary::default();
5723 for (_, _, path_summary) in self.diagnostic_summaries(cx) {
5724 summary.error_count += path_summary.error_count;
5725 summary.warning_count += path_summary.warning_count;
5726 }
5727 summary
5728 }
5729
5730 pub fn diagnostic_summaries<'a>(
5731 &'a self,
5732 cx: &'a AppContext,
5733 ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
5734 self.visible_worktrees(cx).flat_map(move |worktree| {
5735 let worktree = worktree.read(cx);
5736 let worktree_id = worktree.id();
5737 worktree
5738 .diagnostic_summaries()
5739 .map(move |(path, server_id, summary)| {
5740 (ProjectPath { worktree_id, path }, server_id, summary)
5741 })
5742 })
5743 }
5744
5745 pub fn disk_based_diagnostics_started(
5746 &mut self,
5747 language_server_id: LanguageServerId,
5748 cx: &mut ModelContext<Self>,
5749 ) {
5750 cx.emit(Event::DiskBasedDiagnosticsStarted { language_server_id });
5751 }
5752
5753 pub fn disk_based_diagnostics_finished(
5754 &mut self,
5755 language_server_id: LanguageServerId,
5756 cx: &mut ModelContext<Self>,
5757 ) {
5758 cx.emit(Event::DiskBasedDiagnosticsFinished { language_server_id });
5759 }
5760
5761 pub fn active_entry(&self) -> Option<ProjectEntryId> {
5762 self.active_entry
5763 }
5764
5765 pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Entry> {
5766 self.worktree_for_id(path.worktree_id, cx)?
5767 .read(cx)
5768 .entry_for_path(&path.path)
5769 .cloned()
5770 }
5771
5772 pub fn path_for_entry(&self, entry_id: ProjectEntryId, cx: &AppContext) -> Option<ProjectPath> {
5773 let worktree = self.worktree_for_entry(entry_id, cx)?;
5774 let worktree = worktree.read(cx);
5775 let worktree_id = worktree.id();
5776 let path = worktree.entry_for_id(entry_id)?.path.clone();
5777 Some(ProjectPath { worktree_id, path })
5778 }
5779
5780 pub fn absolute_path(&self, project_path: &ProjectPath, cx: &AppContext) -> Option<PathBuf> {
5781 let workspace_root = self
5782 .worktree_for_id(project_path.worktree_id, cx)?
5783 .read(cx)
5784 .abs_path();
5785 let project_path = project_path.path.as_ref();
5786
5787 Some(if project_path == Path::new("") {
5788 workspace_root.to_path_buf()
5789 } else {
5790 workspace_root.join(project_path)
5791 })
5792 }
5793
5794 // RPC message handlers
5795
5796 async fn handle_unshare_project(
5797 this: ModelHandle<Self>,
5798 _: TypedEnvelope<proto::UnshareProject>,
5799 _: Arc<Client>,
5800 mut cx: AsyncAppContext,
5801 ) -> Result<()> {
5802 this.update(&mut cx, |this, cx| {
5803 if this.is_local() {
5804 this.unshare(cx)?;
5805 } else {
5806 this.disconnected_from_host(cx);
5807 }
5808 Ok(())
5809 })
5810 }
5811
5812 async fn handle_add_collaborator(
5813 this: ModelHandle<Self>,
5814 mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
5815 _: Arc<Client>,
5816 mut cx: AsyncAppContext,
5817 ) -> Result<()> {
5818 let collaborator = envelope
5819 .payload
5820 .collaborator
5821 .take()
5822 .ok_or_else(|| anyhow!("empty collaborator"))?;
5823
5824 let collaborator = Collaborator::from_proto(collaborator)?;
5825 this.update(&mut cx, |this, cx| {
5826 this.shared_buffers.remove(&collaborator.peer_id);
5827 this.collaborators
5828 .insert(collaborator.peer_id, collaborator);
5829 cx.notify();
5830 });
5831
5832 Ok(())
5833 }
5834
5835 async fn handle_update_project_collaborator(
5836 this: ModelHandle<Self>,
5837 envelope: TypedEnvelope<proto::UpdateProjectCollaborator>,
5838 _: Arc<Client>,
5839 mut cx: AsyncAppContext,
5840 ) -> Result<()> {
5841 let old_peer_id = envelope
5842 .payload
5843 .old_peer_id
5844 .ok_or_else(|| anyhow!("missing old peer id"))?;
5845 let new_peer_id = envelope
5846 .payload
5847 .new_peer_id
5848 .ok_or_else(|| anyhow!("missing new peer id"))?;
5849 this.update(&mut cx, |this, cx| {
5850 let collaborator = this
5851 .collaborators
5852 .remove(&old_peer_id)
5853 .ok_or_else(|| anyhow!("received UpdateProjectCollaborator for unknown peer"))?;
5854 let is_host = collaborator.replica_id == 0;
5855 this.collaborators.insert(new_peer_id, collaborator);
5856
5857 let buffers = this.shared_buffers.remove(&old_peer_id);
5858 log::info!(
5859 "peer {} became {}. moving buffers {:?}",
5860 old_peer_id,
5861 new_peer_id,
5862 &buffers
5863 );
5864 if let Some(buffers) = buffers {
5865 this.shared_buffers.insert(new_peer_id, buffers);
5866 }
5867
5868 if is_host {
5869 this.opened_buffers
5870 .retain(|_, buffer| !matches!(buffer, OpenBuffer::Operations(_)));
5871 this.buffer_ordered_messages_tx
5872 .unbounded_send(BufferOrderedMessage::Resync)
5873 .unwrap();
5874 }
5875
5876 cx.emit(Event::CollaboratorUpdated {
5877 old_peer_id,
5878 new_peer_id,
5879 });
5880 cx.notify();
5881 Ok(())
5882 })
5883 }
5884
5885 async fn handle_remove_collaborator(
5886 this: ModelHandle<Self>,
5887 envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
5888 _: Arc<Client>,
5889 mut cx: AsyncAppContext,
5890 ) -> Result<()> {
5891 this.update(&mut cx, |this, cx| {
5892 let peer_id = envelope
5893 .payload
5894 .peer_id
5895 .ok_or_else(|| anyhow!("invalid peer id"))?;
5896 let replica_id = this
5897 .collaborators
5898 .remove(&peer_id)
5899 .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
5900 .replica_id;
5901 for buffer in this.opened_buffers.values() {
5902 if let Some(buffer) = buffer.upgrade(cx) {
5903 buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
5904 }
5905 }
5906 this.shared_buffers.remove(&peer_id);
5907
5908 cx.emit(Event::CollaboratorLeft(peer_id));
5909 cx.notify();
5910 Ok(())
5911 })
5912 }
5913
5914 async fn handle_update_project(
5915 this: ModelHandle<Self>,
5916 envelope: TypedEnvelope<proto::UpdateProject>,
5917 _: Arc<Client>,
5918 mut cx: AsyncAppContext,
5919 ) -> Result<()> {
5920 this.update(&mut cx, |this, cx| {
5921 // Don't handle messages that were sent before the response to us joining the project
5922 if envelope.message_id > this.join_project_response_message_id {
5923 this.set_worktrees_from_proto(envelope.payload.worktrees, cx)?;
5924 }
5925 Ok(())
5926 })
5927 }
5928
5929 async fn handle_update_worktree(
5930 this: ModelHandle<Self>,
5931 envelope: TypedEnvelope<proto::UpdateWorktree>,
5932 _: Arc<Client>,
5933 mut cx: AsyncAppContext,
5934 ) -> Result<()> {
5935 this.update(&mut cx, |this, cx| {
5936 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
5937 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
5938 worktree.update(cx, |worktree, _| {
5939 let worktree = worktree.as_remote_mut().unwrap();
5940 worktree.update_from_remote(envelope.payload);
5941 });
5942 }
5943 Ok(())
5944 })
5945 }
5946
5947 async fn handle_update_worktree_settings(
5948 this: ModelHandle<Self>,
5949 envelope: TypedEnvelope<proto::UpdateWorktreeSettings>,
5950 _: Arc<Client>,
5951 mut cx: AsyncAppContext,
5952 ) -> Result<()> {
5953 this.update(&mut cx, |this, cx| {
5954 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
5955 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
5956 cx.update_global::<SettingsStore, _, _>(|store, cx| {
5957 store
5958 .set_local_settings(
5959 worktree.id(),
5960 PathBuf::from(&envelope.payload.path).into(),
5961 envelope.payload.content.as_ref().map(String::as_str),
5962 cx,
5963 )
5964 .log_err();
5965 });
5966 }
5967 Ok(())
5968 })
5969 }
5970
5971 async fn handle_create_project_entry(
5972 this: ModelHandle<Self>,
5973 envelope: TypedEnvelope<proto::CreateProjectEntry>,
5974 _: Arc<Client>,
5975 mut cx: AsyncAppContext,
5976 ) -> Result<proto::ProjectEntryResponse> {
5977 let worktree = this.update(&mut cx, |this, cx| {
5978 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
5979 this.worktree_for_id(worktree_id, cx)
5980 .ok_or_else(|| anyhow!("worktree not found"))
5981 })?;
5982 let worktree_scan_id = worktree.read_with(&cx, |worktree, _| worktree.scan_id());
5983 let entry = worktree
5984 .update(&mut cx, |worktree, cx| {
5985 let worktree = worktree.as_local_mut().unwrap();
5986 let path = PathBuf::from(envelope.payload.path);
5987 worktree.create_entry(path, envelope.payload.is_directory, cx)
5988 })
5989 .await?;
5990 Ok(proto::ProjectEntryResponse {
5991 entry: Some((&entry).into()),
5992 worktree_scan_id: worktree_scan_id as u64,
5993 })
5994 }
5995
5996 async fn handle_rename_project_entry(
5997 this: ModelHandle<Self>,
5998 envelope: TypedEnvelope<proto::RenameProjectEntry>,
5999 _: Arc<Client>,
6000 mut cx: AsyncAppContext,
6001 ) -> Result<proto::ProjectEntryResponse> {
6002 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
6003 let worktree = this.read_with(&cx, |this, cx| {
6004 this.worktree_for_entry(entry_id, cx)
6005 .ok_or_else(|| anyhow!("worktree not found"))
6006 })?;
6007 let worktree_scan_id = worktree.read_with(&cx, |worktree, _| worktree.scan_id());
6008 let entry = worktree
6009 .update(&mut cx, |worktree, cx| {
6010 let new_path = PathBuf::from(envelope.payload.new_path);
6011 worktree
6012 .as_local_mut()
6013 .unwrap()
6014 .rename_entry(entry_id, new_path, cx)
6015 .ok_or_else(|| anyhow!("invalid entry"))
6016 })?
6017 .await?;
6018 Ok(proto::ProjectEntryResponse {
6019 entry: Some((&entry).into()),
6020 worktree_scan_id: worktree_scan_id as u64,
6021 })
6022 }
6023
6024 async fn handle_copy_project_entry(
6025 this: ModelHandle<Self>,
6026 envelope: TypedEnvelope<proto::CopyProjectEntry>,
6027 _: Arc<Client>,
6028 mut cx: AsyncAppContext,
6029 ) -> Result<proto::ProjectEntryResponse> {
6030 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
6031 let worktree = this.read_with(&cx, |this, cx| {
6032 this.worktree_for_entry(entry_id, cx)
6033 .ok_or_else(|| anyhow!("worktree not found"))
6034 })?;
6035 let worktree_scan_id = worktree.read_with(&cx, |worktree, _| worktree.scan_id());
6036 let entry = worktree
6037 .update(&mut cx, |worktree, cx| {
6038 let new_path = PathBuf::from(envelope.payload.new_path);
6039 worktree
6040 .as_local_mut()
6041 .unwrap()
6042 .copy_entry(entry_id, new_path, cx)
6043 .ok_or_else(|| anyhow!("invalid entry"))
6044 })?
6045 .await?;
6046 Ok(proto::ProjectEntryResponse {
6047 entry: Some((&entry).into()),
6048 worktree_scan_id: worktree_scan_id as u64,
6049 })
6050 }
6051
6052 async fn handle_delete_project_entry(
6053 this: ModelHandle<Self>,
6054 envelope: TypedEnvelope<proto::DeleteProjectEntry>,
6055 _: Arc<Client>,
6056 mut cx: AsyncAppContext,
6057 ) -> Result<proto::ProjectEntryResponse> {
6058 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
6059
6060 this.update(&mut cx, |_, cx| cx.emit(Event::DeletedEntry(entry_id)));
6061
6062 let worktree = this.read_with(&cx, |this, cx| {
6063 this.worktree_for_entry(entry_id, cx)
6064 .ok_or_else(|| anyhow!("worktree not found"))
6065 })?;
6066 let worktree_scan_id = worktree.read_with(&cx, |worktree, _| worktree.scan_id());
6067 worktree
6068 .update(&mut cx, |worktree, cx| {
6069 worktree
6070 .as_local_mut()
6071 .unwrap()
6072 .delete_entry(entry_id, cx)
6073 .ok_or_else(|| anyhow!("invalid entry"))
6074 })?
6075 .await?;
6076 Ok(proto::ProjectEntryResponse {
6077 entry: None,
6078 worktree_scan_id: worktree_scan_id as u64,
6079 })
6080 }
6081
6082 async fn handle_expand_project_entry(
6083 this: ModelHandle<Self>,
6084 envelope: TypedEnvelope<proto::ExpandProjectEntry>,
6085 _: Arc<Client>,
6086 mut cx: AsyncAppContext,
6087 ) -> Result<proto::ExpandProjectEntryResponse> {
6088 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
6089 let worktree = this
6090 .read_with(&cx, |this, cx| this.worktree_for_entry(entry_id, cx))
6091 .ok_or_else(|| anyhow!("invalid request"))?;
6092 worktree
6093 .update(&mut cx, |worktree, cx| {
6094 worktree
6095 .as_local_mut()
6096 .unwrap()
6097 .expand_entry(entry_id, cx)
6098 .ok_or_else(|| anyhow!("invalid entry"))
6099 })?
6100 .await?;
6101 let worktree_scan_id = worktree.read_with(&cx, |worktree, _| worktree.scan_id()) as u64;
6102 Ok(proto::ExpandProjectEntryResponse { worktree_scan_id })
6103 }
6104
6105 async fn handle_update_diagnostic_summary(
6106 this: ModelHandle<Self>,
6107 envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
6108 _: Arc<Client>,
6109 mut cx: AsyncAppContext,
6110 ) -> Result<()> {
6111 this.update(&mut cx, |this, cx| {
6112 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
6113 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
6114 if let Some(summary) = envelope.payload.summary {
6115 let project_path = ProjectPath {
6116 worktree_id,
6117 path: Path::new(&summary.path).into(),
6118 };
6119 worktree.update(cx, |worktree, _| {
6120 worktree
6121 .as_remote_mut()
6122 .unwrap()
6123 .update_diagnostic_summary(project_path.path.clone(), &summary);
6124 });
6125 cx.emit(Event::DiagnosticsUpdated {
6126 language_server_id: LanguageServerId(summary.language_server_id as usize),
6127 path: project_path,
6128 });
6129 }
6130 }
6131 Ok(())
6132 })
6133 }
6134
6135 async fn handle_start_language_server(
6136 this: ModelHandle<Self>,
6137 envelope: TypedEnvelope<proto::StartLanguageServer>,
6138 _: Arc<Client>,
6139 mut cx: AsyncAppContext,
6140 ) -> Result<()> {
6141 let server = envelope
6142 .payload
6143 .server
6144 .ok_or_else(|| anyhow!("invalid server"))?;
6145 this.update(&mut cx, |this, cx| {
6146 this.language_server_statuses.insert(
6147 LanguageServerId(server.id as usize),
6148 LanguageServerStatus {
6149 name: server.name,
6150 pending_work: Default::default(),
6151 has_pending_diagnostic_updates: false,
6152 progress_tokens: Default::default(),
6153 },
6154 );
6155 cx.notify();
6156 });
6157 Ok(())
6158 }
6159
6160 async fn handle_update_language_server(
6161 this: ModelHandle<Self>,
6162 envelope: TypedEnvelope<proto::UpdateLanguageServer>,
6163 _: Arc<Client>,
6164 mut cx: AsyncAppContext,
6165 ) -> Result<()> {
6166 this.update(&mut cx, |this, cx| {
6167 let language_server_id = LanguageServerId(envelope.payload.language_server_id as usize);
6168
6169 match envelope
6170 .payload
6171 .variant
6172 .ok_or_else(|| anyhow!("invalid variant"))?
6173 {
6174 proto::update_language_server::Variant::WorkStart(payload) => {
6175 this.on_lsp_work_start(
6176 language_server_id,
6177 payload.token,
6178 LanguageServerProgress {
6179 message: payload.message,
6180 percentage: payload.percentage.map(|p| p as usize),
6181 last_update_at: Instant::now(),
6182 },
6183 cx,
6184 );
6185 }
6186
6187 proto::update_language_server::Variant::WorkProgress(payload) => {
6188 this.on_lsp_work_progress(
6189 language_server_id,
6190 payload.token,
6191 LanguageServerProgress {
6192 message: payload.message,
6193 percentage: payload.percentage.map(|p| p as usize),
6194 last_update_at: Instant::now(),
6195 },
6196 cx,
6197 );
6198 }
6199
6200 proto::update_language_server::Variant::WorkEnd(payload) => {
6201 this.on_lsp_work_end(language_server_id, payload.token, cx);
6202 }
6203
6204 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(_) => {
6205 this.disk_based_diagnostics_started(language_server_id, cx);
6206 }
6207
6208 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(_) => {
6209 this.disk_based_diagnostics_finished(language_server_id, cx)
6210 }
6211 }
6212
6213 Ok(())
6214 })
6215 }
6216
6217 async fn handle_update_buffer(
6218 this: ModelHandle<Self>,
6219 envelope: TypedEnvelope<proto::UpdateBuffer>,
6220 _: Arc<Client>,
6221 mut cx: AsyncAppContext,
6222 ) -> Result<proto::Ack> {
6223 this.update(&mut cx, |this, cx| {
6224 let payload = envelope.payload.clone();
6225 let buffer_id = payload.buffer_id;
6226 let ops = payload
6227 .operations
6228 .into_iter()
6229 .map(language::proto::deserialize_operation)
6230 .collect::<Result<Vec<_>, _>>()?;
6231 let is_remote = this.is_remote();
6232 match this.opened_buffers.entry(buffer_id) {
6233 hash_map::Entry::Occupied(mut e) => match e.get_mut() {
6234 OpenBuffer::Strong(buffer) => {
6235 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
6236 }
6237 OpenBuffer::Operations(operations) => operations.extend_from_slice(&ops),
6238 OpenBuffer::Weak(_) => {}
6239 },
6240 hash_map::Entry::Vacant(e) => {
6241 assert!(
6242 is_remote,
6243 "received buffer update from {:?}",
6244 envelope.original_sender_id
6245 );
6246 e.insert(OpenBuffer::Operations(ops));
6247 }
6248 }
6249 Ok(proto::Ack {})
6250 })
6251 }
6252
6253 async fn handle_create_buffer_for_peer(
6254 this: ModelHandle<Self>,
6255 envelope: TypedEnvelope<proto::CreateBufferForPeer>,
6256 _: Arc<Client>,
6257 mut cx: AsyncAppContext,
6258 ) -> Result<()> {
6259 this.update(&mut cx, |this, cx| {
6260 match envelope
6261 .payload
6262 .variant
6263 .ok_or_else(|| anyhow!("missing variant"))?
6264 {
6265 proto::create_buffer_for_peer::Variant::State(mut state) => {
6266 let mut buffer_file = None;
6267 if let Some(file) = state.file.take() {
6268 let worktree_id = WorktreeId::from_proto(file.worktree_id);
6269 let worktree = this.worktree_for_id(worktree_id, cx).ok_or_else(|| {
6270 anyhow!("no worktree found for id {}", file.worktree_id)
6271 })?;
6272 buffer_file = Some(Arc::new(File::from_proto(file, worktree.clone(), cx)?)
6273 as Arc<dyn language::File>);
6274 }
6275
6276 let buffer_id = state.id;
6277 let buffer = cx.add_model(|_| {
6278 Buffer::from_proto(this.replica_id(), state, buffer_file).unwrap()
6279 });
6280 this.incomplete_remote_buffers
6281 .insert(buffer_id, Some(buffer));
6282 }
6283 proto::create_buffer_for_peer::Variant::Chunk(chunk) => {
6284 let buffer = this
6285 .incomplete_remote_buffers
6286 .get(&chunk.buffer_id)
6287 .cloned()
6288 .flatten()
6289 .ok_or_else(|| {
6290 anyhow!(
6291 "received chunk for buffer {} without initial state",
6292 chunk.buffer_id
6293 )
6294 })?;
6295 let operations = chunk
6296 .operations
6297 .into_iter()
6298 .map(language::proto::deserialize_operation)
6299 .collect::<Result<Vec<_>>>()?;
6300 buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?;
6301
6302 if chunk.is_last {
6303 this.incomplete_remote_buffers.remove(&chunk.buffer_id);
6304 this.register_buffer(&buffer, cx)?;
6305 }
6306 }
6307 }
6308
6309 Ok(())
6310 })
6311 }
6312
6313 async fn handle_update_diff_base(
6314 this: ModelHandle<Self>,
6315 envelope: TypedEnvelope<proto::UpdateDiffBase>,
6316 _: Arc<Client>,
6317 mut cx: AsyncAppContext,
6318 ) -> Result<()> {
6319 this.update(&mut cx, |this, cx| {
6320 let buffer_id = envelope.payload.buffer_id;
6321 let diff_base = envelope.payload.diff_base;
6322 if let Some(buffer) = this
6323 .opened_buffers
6324 .get_mut(&buffer_id)
6325 .and_then(|b| b.upgrade(cx))
6326 .or_else(|| {
6327 this.incomplete_remote_buffers
6328 .get(&buffer_id)
6329 .cloned()
6330 .flatten()
6331 })
6332 {
6333 buffer.update(cx, |buffer, cx| buffer.set_diff_base(diff_base, cx));
6334 }
6335 Ok(())
6336 })
6337 }
6338
6339 async fn handle_update_buffer_file(
6340 this: ModelHandle<Self>,
6341 envelope: TypedEnvelope<proto::UpdateBufferFile>,
6342 _: Arc<Client>,
6343 mut cx: AsyncAppContext,
6344 ) -> Result<()> {
6345 let buffer_id = envelope.payload.buffer_id;
6346
6347 this.update(&mut cx, |this, cx| {
6348 let payload = envelope.payload.clone();
6349 if let Some(buffer) = this
6350 .opened_buffers
6351 .get(&buffer_id)
6352 .and_then(|b| b.upgrade(cx))
6353 .or_else(|| {
6354 this.incomplete_remote_buffers
6355 .get(&buffer_id)
6356 .cloned()
6357 .flatten()
6358 })
6359 {
6360 let file = payload.file.ok_or_else(|| anyhow!("invalid file"))?;
6361 let worktree = this
6362 .worktree_for_id(WorktreeId::from_proto(file.worktree_id), cx)
6363 .ok_or_else(|| anyhow!("no such worktree"))?;
6364 let file = File::from_proto(file, worktree, cx)?;
6365 buffer.update(cx, |buffer, cx| {
6366 buffer.file_updated(Arc::new(file), cx).detach();
6367 });
6368 this.detect_language_for_buffer(&buffer, cx);
6369 }
6370 Ok(())
6371 })
6372 }
6373
6374 async fn handle_save_buffer(
6375 this: ModelHandle<Self>,
6376 envelope: TypedEnvelope<proto::SaveBuffer>,
6377 _: Arc<Client>,
6378 mut cx: AsyncAppContext,
6379 ) -> Result<proto::BufferSaved> {
6380 let buffer_id = envelope.payload.buffer_id;
6381 let (project_id, buffer) = this.update(&mut cx, |this, cx| {
6382 let project_id = this.remote_id().ok_or_else(|| anyhow!("not connected"))?;
6383 let buffer = this
6384 .opened_buffers
6385 .get(&buffer_id)
6386 .and_then(|buffer| buffer.upgrade(cx))
6387 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
6388 anyhow::Ok((project_id, buffer))
6389 })?;
6390 buffer
6391 .update(&mut cx, |buffer, _| {
6392 buffer.wait_for_version(deserialize_version(&envelope.payload.version))
6393 })
6394 .await?;
6395 let buffer_id = buffer.read_with(&cx, |buffer, _| buffer.remote_id());
6396
6397 this.update(&mut cx, |this, cx| this.save_buffer(buffer.clone(), cx))
6398 .await?;
6399 Ok(buffer.read_with(&cx, |buffer, _| proto::BufferSaved {
6400 project_id,
6401 buffer_id,
6402 version: serialize_version(buffer.saved_version()),
6403 mtime: Some(buffer.saved_mtime().into()),
6404 fingerprint: language::proto::serialize_fingerprint(buffer.saved_version_fingerprint()),
6405 }))
6406 }
6407
6408 async fn handle_reload_buffers(
6409 this: ModelHandle<Self>,
6410 envelope: TypedEnvelope<proto::ReloadBuffers>,
6411 _: Arc<Client>,
6412 mut cx: AsyncAppContext,
6413 ) -> Result<proto::ReloadBuffersResponse> {
6414 let sender_id = envelope.original_sender_id()?;
6415 let reload = this.update(&mut cx, |this, cx| {
6416 let mut buffers = HashSet::default();
6417 for buffer_id in &envelope.payload.buffer_ids {
6418 buffers.insert(
6419 this.opened_buffers
6420 .get(buffer_id)
6421 .and_then(|buffer| buffer.upgrade(cx))
6422 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
6423 );
6424 }
6425 Ok::<_, anyhow::Error>(this.reload_buffers(buffers, false, cx))
6426 })?;
6427
6428 let project_transaction = reload.await?;
6429 let project_transaction = this.update(&mut cx, |this, cx| {
6430 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
6431 });
6432 Ok(proto::ReloadBuffersResponse {
6433 transaction: Some(project_transaction),
6434 })
6435 }
6436
6437 async fn handle_synchronize_buffers(
6438 this: ModelHandle<Self>,
6439 envelope: TypedEnvelope<proto::SynchronizeBuffers>,
6440 _: Arc<Client>,
6441 mut cx: AsyncAppContext,
6442 ) -> Result<proto::SynchronizeBuffersResponse> {
6443 let project_id = envelope.payload.project_id;
6444 let mut response = proto::SynchronizeBuffersResponse {
6445 buffers: Default::default(),
6446 };
6447
6448 this.update(&mut cx, |this, cx| {
6449 let Some(guest_id) = envelope.original_sender_id else {
6450 error!("missing original_sender_id on SynchronizeBuffers request");
6451 return;
6452 };
6453
6454 this.shared_buffers.entry(guest_id).or_default().clear();
6455 for buffer in envelope.payload.buffers {
6456 let buffer_id = buffer.id;
6457 let remote_version = language::proto::deserialize_version(&buffer.version);
6458 if let Some(buffer) = this.buffer_for_id(buffer_id, cx) {
6459 this.shared_buffers
6460 .entry(guest_id)
6461 .or_default()
6462 .insert(buffer_id);
6463
6464 let buffer = buffer.read(cx);
6465 response.buffers.push(proto::BufferVersion {
6466 id: buffer_id,
6467 version: language::proto::serialize_version(&buffer.version),
6468 });
6469
6470 let operations = buffer.serialize_ops(Some(remote_version), cx);
6471 let client = this.client.clone();
6472 if let Some(file) = buffer.file() {
6473 client
6474 .send(proto::UpdateBufferFile {
6475 project_id,
6476 buffer_id: buffer_id as u64,
6477 file: Some(file.to_proto()),
6478 })
6479 .log_err();
6480 }
6481
6482 client
6483 .send(proto::UpdateDiffBase {
6484 project_id,
6485 buffer_id: buffer_id as u64,
6486 diff_base: buffer.diff_base().map(Into::into),
6487 })
6488 .log_err();
6489
6490 client
6491 .send(proto::BufferReloaded {
6492 project_id,
6493 buffer_id,
6494 version: language::proto::serialize_version(buffer.saved_version()),
6495 mtime: Some(buffer.saved_mtime().into()),
6496 fingerprint: language::proto::serialize_fingerprint(
6497 buffer.saved_version_fingerprint(),
6498 ),
6499 line_ending: language::proto::serialize_line_ending(
6500 buffer.line_ending(),
6501 ) as i32,
6502 })
6503 .log_err();
6504
6505 cx.background()
6506 .spawn(
6507 async move {
6508 let operations = operations.await;
6509 for chunk in split_operations(operations) {
6510 client
6511 .request(proto::UpdateBuffer {
6512 project_id,
6513 buffer_id,
6514 operations: chunk,
6515 })
6516 .await?;
6517 }
6518 anyhow::Ok(())
6519 }
6520 .log_err(),
6521 )
6522 .detach();
6523 }
6524 }
6525 });
6526
6527 Ok(response)
6528 }
6529
6530 async fn handle_format_buffers(
6531 this: ModelHandle<Self>,
6532 envelope: TypedEnvelope<proto::FormatBuffers>,
6533 _: Arc<Client>,
6534 mut cx: AsyncAppContext,
6535 ) -> Result<proto::FormatBuffersResponse> {
6536 let sender_id = envelope.original_sender_id()?;
6537 let format = this.update(&mut cx, |this, cx| {
6538 let mut buffers = HashSet::default();
6539 for buffer_id in &envelope.payload.buffer_ids {
6540 buffers.insert(
6541 this.opened_buffers
6542 .get(buffer_id)
6543 .and_then(|buffer| buffer.upgrade(cx))
6544 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
6545 );
6546 }
6547 let trigger = FormatTrigger::from_proto(envelope.payload.trigger);
6548 Ok::<_, anyhow::Error>(this.format(buffers, false, trigger, cx))
6549 })?;
6550
6551 let project_transaction = format.await?;
6552 let project_transaction = this.update(&mut cx, |this, cx| {
6553 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
6554 });
6555 Ok(proto::FormatBuffersResponse {
6556 transaction: Some(project_transaction),
6557 })
6558 }
6559
6560 async fn handle_apply_additional_edits_for_completion(
6561 this: ModelHandle<Self>,
6562 envelope: TypedEnvelope<proto::ApplyCompletionAdditionalEdits>,
6563 _: Arc<Client>,
6564 mut cx: AsyncAppContext,
6565 ) -> Result<proto::ApplyCompletionAdditionalEditsResponse> {
6566 let (buffer, completion) = this.update(&mut cx, |this, cx| {
6567 let buffer = this
6568 .opened_buffers
6569 .get(&envelope.payload.buffer_id)
6570 .and_then(|buffer| buffer.upgrade(cx))
6571 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
6572 let language = buffer.read(cx).language();
6573 let completion = language::proto::deserialize_completion(
6574 envelope
6575 .payload
6576 .completion
6577 .ok_or_else(|| anyhow!("invalid completion"))?,
6578 language.cloned(),
6579 );
6580 Ok::<_, anyhow::Error>((buffer, completion))
6581 })?;
6582
6583 let completion = completion.await?;
6584
6585 let apply_additional_edits = this.update(&mut cx, |this, cx| {
6586 this.apply_additional_edits_for_completion(buffer, completion, false, cx)
6587 });
6588
6589 Ok(proto::ApplyCompletionAdditionalEditsResponse {
6590 transaction: apply_additional_edits
6591 .await?
6592 .as_ref()
6593 .map(language::proto::serialize_transaction),
6594 })
6595 }
6596
6597 async fn handle_apply_code_action(
6598 this: ModelHandle<Self>,
6599 envelope: TypedEnvelope<proto::ApplyCodeAction>,
6600 _: Arc<Client>,
6601 mut cx: AsyncAppContext,
6602 ) -> Result<proto::ApplyCodeActionResponse> {
6603 let sender_id = envelope.original_sender_id()?;
6604 let action = language::proto::deserialize_code_action(
6605 envelope
6606 .payload
6607 .action
6608 .ok_or_else(|| anyhow!("invalid action"))?,
6609 )?;
6610 let apply_code_action = this.update(&mut cx, |this, cx| {
6611 let buffer = this
6612 .opened_buffers
6613 .get(&envelope.payload.buffer_id)
6614 .and_then(|buffer| buffer.upgrade(cx))
6615 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
6616 Ok::<_, anyhow::Error>(this.apply_code_action(buffer, action, false, cx))
6617 })?;
6618
6619 let project_transaction = apply_code_action.await?;
6620 let project_transaction = this.update(&mut cx, |this, cx| {
6621 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
6622 });
6623 Ok(proto::ApplyCodeActionResponse {
6624 transaction: Some(project_transaction),
6625 })
6626 }
6627
6628 async fn handle_on_type_formatting(
6629 this: ModelHandle<Self>,
6630 envelope: TypedEnvelope<proto::OnTypeFormatting>,
6631 _: Arc<Client>,
6632 mut cx: AsyncAppContext,
6633 ) -> Result<proto::OnTypeFormattingResponse> {
6634 let on_type_formatting = this.update(&mut cx, |this, cx| {
6635 let buffer = this
6636 .opened_buffers
6637 .get(&envelope.payload.buffer_id)
6638 .and_then(|buffer| buffer.upgrade(cx))
6639 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
6640 let position = envelope
6641 .payload
6642 .position
6643 .and_then(deserialize_anchor)
6644 .ok_or_else(|| anyhow!("invalid position"))?;
6645 Ok::<_, anyhow::Error>(this.apply_on_type_formatting(
6646 buffer,
6647 position,
6648 envelope.payload.trigger.clone(),
6649 cx,
6650 ))
6651 })?;
6652
6653 let transaction = on_type_formatting
6654 .await?
6655 .as_ref()
6656 .map(language::proto::serialize_transaction);
6657 Ok(proto::OnTypeFormattingResponse { transaction })
6658 }
6659
6660 async fn handle_inlay_hints(
6661 this: ModelHandle<Self>,
6662 envelope: TypedEnvelope<proto::InlayHints>,
6663 _: Arc<Client>,
6664 mut cx: AsyncAppContext,
6665 ) -> Result<proto::InlayHintsResponse> {
6666 let sender_id = envelope.original_sender_id()?;
6667 let buffer = this.update(&mut cx, |this, cx| {
6668 this.opened_buffers
6669 .get(&envelope.payload.buffer_id)
6670 .and_then(|buffer| buffer.upgrade(cx))
6671 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
6672 })?;
6673 let buffer_version = deserialize_version(&envelope.payload.version);
6674
6675 buffer
6676 .update(&mut cx, |buffer, _| {
6677 buffer.wait_for_version(buffer_version.clone())
6678 })
6679 .await
6680 .with_context(|| {
6681 format!(
6682 "waiting for version {:?} for buffer {}",
6683 buffer_version,
6684 buffer.id()
6685 )
6686 })?;
6687
6688 let buffer_hints = this
6689 .update(&mut cx, |project, cx| {
6690 let end = buffer.read(cx).len();
6691 project.inlay_hints_for_buffer(buffer, 0..end, cx)
6692 })
6693 .await
6694 .context("inlay hints fetch")?;
6695
6696 Ok(this.update(&mut cx, |project, cx| {
6697 InlayHints::response_to_proto(buffer_hints, project, sender_id, &buffer_version, cx)
6698 }))
6699 }
6700
6701 async fn handle_lsp_command<T: LspCommand>(
6702 this: ModelHandle<Self>,
6703 envelope: TypedEnvelope<T::ProtoRequest>,
6704 _: Arc<Client>,
6705 mut cx: AsyncAppContext,
6706 ) -> Result<<T::ProtoRequest as proto::RequestMessage>::Response>
6707 where
6708 <T::LspRequest as lsp::request::Request>::Result: Send,
6709 {
6710 let sender_id = envelope.original_sender_id()?;
6711 let buffer_id = T::buffer_id_from_proto(&envelope.payload);
6712 let buffer_handle = this.read_with(&cx, |this, _| {
6713 this.opened_buffers
6714 .get(&buffer_id)
6715 .and_then(|buffer| buffer.upgrade(&cx))
6716 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))
6717 })?;
6718 let request = T::from_proto(
6719 envelope.payload,
6720 this.clone(),
6721 buffer_handle.clone(),
6722 cx.clone(),
6723 )
6724 .await?;
6725 let buffer_version = buffer_handle.read_with(&cx, |buffer, _| buffer.version());
6726 let response = this
6727 .update(&mut cx, |this, cx| {
6728 this.request_lsp(buffer_handle, request, cx)
6729 })
6730 .await?;
6731 this.update(&mut cx, |this, cx| {
6732 Ok(T::response_to_proto(
6733 response,
6734 this,
6735 sender_id,
6736 &buffer_version,
6737 cx,
6738 ))
6739 })
6740 }
6741
6742 async fn handle_get_project_symbols(
6743 this: ModelHandle<Self>,
6744 envelope: TypedEnvelope<proto::GetProjectSymbols>,
6745 _: Arc<Client>,
6746 mut cx: AsyncAppContext,
6747 ) -> Result<proto::GetProjectSymbolsResponse> {
6748 let symbols = this
6749 .update(&mut cx, |this, cx| {
6750 this.symbols(&envelope.payload.query, cx)
6751 })
6752 .await?;
6753
6754 Ok(proto::GetProjectSymbolsResponse {
6755 symbols: symbols.iter().map(serialize_symbol).collect(),
6756 })
6757 }
6758
6759 async fn handle_search_project(
6760 this: ModelHandle<Self>,
6761 envelope: TypedEnvelope<proto::SearchProject>,
6762 _: Arc<Client>,
6763 mut cx: AsyncAppContext,
6764 ) -> Result<proto::SearchProjectResponse> {
6765 let peer_id = envelope.original_sender_id()?;
6766 let query = SearchQuery::from_proto(envelope.payload)?;
6767 let result = this
6768 .update(&mut cx, |this, cx| this.search(query, cx))
6769 .await?;
6770
6771 this.update(&mut cx, |this, cx| {
6772 let mut locations = Vec::new();
6773 for (buffer, ranges) in result {
6774 for range in ranges {
6775 let start = serialize_anchor(&range.start);
6776 let end = serialize_anchor(&range.end);
6777 let buffer_id = this.create_buffer_for_peer(&buffer, peer_id, cx);
6778 locations.push(proto::Location {
6779 buffer_id,
6780 start: Some(start),
6781 end: Some(end),
6782 });
6783 }
6784 }
6785 Ok(proto::SearchProjectResponse { locations })
6786 })
6787 }
6788
6789 async fn handle_open_buffer_for_symbol(
6790 this: ModelHandle<Self>,
6791 envelope: TypedEnvelope<proto::OpenBufferForSymbol>,
6792 _: Arc<Client>,
6793 mut cx: AsyncAppContext,
6794 ) -> Result<proto::OpenBufferForSymbolResponse> {
6795 let peer_id = envelope.original_sender_id()?;
6796 let symbol = envelope
6797 .payload
6798 .symbol
6799 .ok_or_else(|| anyhow!("invalid symbol"))?;
6800 let symbol = this
6801 .read_with(&cx, |this, _| this.deserialize_symbol(symbol))
6802 .await?;
6803 let symbol = this.read_with(&cx, |this, _| {
6804 let signature = this.symbol_signature(&symbol.path);
6805 if signature == symbol.signature {
6806 Ok(symbol)
6807 } else {
6808 Err(anyhow!("invalid symbol signature"))
6809 }
6810 })?;
6811 let buffer = this
6812 .update(&mut cx, |this, cx| this.open_buffer_for_symbol(&symbol, cx))
6813 .await?;
6814
6815 Ok(proto::OpenBufferForSymbolResponse {
6816 buffer_id: this.update(&mut cx, |this, cx| {
6817 this.create_buffer_for_peer(&buffer, peer_id, cx)
6818 }),
6819 })
6820 }
6821
6822 fn symbol_signature(&self, project_path: &ProjectPath) -> [u8; 32] {
6823 let mut hasher = Sha256::new();
6824 hasher.update(project_path.worktree_id.to_proto().to_be_bytes());
6825 hasher.update(project_path.path.to_string_lossy().as_bytes());
6826 hasher.update(self.nonce.to_be_bytes());
6827 hasher.finalize().as_slice().try_into().unwrap()
6828 }
6829
6830 async fn handle_open_buffer_by_id(
6831 this: ModelHandle<Self>,
6832 envelope: TypedEnvelope<proto::OpenBufferById>,
6833 _: Arc<Client>,
6834 mut cx: AsyncAppContext,
6835 ) -> Result<proto::OpenBufferResponse> {
6836 let peer_id = envelope.original_sender_id()?;
6837 let buffer = this
6838 .update(&mut cx, |this, cx| {
6839 this.open_buffer_by_id(envelope.payload.id, cx)
6840 })
6841 .await?;
6842 this.update(&mut cx, |this, cx| {
6843 Ok(proto::OpenBufferResponse {
6844 buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx),
6845 })
6846 })
6847 }
6848
6849 async fn handle_open_buffer_by_path(
6850 this: ModelHandle<Self>,
6851 envelope: TypedEnvelope<proto::OpenBufferByPath>,
6852 _: Arc<Client>,
6853 mut cx: AsyncAppContext,
6854 ) -> Result<proto::OpenBufferResponse> {
6855 let peer_id = envelope.original_sender_id()?;
6856 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
6857 let open_buffer = this.update(&mut cx, |this, cx| {
6858 this.open_buffer(
6859 ProjectPath {
6860 worktree_id,
6861 path: PathBuf::from(envelope.payload.path).into(),
6862 },
6863 cx,
6864 )
6865 });
6866
6867 let buffer = open_buffer.await?;
6868 this.update(&mut cx, |this, cx| {
6869 Ok(proto::OpenBufferResponse {
6870 buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx),
6871 })
6872 })
6873 }
6874
6875 fn serialize_project_transaction_for_peer(
6876 &mut self,
6877 project_transaction: ProjectTransaction,
6878 peer_id: proto::PeerId,
6879 cx: &mut AppContext,
6880 ) -> proto::ProjectTransaction {
6881 let mut serialized_transaction = proto::ProjectTransaction {
6882 buffer_ids: Default::default(),
6883 transactions: Default::default(),
6884 };
6885 for (buffer, transaction) in project_transaction.0 {
6886 serialized_transaction
6887 .buffer_ids
6888 .push(self.create_buffer_for_peer(&buffer, peer_id, cx));
6889 serialized_transaction
6890 .transactions
6891 .push(language::proto::serialize_transaction(&transaction));
6892 }
6893 serialized_transaction
6894 }
6895
6896 fn deserialize_project_transaction(
6897 &mut self,
6898 message: proto::ProjectTransaction,
6899 push_to_history: bool,
6900 cx: &mut ModelContext<Self>,
6901 ) -> Task<Result<ProjectTransaction>> {
6902 cx.spawn(|this, mut cx| async move {
6903 let mut project_transaction = ProjectTransaction::default();
6904 for (buffer_id, transaction) in message.buffer_ids.into_iter().zip(message.transactions)
6905 {
6906 let buffer = this
6907 .update(&mut cx, |this, cx| {
6908 this.wait_for_remote_buffer(buffer_id, cx)
6909 })
6910 .await?;
6911 let transaction = language::proto::deserialize_transaction(transaction)?;
6912 project_transaction.0.insert(buffer, transaction);
6913 }
6914
6915 for (buffer, transaction) in &project_transaction.0 {
6916 buffer
6917 .update(&mut cx, |buffer, _| {
6918 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
6919 })
6920 .await?;
6921
6922 if push_to_history {
6923 buffer.update(&mut cx, |buffer, _| {
6924 buffer.push_transaction(transaction.clone(), Instant::now());
6925 });
6926 }
6927 }
6928
6929 Ok(project_transaction)
6930 })
6931 }
6932
6933 fn create_buffer_for_peer(
6934 &mut self,
6935 buffer: &ModelHandle<Buffer>,
6936 peer_id: proto::PeerId,
6937 cx: &mut AppContext,
6938 ) -> u64 {
6939 let buffer_id = buffer.read(cx).remote_id();
6940 if let Some(ProjectClientState::Local { updates_tx, .. }) = &self.client_state {
6941 updates_tx
6942 .unbounded_send(LocalProjectUpdate::CreateBufferForPeer { peer_id, buffer_id })
6943 .ok();
6944 }
6945 buffer_id
6946 }
6947
6948 fn wait_for_remote_buffer(
6949 &mut self,
6950 id: u64,
6951 cx: &mut ModelContext<Self>,
6952 ) -> Task<Result<ModelHandle<Buffer>>> {
6953 let mut opened_buffer_rx = self.opened_buffer.1.clone();
6954
6955 cx.spawn_weak(|this, mut cx| async move {
6956 let buffer = loop {
6957 let Some(this) = this.upgrade(&cx) else {
6958 return Err(anyhow!("project dropped"));
6959 };
6960
6961 let buffer = this.read_with(&cx, |this, cx| {
6962 this.opened_buffers
6963 .get(&id)
6964 .and_then(|buffer| buffer.upgrade(cx))
6965 });
6966
6967 if let Some(buffer) = buffer {
6968 break buffer;
6969 } else if this.read_with(&cx, |this, _| this.is_read_only()) {
6970 return Err(anyhow!("disconnected before buffer {} could be opened", id));
6971 }
6972
6973 this.update(&mut cx, |this, _| {
6974 this.incomplete_remote_buffers.entry(id).or_default();
6975 });
6976 drop(this);
6977
6978 opened_buffer_rx
6979 .next()
6980 .await
6981 .ok_or_else(|| anyhow!("project dropped while waiting for buffer"))?;
6982 };
6983
6984 Ok(buffer)
6985 })
6986 }
6987
6988 fn synchronize_remote_buffers(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
6989 let project_id = match self.client_state.as_ref() {
6990 Some(ProjectClientState::Remote {
6991 sharing_has_stopped,
6992 remote_id,
6993 ..
6994 }) => {
6995 if *sharing_has_stopped {
6996 return Task::ready(Err(anyhow!(
6997 "can't synchronize remote buffers on a readonly project"
6998 )));
6999 } else {
7000 *remote_id
7001 }
7002 }
7003 Some(ProjectClientState::Local { .. }) | None => {
7004 return Task::ready(Err(anyhow!(
7005 "can't synchronize remote buffers on a local project"
7006 )))
7007 }
7008 };
7009
7010 let client = self.client.clone();
7011 cx.spawn(|this, cx| async move {
7012 let (buffers, incomplete_buffer_ids) = this.read_with(&cx, |this, cx| {
7013 let buffers = this
7014 .opened_buffers
7015 .iter()
7016 .filter_map(|(id, buffer)| {
7017 let buffer = buffer.upgrade(cx)?;
7018 Some(proto::BufferVersion {
7019 id: *id,
7020 version: language::proto::serialize_version(&buffer.read(cx).version),
7021 })
7022 })
7023 .collect();
7024 let incomplete_buffer_ids = this
7025 .incomplete_remote_buffers
7026 .keys()
7027 .copied()
7028 .collect::<Vec<_>>();
7029
7030 (buffers, incomplete_buffer_ids)
7031 });
7032 let response = client
7033 .request(proto::SynchronizeBuffers {
7034 project_id,
7035 buffers,
7036 })
7037 .await?;
7038
7039 let send_updates_for_buffers = response.buffers.into_iter().map(|buffer| {
7040 let client = client.clone();
7041 let buffer_id = buffer.id;
7042 let remote_version = language::proto::deserialize_version(&buffer.version);
7043 this.read_with(&cx, |this, cx| {
7044 if let Some(buffer) = this.buffer_for_id(buffer_id, cx) {
7045 let operations = buffer.read(cx).serialize_ops(Some(remote_version), cx);
7046 cx.background().spawn(async move {
7047 let operations = operations.await;
7048 for chunk in split_operations(operations) {
7049 client
7050 .request(proto::UpdateBuffer {
7051 project_id,
7052 buffer_id,
7053 operations: chunk,
7054 })
7055 .await?;
7056 }
7057 anyhow::Ok(())
7058 })
7059 } else {
7060 Task::ready(Ok(()))
7061 }
7062 })
7063 });
7064
7065 // Any incomplete buffers have open requests waiting. Request that the host sends
7066 // creates these buffers for us again to unblock any waiting futures.
7067 for id in incomplete_buffer_ids {
7068 cx.background()
7069 .spawn(client.request(proto::OpenBufferById { project_id, id }))
7070 .detach();
7071 }
7072
7073 futures::future::join_all(send_updates_for_buffers)
7074 .await
7075 .into_iter()
7076 .collect()
7077 })
7078 }
7079
7080 pub fn worktree_metadata_protos(&self, cx: &AppContext) -> Vec<proto::WorktreeMetadata> {
7081 self.worktrees(cx)
7082 .map(|worktree| {
7083 let worktree = worktree.read(cx);
7084 proto::WorktreeMetadata {
7085 id: worktree.id().to_proto(),
7086 root_name: worktree.root_name().into(),
7087 visible: worktree.is_visible(),
7088 abs_path: worktree.abs_path().to_string_lossy().into(),
7089 }
7090 })
7091 .collect()
7092 }
7093
7094 fn set_worktrees_from_proto(
7095 &mut self,
7096 worktrees: Vec<proto::WorktreeMetadata>,
7097 cx: &mut ModelContext<Project>,
7098 ) -> Result<()> {
7099 let replica_id = self.replica_id();
7100 let remote_id = self.remote_id().ok_or_else(|| anyhow!("invalid project"))?;
7101
7102 let mut old_worktrees_by_id = self
7103 .worktrees
7104 .drain(..)
7105 .filter_map(|worktree| {
7106 let worktree = worktree.upgrade(cx)?;
7107 Some((worktree.read(cx).id(), worktree))
7108 })
7109 .collect::<HashMap<_, _>>();
7110
7111 for worktree in worktrees {
7112 if let Some(old_worktree) =
7113 old_worktrees_by_id.remove(&WorktreeId::from_proto(worktree.id))
7114 {
7115 self.worktrees.push(WorktreeHandle::Strong(old_worktree));
7116 } else {
7117 let worktree =
7118 Worktree::remote(remote_id, replica_id, worktree, self.client.clone(), cx);
7119 let _ = self.add_worktree(&worktree, cx);
7120 }
7121 }
7122
7123 self.metadata_changed(cx);
7124 for id in old_worktrees_by_id.keys() {
7125 cx.emit(Event::WorktreeRemoved(*id));
7126 }
7127
7128 Ok(())
7129 }
7130
7131 fn set_collaborators_from_proto(
7132 &mut self,
7133 messages: Vec<proto::Collaborator>,
7134 cx: &mut ModelContext<Self>,
7135 ) -> Result<()> {
7136 let mut collaborators = HashMap::default();
7137 for message in messages {
7138 let collaborator = Collaborator::from_proto(message)?;
7139 collaborators.insert(collaborator.peer_id, collaborator);
7140 }
7141 for old_peer_id in self.collaborators.keys() {
7142 if !collaborators.contains_key(old_peer_id) {
7143 cx.emit(Event::CollaboratorLeft(*old_peer_id));
7144 }
7145 }
7146 self.collaborators = collaborators;
7147 Ok(())
7148 }
7149
7150 fn deserialize_symbol(
7151 &self,
7152 serialized_symbol: proto::Symbol,
7153 ) -> impl Future<Output = Result<Symbol>> {
7154 let languages = self.languages.clone();
7155 async move {
7156 let source_worktree_id = WorktreeId::from_proto(serialized_symbol.source_worktree_id);
7157 let worktree_id = WorktreeId::from_proto(serialized_symbol.worktree_id);
7158 let start = serialized_symbol
7159 .start
7160 .ok_or_else(|| anyhow!("invalid start"))?;
7161 let end = serialized_symbol
7162 .end
7163 .ok_or_else(|| anyhow!("invalid end"))?;
7164 let kind = unsafe { mem::transmute(serialized_symbol.kind) };
7165 let path = ProjectPath {
7166 worktree_id,
7167 path: PathBuf::from(serialized_symbol.path).into(),
7168 };
7169 let language = languages
7170 .language_for_file(&path.path, None)
7171 .await
7172 .log_err();
7173 Ok(Symbol {
7174 language_server_name: LanguageServerName(
7175 serialized_symbol.language_server_name.into(),
7176 ),
7177 source_worktree_id,
7178 path,
7179 label: {
7180 match language {
7181 Some(language) => {
7182 language
7183 .label_for_symbol(&serialized_symbol.name, kind)
7184 .await
7185 }
7186 None => None,
7187 }
7188 .unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None))
7189 },
7190
7191 name: serialized_symbol.name,
7192 range: Unclipped(PointUtf16::new(start.row, start.column))
7193 ..Unclipped(PointUtf16::new(end.row, end.column)),
7194 kind,
7195 signature: serialized_symbol
7196 .signature
7197 .try_into()
7198 .map_err(|_| anyhow!("invalid signature"))?,
7199 })
7200 }
7201 }
7202
7203 async fn handle_buffer_saved(
7204 this: ModelHandle<Self>,
7205 envelope: TypedEnvelope<proto::BufferSaved>,
7206 _: Arc<Client>,
7207 mut cx: AsyncAppContext,
7208 ) -> Result<()> {
7209 let fingerprint = deserialize_fingerprint(&envelope.payload.fingerprint)?;
7210 let version = deserialize_version(&envelope.payload.version);
7211 let mtime = envelope
7212 .payload
7213 .mtime
7214 .ok_or_else(|| anyhow!("missing mtime"))?
7215 .into();
7216
7217 this.update(&mut cx, |this, cx| {
7218 let buffer = this
7219 .opened_buffers
7220 .get(&envelope.payload.buffer_id)
7221 .and_then(|buffer| buffer.upgrade(cx))
7222 .or_else(|| {
7223 this.incomplete_remote_buffers
7224 .get(&envelope.payload.buffer_id)
7225 .and_then(|b| b.clone())
7226 });
7227 if let Some(buffer) = buffer {
7228 buffer.update(cx, |buffer, cx| {
7229 buffer.did_save(version, fingerprint, mtime, cx);
7230 });
7231 }
7232 Ok(())
7233 })
7234 }
7235
7236 async fn handle_buffer_reloaded(
7237 this: ModelHandle<Self>,
7238 envelope: TypedEnvelope<proto::BufferReloaded>,
7239 _: Arc<Client>,
7240 mut cx: AsyncAppContext,
7241 ) -> Result<()> {
7242 let payload = envelope.payload;
7243 let version = deserialize_version(&payload.version);
7244 let fingerprint = deserialize_fingerprint(&payload.fingerprint)?;
7245 let line_ending = deserialize_line_ending(
7246 proto::LineEnding::from_i32(payload.line_ending)
7247 .ok_or_else(|| anyhow!("missing line ending"))?,
7248 );
7249 let mtime = payload
7250 .mtime
7251 .ok_or_else(|| anyhow!("missing mtime"))?
7252 .into();
7253 this.update(&mut cx, |this, cx| {
7254 let buffer = this
7255 .opened_buffers
7256 .get(&payload.buffer_id)
7257 .and_then(|buffer| buffer.upgrade(cx))
7258 .or_else(|| {
7259 this.incomplete_remote_buffers
7260 .get(&payload.buffer_id)
7261 .cloned()
7262 .flatten()
7263 });
7264 if let Some(buffer) = buffer {
7265 buffer.update(cx, |buffer, cx| {
7266 buffer.did_reload(version, fingerprint, line_ending, mtime, cx);
7267 });
7268 }
7269 Ok(())
7270 })
7271 }
7272
7273 #[allow(clippy::type_complexity)]
7274 fn edits_from_lsp(
7275 &mut self,
7276 buffer: &ModelHandle<Buffer>,
7277 lsp_edits: impl 'static + Send + IntoIterator<Item = lsp::TextEdit>,
7278 server_id: LanguageServerId,
7279 version: Option<i32>,
7280 cx: &mut ModelContext<Self>,
7281 ) -> Task<Result<Vec<(Range<Anchor>, String)>>> {
7282 let snapshot = self.buffer_snapshot_for_lsp_version(buffer, server_id, version, cx);
7283 cx.background().spawn(async move {
7284 let snapshot = snapshot?;
7285 let mut lsp_edits = lsp_edits
7286 .into_iter()
7287 .map(|edit| (range_from_lsp(edit.range), edit.new_text))
7288 .collect::<Vec<_>>();
7289 lsp_edits.sort_by_key(|(range, _)| range.start);
7290
7291 let mut lsp_edits = lsp_edits.into_iter().peekable();
7292 let mut edits = Vec::new();
7293 while let Some((range, mut new_text)) = lsp_edits.next() {
7294 // Clip invalid ranges provided by the language server.
7295 let mut range = snapshot.clip_point_utf16(range.start, Bias::Left)
7296 ..snapshot.clip_point_utf16(range.end, Bias::Left);
7297
7298 // Combine any LSP edits that are adjacent.
7299 //
7300 // Also, combine LSP edits that are separated from each other by only
7301 // a newline. This is important because for some code actions,
7302 // Rust-analyzer rewrites the entire buffer via a series of edits that
7303 // are separated by unchanged newline characters.
7304 //
7305 // In order for the diffing logic below to work properly, any edits that
7306 // cancel each other out must be combined into one.
7307 while let Some((next_range, next_text)) = lsp_edits.peek() {
7308 if next_range.start.0 > range.end {
7309 if next_range.start.0.row > range.end.row + 1
7310 || next_range.start.0.column > 0
7311 || snapshot.clip_point_utf16(
7312 Unclipped(PointUtf16::new(range.end.row, u32::MAX)),
7313 Bias::Left,
7314 ) > range.end
7315 {
7316 break;
7317 }
7318 new_text.push('\n');
7319 }
7320 range.end = snapshot.clip_point_utf16(next_range.end, Bias::Left);
7321 new_text.push_str(next_text);
7322 lsp_edits.next();
7323 }
7324
7325 // For multiline edits, perform a diff of the old and new text so that
7326 // we can identify the changes more precisely, preserving the locations
7327 // of any anchors positioned in the unchanged regions.
7328 if range.end.row > range.start.row {
7329 let mut offset = range.start.to_offset(&snapshot);
7330 let old_text = snapshot.text_for_range(range).collect::<String>();
7331
7332 let diff = TextDiff::from_lines(old_text.as_str(), &new_text);
7333 let mut moved_since_edit = true;
7334 for change in diff.iter_all_changes() {
7335 let tag = change.tag();
7336 let value = change.value();
7337 match tag {
7338 ChangeTag::Equal => {
7339 offset += value.len();
7340 moved_since_edit = true;
7341 }
7342 ChangeTag::Delete => {
7343 let start = snapshot.anchor_after(offset);
7344 let end = snapshot.anchor_before(offset + value.len());
7345 if moved_since_edit {
7346 edits.push((start..end, String::new()));
7347 } else {
7348 edits.last_mut().unwrap().0.end = end;
7349 }
7350 offset += value.len();
7351 moved_since_edit = false;
7352 }
7353 ChangeTag::Insert => {
7354 if moved_since_edit {
7355 let anchor = snapshot.anchor_after(offset);
7356 edits.push((anchor..anchor, value.to_string()));
7357 } else {
7358 edits.last_mut().unwrap().1.push_str(value);
7359 }
7360 moved_since_edit = false;
7361 }
7362 }
7363 }
7364 } else if range.end == range.start {
7365 let anchor = snapshot.anchor_after(range.start);
7366 edits.push((anchor..anchor, new_text));
7367 } else {
7368 let edit_start = snapshot.anchor_after(range.start);
7369 let edit_end = snapshot.anchor_before(range.end);
7370 edits.push((edit_start..edit_end, new_text));
7371 }
7372 }
7373
7374 Ok(edits)
7375 })
7376 }
7377
7378 fn buffer_snapshot_for_lsp_version(
7379 &mut self,
7380 buffer: &ModelHandle<Buffer>,
7381 server_id: LanguageServerId,
7382 version: Option<i32>,
7383 cx: &AppContext,
7384 ) -> Result<TextBufferSnapshot> {
7385 const OLD_VERSIONS_TO_RETAIN: i32 = 10;
7386
7387 if let Some(version) = version {
7388 let buffer_id = buffer.read(cx).remote_id();
7389 let snapshots = self
7390 .buffer_snapshots
7391 .get_mut(&buffer_id)
7392 .and_then(|m| m.get_mut(&server_id))
7393 .ok_or_else(|| {
7394 anyhow!("no snapshots found for buffer {buffer_id} and server {server_id}")
7395 })?;
7396
7397 let found_snapshot = snapshots
7398 .binary_search_by_key(&version, |e| e.version)
7399 .map(|ix| snapshots[ix].snapshot.clone())
7400 .map_err(|_| {
7401 anyhow!("snapshot not found for buffer {buffer_id} server {server_id} at version {version}")
7402 })?;
7403
7404 snapshots.retain(|snapshot| snapshot.version + OLD_VERSIONS_TO_RETAIN >= version);
7405 Ok(found_snapshot)
7406 } else {
7407 Ok((buffer.read(cx)).text_snapshot())
7408 }
7409 }
7410
7411 pub fn language_servers(
7412 &self,
7413 ) -> impl '_ + Iterator<Item = (LanguageServerId, LanguageServerName, WorktreeId)> {
7414 self.language_server_ids
7415 .iter()
7416 .map(|((worktree_id, server_name), server_id)| {
7417 (*server_id, server_name.clone(), *worktree_id)
7418 })
7419 }
7420
7421 pub fn language_server_for_id(&self, id: LanguageServerId) -> Option<Arc<LanguageServer>> {
7422 if let LanguageServerState::Running { server, .. } = self.language_servers.get(&id)? {
7423 Some(server.clone())
7424 } else {
7425 None
7426 }
7427 }
7428
7429 pub fn language_servers_for_buffer(
7430 &self,
7431 buffer: &Buffer,
7432 cx: &AppContext,
7433 ) -> impl Iterator<Item = (&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
7434 self.language_server_ids_for_buffer(buffer, cx)
7435 .into_iter()
7436 .filter_map(|server_id| {
7437 if let LanguageServerState::Running {
7438 adapter, server, ..
7439 } = self.language_servers.get(&server_id)?
7440 {
7441 Some((adapter, server))
7442 } else {
7443 None
7444 }
7445 })
7446 }
7447
7448 fn primary_language_servers_for_buffer(
7449 &self,
7450 buffer: &Buffer,
7451 cx: &AppContext,
7452 ) -> Option<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
7453 self.language_servers_for_buffer(buffer, cx).next()
7454 }
7455
7456 fn language_server_for_buffer(
7457 &self,
7458 buffer: &Buffer,
7459 server_id: LanguageServerId,
7460 cx: &AppContext,
7461 ) -> Option<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
7462 self.language_servers_for_buffer(buffer, cx)
7463 .find(|(_, s)| s.server_id() == server_id)
7464 }
7465
7466 fn language_server_ids_for_buffer(
7467 &self,
7468 buffer: &Buffer,
7469 cx: &AppContext,
7470 ) -> Vec<LanguageServerId> {
7471 if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
7472 let worktree_id = file.worktree_id(cx);
7473 language
7474 .lsp_adapters()
7475 .iter()
7476 .flat_map(|adapter| {
7477 let key = (worktree_id, adapter.name.clone());
7478 self.language_server_ids.get(&key).copied()
7479 })
7480 .collect()
7481 } else {
7482 Vec::new()
7483 }
7484 }
7485}
7486
7487fn glob_literal_prefix<'a>(glob: &'a str) -> &'a str {
7488 let mut literal_end = 0;
7489 for (i, part) in glob.split(path::MAIN_SEPARATOR).enumerate() {
7490 if part.contains(&['*', '?', '{', '}']) {
7491 break;
7492 } else {
7493 if i > 0 {
7494 // Acount for separator prior to this part
7495 literal_end += path::MAIN_SEPARATOR.len_utf8();
7496 }
7497 literal_end += part.len();
7498 }
7499 }
7500 &glob[..literal_end]
7501}
7502
7503impl WorktreeHandle {
7504 pub fn upgrade(&self, cx: &AppContext) -> Option<ModelHandle<Worktree>> {
7505 match self {
7506 WorktreeHandle::Strong(handle) => Some(handle.clone()),
7507 WorktreeHandle::Weak(handle) => handle.upgrade(cx),
7508 }
7509 }
7510
7511 pub fn handle_id(&self) -> usize {
7512 match self {
7513 WorktreeHandle::Strong(handle) => handle.id(),
7514 WorktreeHandle::Weak(handle) => handle.id(),
7515 }
7516 }
7517}
7518
7519impl OpenBuffer {
7520 pub fn upgrade(&self, cx: &impl BorrowAppContext) -> Option<ModelHandle<Buffer>> {
7521 match self {
7522 OpenBuffer::Strong(handle) => Some(handle.clone()),
7523 OpenBuffer::Weak(handle) => handle.upgrade(cx),
7524 OpenBuffer::Operations(_) => None,
7525 }
7526 }
7527}
7528
7529pub struct PathMatchCandidateSet {
7530 pub snapshot: Snapshot,
7531 pub include_ignored: bool,
7532 pub include_root_name: bool,
7533}
7534
7535impl<'a> fuzzy::PathMatchCandidateSet<'a> for PathMatchCandidateSet {
7536 type Candidates = PathMatchCandidateSetIter<'a>;
7537
7538 fn id(&self) -> usize {
7539 self.snapshot.id().to_usize()
7540 }
7541
7542 fn len(&self) -> usize {
7543 if self.include_ignored {
7544 self.snapshot.file_count()
7545 } else {
7546 self.snapshot.visible_file_count()
7547 }
7548 }
7549
7550 fn prefix(&self) -> Arc<str> {
7551 if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
7552 self.snapshot.root_name().into()
7553 } else if self.include_root_name {
7554 format!("{}/", self.snapshot.root_name()).into()
7555 } else {
7556 "".into()
7557 }
7558 }
7559
7560 fn candidates(&'a self, start: usize) -> Self::Candidates {
7561 PathMatchCandidateSetIter {
7562 traversal: self.snapshot.files(self.include_ignored, start),
7563 }
7564 }
7565}
7566
7567pub struct PathMatchCandidateSetIter<'a> {
7568 traversal: Traversal<'a>,
7569}
7570
7571impl<'a> Iterator for PathMatchCandidateSetIter<'a> {
7572 type Item = fuzzy::PathMatchCandidate<'a>;
7573
7574 fn next(&mut self) -> Option<Self::Item> {
7575 self.traversal.next().map(|entry| {
7576 if let EntryKind::File(char_bag) = entry.kind {
7577 fuzzy::PathMatchCandidate {
7578 path: &entry.path,
7579 char_bag,
7580 }
7581 } else {
7582 unreachable!()
7583 }
7584 })
7585 }
7586}
7587
7588impl Entity for Project {
7589 type Event = Event;
7590
7591 fn release(&mut self, cx: &mut gpui::AppContext) {
7592 match &self.client_state {
7593 Some(ProjectClientState::Local { .. }) => {
7594 let _ = self.unshare_internal(cx);
7595 }
7596 Some(ProjectClientState::Remote { remote_id, .. }) => {
7597 let _ = self.client.send(proto::LeaveProject {
7598 project_id: *remote_id,
7599 });
7600 self.disconnected_from_host_internal(cx);
7601 }
7602 _ => {}
7603 }
7604 }
7605
7606 fn app_will_quit(
7607 &mut self,
7608 _: &mut AppContext,
7609 ) -> Option<std::pin::Pin<Box<dyn 'static + Future<Output = ()>>>> {
7610 let shutdown_futures = self
7611 .language_servers
7612 .drain()
7613 .map(|(_, server_state)| async {
7614 use LanguageServerState::*;
7615 match server_state {
7616 Running { server, .. } => server.shutdown()?.await,
7617 Starting(task) => task.await?.shutdown()?.await,
7618 }
7619 })
7620 .collect::<Vec<_>>();
7621
7622 Some(
7623 async move {
7624 futures::future::join_all(shutdown_futures).await;
7625 }
7626 .boxed(),
7627 )
7628 }
7629}
7630
7631impl Collaborator {
7632 fn from_proto(message: proto::Collaborator) -> Result<Self> {
7633 Ok(Self {
7634 peer_id: message.peer_id.ok_or_else(|| anyhow!("invalid peer id"))?,
7635 replica_id: message.replica_id as ReplicaId,
7636 })
7637 }
7638}
7639
7640impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
7641 fn from((worktree_id, path): (WorktreeId, P)) -> Self {
7642 Self {
7643 worktree_id,
7644 path: path.as_ref().into(),
7645 }
7646 }
7647}
7648
7649impl ProjectLspAdapterDelegate {
7650 fn new(project: &Project, cx: &ModelContext<Project>) -> Arc<Self> {
7651 Arc::new(Self {
7652 project: cx.handle(),
7653 http_client: project.client.http_client(),
7654 })
7655 }
7656}
7657
7658impl LspAdapterDelegate for ProjectLspAdapterDelegate {
7659 fn show_notification(&self, message: &str, cx: &mut AppContext) {
7660 self.project
7661 .update(cx, |_, cx| cx.emit(Event::Notification(message.to_owned())));
7662 }
7663
7664 fn http_client(&self) -> Arc<dyn HttpClient> {
7665 self.http_client.clone()
7666 }
7667}
7668
7669fn split_operations(
7670 mut operations: Vec<proto::Operation>,
7671) -> impl Iterator<Item = Vec<proto::Operation>> {
7672 #[cfg(any(test, feature = "test-support"))]
7673 const CHUNK_SIZE: usize = 5;
7674
7675 #[cfg(not(any(test, feature = "test-support")))]
7676 const CHUNK_SIZE: usize = 100;
7677
7678 let mut done = false;
7679 std::iter::from_fn(move || {
7680 if done {
7681 return None;
7682 }
7683
7684 let operations = operations
7685 .drain(..cmp::min(CHUNK_SIZE, operations.len()))
7686 .collect::<Vec<_>>();
7687 if operations.is_empty() {
7688 done = true;
7689 }
7690 Some(operations)
7691 })
7692}
7693
7694fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
7695 proto::Symbol {
7696 language_server_name: symbol.language_server_name.0.to_string(),
7697 source_worktree_id: symbol.source_worktree_id.to_proto(),
7698 worktree_id: symbol.path.worktree_id.to_proto(),
7699 path: symbol.path.path.to_string_lossy().to_string(),
7700 name: symbol.name.clone(),
7701 kind: unsafe { mem::transmute(symbol.kind) },
7702 start: Some(proto::PointUtf16 {
7703 row: symbol.range.start.0.row,
7704 column: symbol.range.start.0.column,
7705 }),
7706 end: Some(proto::PointUtf16 {
7707 row: symbol.range.end.0.row,
7708 column: symbol.range.end.0.column,
7709 }),
7710 signature: symbol.signature.to_vec(),
7711 }
7712}
7713
7714fn relativize_path(base: &Path, path: &Path) -> PathBuf {
7715 let mut path_components = path.components();
7716 let mut base_components = base.components();
7717 let mut components: Vec<Component> = Vec::new();
7718 loop {
7719 match (path_components.next(), base_components.next()) {
7720 (None, None) => break,
7721 (Some(a), None) => {
7722 components.push(a);
7723 components.extend(path_components.by_ref());
7724 break;
7725 }
7726 (None, _) => components.push(Component::ParentDir),
7727 (Some(a), Some(b)) if components.is_empty() && a == b => (),
7728 (Some(a), Some(b)) if b == Component::CurDir => components.push(a),
7729 (Some(a), Some(_)) => {
7730 components.push(Component::ParentDir);
7731 for _ in base_components {
7732 components.push(Component::ParentDir);
7733 }
7734 components.push(a);
7735 components.extend(path_components.by_ref());
7736 break;
7737 }
7738 }
7739 }
7740 components.iter().map(|c| c.as_os_str()).collect()
7741}
7742
7743impl Item for Buffer {
7744 fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
7745 File::from_dyn(self.file()).and_then(|file| file.project_entry_id(cx))
7746 }
7747
7748 fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
7749 File::from_dyn(self.file()).map(|file| ProjectPath {
7750 worktree_id: file.worktree_id(cx),
7751 path: file.path().clone(),
7752 })
7753 }
7754}
7755
7756async fn wait_for_loading_buffer(
7757 mut receiver: postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
7758) -> Result<ModelHandle<Buffer>, Arc<anyhow::Error>> {
7759 loop {
7760 if let Some(result) = receiver.borrow().as_ref() {
7761 match result {
7762 Ok(buffer) => return Ok(buffer.to_owned()),
7763 Err(e) => return Err(e.to_owned()),
7764 }
7765 }
7766 receiver.next().await;
7767 }
7768}