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