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