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