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