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