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