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