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