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