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