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