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