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