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