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