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