project.rs

   1pub mod buffer_store;
   2pub mod connection_manager;
   3pub mod debounced_delay;
   4pub mod lsp_command;
   5pub mod lsp_ext_command;
   6pub mod lsp_store;
   7mod prettier_support;
   8pub mod project_settings;
   9pub mod search;
  10mod task_inventory;
  11pub mod terminals;
  12pub mod worktree_store;
  13
  14#[cfg(test)]
  15mod project_tests;
  16
  17mod environment;
  18pub mod search_history;
  19mod yarn;
  20
  21use anyhow::{anyhow, Context as _, Result};
  22use buffer_store::{BufferStore, BufferStoreEvent};
  23use client::{
  24    proto, Client, Collaborator, DevServerProjectId, PendingEntitySubscription, ProjectId,
  25    TypedEnvelope, UserStore,
  26};
  27use clock::ReplicaId;
  28use collections::{BTreeSet, HashMap, HashSet};
  29use debounced_delay::DebouncedDelay;
  30use environment::ProjectEnvironment;
  31use futures::{
  32    channel::mpsc::{self, UnboundedReceiver},
  33    future::try_join_all,
  34    stream::FuturesUnordered,
  35    AsyncWriteExt, FutureExt, StreamExt,
  36};
  37
  38use git::{blame::Blame, repository::GitRepository};
  39use gpui::{
  40    AnyModel, AppContext, AsyncAppContext, BorrowAppContext, Context, Entity, EventEmitter, Model,
  41    ModelContext, SharedString, Task, WeakModel, WindowContext,
  42};
  43use itertools::Itertools;
  44use language::{
  45    language_settings::{
  46        language_settings, FormatOnSave, Formatter, InlayHintKind, LanguageSettings,
  47        SelectedFormatter,
  48    },
  49    proto::{
  50        deserialize_anchor, serialize_anchor, serialize_line_ending, serialize_version,
  51        split_operations,
  52    },
  53    Buffer, CachedLspAdapter, Capability, CodeLabel, ContextProvider, DiagnosticEntry, Diff,
  54    Documentation, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName,
  55    PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped,
  56};
  57use lsp::{CompletionContext, DocumentHighlightKind, LanguageServer, LanguageServerId};
  58use lsp_command::*;
  59use node_runtime::NodeRuntime;
  60use parking_lot::{Mutex, RwLock};
  61use paths::{
  62    local_settings_file_relative_path, local_tasks_file_relative_path,
  63    local_vscode_tasks_file_relative_path,
  64};
  65use prettier_support::{DefaultPrettier, PrettierInstance};
  66use project_settings::{LspSettings, ProjectSettings};
  67use remote::SshSession;
  68use rpc::{
  69    proto::{AnyProtoClient, SSH_PROJECT_ID},
  70    ErrorCode,
  71};
  72use search::{SearchQuery, SearchResult};
  73use search_history::SearchHistory;
  74use settings::{watch_config_file, Settings, SettingsLocation, SettingsStore};
  75use smol::channel::Receiver;
  76use snippet::Snippet;
  77use snippet_provider::SnippetProvider;
  78use std::{
  79    borrow::Cow,
  80    ops::Range,
  81    path::{Component, Path, PathBuf},
  82    str,
  83    sync::Arc,
  84    time::Duration,
  85};
  86use task::{
  87    static_source::{StaticSource, TrackedFile},
  88    HideStrategy, RevealStrategy, Shell, TaskContext, TaskTemplate, TaskVariables, VariableName,
  89};
  90use terminals::Terminals;
  91use text::{Anchor, BufferId};
  92use util::{defer, paths::compare_paths, ResultExt as _};
  93use worktree::{CreatedEntry, Snapshot, Traversal};
  94use worktree_store::{WorktreeStore, WorktreeStoreEvent};
  95
  96pub use fs::*;
  97pub use language::Location;
  98#[cfg(any(test, feature = "test-support"))]
  99pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
 100pub use task_inventory::{
 101    BasicContextProvider, ContextProviderWithTasks, Inventory, TaskSourceKind,
 102};
 103pub use worktree::{
 104    Entry, EntryKind, File, LocalWorktree, PathChange, ProjectEntryId, RepositoryEntry,
 105    UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings,
 106    FS_WATCH_LATENCY,
 107};
 108
 109pub use buffer_store::ProjectTransaction;
 110pub use lsp_store::{
 111    DiagnosticSummary, LanguageServerLogType, LanguageServerProgress, LanguageServerPromptRequest,
 112    LanguageServerStatus, LanguageServerToQuery, LspStore, LspStoreEvent,
 113    ProjectLspAdapterDelegate, SERVER_PROGRESS_THROTTLE_TIMEOUT,
 114};
 115
 116const MAX_PROJECT_SEARCH_HISTORY_SIZE: usize = 500;
 117const MAX_SEARCH_RESULT_FILES: usize = 5_000;
 118const MAX_SEARCH_RESULT_RANGES: usize = 10_000;
 119
 120pub trait Item {
 121    fn try_open(
 122        project: &Model<Project>,
 123        path: &ProjectPath,
 124        cx: &mut AppContext,
 125    ) -> Option<Task<Result<Model<Self>>>>
 126    where
 127        Self: Sized;
 128    fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
 129    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
 130}
 131
 132#[derive(Clone)]
 133pub enum OpenedBufferEvent {
 134    Disconnected,
 135    Ok(BufferId),
 136    Err(BufferId, Arc<anyhow::Error>),
 137}
 138
 139/// Semantics-aware entity that is relevant to one or more [`Worktree`] with the files.
 140/// `Project` is responsible for tasks, LSP and collab queries, synchronizing worktree states accordingly.
 141/// Maps [`Worktree`] entries with its own logic using [`ProjectEntryId`] and [`ProjectPath`] structs.
 142///
 143/// Can be either local (for the project opened on the same host) or remote.(for collab projects, browsed by multiple remote users).
 144pub struct Project {
 145    active_entry: Option<ProjectEntryId>,
 146    buffer_ordered_messages_tx: mpsc::UnboundedSender<BufferOrderedMessage>,
 147    languages: Arc<LanguageRegistry>,
 148    client: Arc<client::Client>,
 149    current_lsp_settings: HashMap<Arc<str>, LspSettings>,
 150    join_project_response_message_id: u32,
 151    user_store: Model<UserStore>,
 152    fs: Arc<dyn Fs>,
 153    ssh_session: Option<Arc<SshSession>>,
 154    client_state: ProjectClientState,
 155    collaborators: HashMap<proto::PeerId, Collaborator>,
 156    client_subscriptions: Vec<client::Subscription>,
 157    worktree_store: Model<WorktreeStore>,
 158    buffer_store: Model<BufferStore>,
 159    lsp_store: Model<LspStore>,
 160    _subscriptions: Vec<gpui::Subscription>,
 161    buffers_needing_diff: HashSet<WeakModel<Buffer>>,
 162    git_diff_debouncer: DebouncedDelay<Self>,
 163    remotely_created_buffers: Arc<Mutex<RemotelyCreatedBuffers>>,
 164    terminals: Terminals,
 165    node: Option<Arc<dyn NodeRuntime>>,
 166    default_prettier: DefaultPrettier,
 167    prettiers_per_worktree: HashMap<WorktreeId, HashSet<Option<PathBuf>>>,
 168    prettier_instances: HashMap<PathBuf, PrettierInstance>,
 169    tasks: Model<Inventory>,
 170    hosted_project_id: Option<ProjectId>,
 171    dev_server_project_id: Option<client::DevServerProjectId>,
 172    search_history: SearchHistory,
 173    snippets: Model<SnippetProvider>,
 174    last_formatting_failure: Option<String>,
 175    buffers_being_formatted: HashSet<BufferId>,
 176    environment: Model<ProjectEnvironment>,
 177}
 178
 179#[derive(Default)]
 180struct RemotelyCreatedBuffers {
 181    buffers: Vec<Model<Buffer>>,
 182    retain_count: usize,
 183}
 184
 185struct RemotelyCreatedBufferGuard {
 186    remote_buffers: std::sync::Weak<Mutex<RemotelyCreatedBuffers>>,
 187}
 188
 189impl Drop for RemotelyCreatedBufferGuard {
 190    fn drop(&mut self) {
 191        if let Some(remote_buffers) = self.remote_buffers.upgrade() {
 192            let mut remote_buffers = remote_buffers.lock();
 193            assert!(
 194                remote_buffers.retain_count > 0,
 195                "RemotelyCreatedBufferGuard dropped too many times"
 196            );
 197            remote_buffers.retain_count -= 1;
 198            if remote_buffers.retain_count == 0 {
 199                remote_buffers.buffers.clear();
 200            }
 201        }
 202    }
 203}
 204/// Message ordered with respect to buffer operations
 205#[derive(Debug)]
 206enum BufferOrderedMessage {
 207    Operation {
 208        buffer_id: BufferId,
 209        operation: proto::Operation,
 210    },
 211    LanguageServerUpdate {
 212        language_server_id: LanguageServerId,
 213        message: proto::update_language_server::Variant,
 214    },
 215    Resync,
 216}
 217
 218#[derive(Debug)]
 219enum ProjectClientState {
 220    Local,
 221    Shared {
 222        remote_id: u64,
 223    },
 224    Remote {
 225        sharing_has_stopped: bool,
 226        capability: Capability,
 227        remote_id: u64,
 228        replica_id: ReplicaId,
 229        in_room: bool,
 230    },
 231}
 232
 233#[derive(Clone, Debug, PartialEq)]
 234pub enum Event {
 235    LanguageServerAdded(LanguageServerId),
 236    LanguageServerRemoved(LanguageServerId),
 237    LanguageServerLog(LanguageServerId, LanguageServerLogType, String),
 238    Notification(String),
 239    LanguageServerPrompt(LanguageServerPromptRequest),
 240    LanguageNotFound(Model<Buffer>),
 241    ActiveEntryChanged(Option<ProjectEntryId>),
 242    ActivateProjectPanel,
 243    WorktreeAdded,
 244    WorktreeOrderChanged,
 245    WorktreeRemoved(WorktreeId),
 246    WorktreeUpdatedEntries(WorktreeId, UpdatedEntriesSet),
 247    WorktreeUpdatedGitRepositories,
 248    DiskBasedDiagnosticsStarted {
 249        language_server_id: LanguageServerId,
 250    },
 251    DiskBasedDiagnosticsFinished {
 252        language_server_id: LanguageServerId,
 253    },
 254    DiagnosticsUpdated {
 255        path: ProjectPath,
 256        language_server_id: LanguageServerId,
 257    },
 258    RemoteIdChanged(Option<u64>),
 259    DisconnectedFromHost,
 260    Closed,
 261    DeletedEntry(ProjectEntryId),
 262    CollaboratorUpdated {
 263        old_peer_id: proto::PeerId,
 264        new_peer_id: proto::PeerId,
 265    },
 266    CollaboratorJoined(proto::PeerId),
 267    CollaboratorLeft(proto::PeerId),
 268    HostReshared,
 269    Reshared,
 270    Rejoined,
 271    RefreshInlayHints,
 272    RevealInProjectPanel(ProjectEntryId),
 273    SnippetEdit(BufferId, Vec<(lsp::Range, Snippet)>),
 274}
 275
 276#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
 277pub struct ProjectPath {
 278    pub worktree_id: WorktreeId,
 279    pub path: Arc<Path>,
 280}
 281
 282impl ProjectPath {
 283    pub fn from_proto(p: proto::ProjectPath) -> Self {
 284        Self {
 285            worktree_id: WorktreeId::from_proto(p.worktree_id),
 286            path: Arc::from(PathBuf::from(p.path)),
 287        }
 288    }
 289
 290    pub fn to_proto(&self) -> proto::ProjectPath {
 291        proto::ProjectPath {
 292            worktree_id: self.worktree_id.to_proto(),
 293            path: self.path.to_string_lossy().to_string(),
 294        }
 295    }
 296}
 297
 298#[derive(Debug, Clone, PartialEq, Eq)]
 299pub struct InlayHint {
 300    pub position: language::Anchor,
 301    pub label: InlayHintLabel,
 302    pub kind: Option<InlayHintKind>,
 303    pub padding_left: bool,
 304    pub padding_right: bool,
 305    pub tooltip: Option<InlayHintTooltip>,
 306    pub resolve_state: ResolveState,
 307}
 308
 309/// The user's intent behind a given completion confirmation
 310#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
 311pub enum CompletionIntent {
 312    /// The user intends to 'commit' this result, if possible
 313    /// completion confirmations should run side effects
 314    Complete,
 315    /// The user intends to continue 'composing' this completion
 316    /// completion confirmations should not run side effects and
 317    /// let the user continue composing their action
 318    Compose,
 319}
 320
 321impl CompletionIntent {
 322    pub fn is_complete(&self) -> bool {
 323        self == &Self::Complete
 324    }
 325
 326    pub fn is_compose(&self) -> bool {
 327        self == &Self::Compose
 328    }
 329}
 330
 331/// A completion provided by a language server
 332#[derive(Clone)]
 333pub struct Completion {
 334    /// The range of the buffer that will be replaced.
 335    pub old_range: Range<Anchor>,
 336    /// The new text that will be inserted.
 337    pub new_text: String,
 338    /// A label for this completion that is shown in the menu.
 339    pub label: CodeLabel,
 340    /// The id of the language server that produced this completion.
 341    pub server_id: LanguageServerId,
 342    /// The documentation for this completion.
 343    pub documentation: Option<Documentation>,
 344    /// The raw completion provided by the language server.
 345    pub lsp_completion: lsp::CompletionItem,
 346    /// An optional callback to invoke when this completion is confirmed.
 347    /// Returns, whether new completions should be retriggered after the current one.
 348    /// If `true` is returned, the editor will show a new completion menu after this completion is confirmed.
 349    /// if no confirmation is provided or `false` is returned, the completion will be committed.
 350    pub confirm: Option<Arc<dyn Send + Sync + Fn(CompletionIntent, &mut WindowContext) -> bool>>,
 351}
 352
 353impl std::fmt::Debug for Completion {
 354    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 355        f.debug_struct("Completion")
 356            .field("old_range", &self.old_range)
 357            .field("new_text", &self.new_text)
 358            .field("label", &self.label)
 359            .field("server_id", &self.server_id)
 360            .field("documentation", &self.documentation)
 361            .field("lsp_completion", &self.lsp_completion)
 362            .finish()
 363    }
 364}
 365
 366/// A completion provided by a language server
 367#[derive(Clone, Debug)]
 368pub(crate) struct CoreCompletion {
 369    old_range: Range<Anchor>,
 370    new_text: String,
 371    server_id: LanguageServerId,
 372    lsp_completion: lsp::CompletionItem,
 373}
 374
 375/// A code action provided by a language server.
 376#[derive(Clone, Debug)]
 377pub struct CodeAction {
 378    /// The id of the language server that produced this code action.
 379    pub server_id: LanguageServerId,
 380    /// The range of the buffer where this code action is applicable.
 381    pub range: Range<Anchor>,
 382    /// The raw code action provided by the language server.
 383    pub lsp_action: lsp::CodeAction,
 384}
 385
 386#[derive(Debug, Clone, PartialEq, Eq)]
 387pub enum ResolveState {
 388    Resolved,
 389    CanResolve(LanguageServerId, Option<lsp::LSPAny>),
 390    Resolving,
 391}
 392
 393impl InlayHint {
 394    pub fn text(&self) -> String {
 395        match &self.label {
 396            InlayHintLabel::String(s) => s.to_owned(),
 397            InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &part.value).join(""),
 398        }
 399    }
 400}
 401
 402#[derive(Debug, Clone, PartialEq, Eq)]
 403pub enum InlayHintLabel {
 404    String(String),
 405    LabelParts(Vec<InlayHintLabelPart>),
 406}
 407
 408#[derive(Debug, Clone, PartialEq, Eq)]
 409pub struct InlayHintLabelPart {
 410    pub value: String,
 411    pub tooltip: Option<InlayHintLabelPartTooltip>,
 412    pub location: Option<(LanguageServerId, lsp::Location)>,
 413}
 414
 415#[derive(Debug, Clone, PartialEq, Eq)]
 416pub enum InlayHintTooltip {
 417    String(String),
 418    MarkupContent(MarkupContent),
 419}
 420
 421#[derive(Debug, Clone, PartialEq, Eq)]
 422pub enum InlayHintLabelPartTooltip {
 423    String(String),
 424    MarkupContent(MarkupContent),
 425}
 426
 427#[derive(Debug, Clone, PartialEq, Eq)]
 428pub struct MarkupContent {
 429    pub kind: HoverBlockKind,
 430    pub value: String,
 431}
 432
 433#[derive(Debug, Clone)]
 434pub struct LocationLink {
 435    pub origin: Option<Location>,
 436    pub target: Location,
 437}
 438
 439#[derive(Debug)]
 440pub struct DocumentHighlight {
 441    pub range: Range<language::Anchor>,
 442    pub kind: DocumentHighlightKind,
 443}
 444
 445#[derive(Clone, Debug)]
 446pub struct Symbol {
 447    pub language_server_name: LanguageServerName,
 448    pub source_worktree_id: WorktreeId,
 449    pub path: ProjectPath,
 450    pub label: CodeLabel,
 451    pub name: String,
 452    pub kind: lsp::SymbolKind,
 453    pub range: Range<Unclipped<PointUtf16>>,
 454    pub signature: [u8; 32],
 455}
 456
 457#[derive(Clone, Debug, PartialEq)]
 458pub struct HoverBlock {
 459    pub text: String,
 460    pub kind: HoverBlockKind,
 461}
 462
 463#[derive(Clone, Debug, PartialEq, Eq)]
 464pub enum HoverBlockKind {
 465    PlainText,
 466    Markdown,
 467    Code { language: String },
 468}
 469
 470#[derive(Debug, Clone)]
 471pub struct Hover {
 472    pub contents: Vec<HoverBlock>,
 473    pub range: Option<Range<language::Anchor>>,
 474    pub language: Option<Arc<Language>>,
 475}
 476
 477impl Hover {
 478    pub fn is_empty(&self) -> bool {
 479        self.contents.iter().all(|block| block.text.is_empty())
 480    }
 481}
 482
 483#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 484pub enum FormatTrigger {
 485    Save,
 486    Manual,
 487}
 488
 489// Currently, formatting operations are represented differently depending on
 490// whether they come from a language server or an external command.
 491#[derive(Debug)]
 492enum FormatOperation {
 493    Lsp(Vec<(Range<Anchor>, String)>),
 494    External(Diff),
 495    Prettier(Diff),
 496}
 497
 498impl FormatTrigger {
 499    fn from_proto(value: i32) -> FormatTrigger {
 500        match value {
 501            0 => FormatTrigger::Save,
 502            1 => FormatTrigger::Manual,
 503            _ => FormatTrigger::Save,
 504        }
 505    }
 506}
 507
 508#[derive(Clone)]
 509pub enum DirectoryLister {
 510    Project(Model<Project>),
 511    Local(Arc<dyn Fs>),
 512}
 513
 514impl DirectoryLister {
 515    pub fn is_local(&self, cx: &AppContext) -> bool {
 516        match self {
 517            DirectoryLister::Local(_) => true,
 518            DirectoryLister::Project(project) => project.read(cx).is_local_or_ssh(),
 519        }
 520    }
 521
 522    pub fn resolve_tilde<'a>(&self, path: &'a String, cx: &AppContext) -> Cow<'a, str> {
 523        if self.is_local(cx) {
 524            shellexpand::tilde(path)
 525        } else {
 526            Cow::from(path)
 527        }
 528    }
 529
 530    pub fn default_query(&self, cx: &mut AppContext) -> String {
 531        if let DirectoryLister::Project(project) = self {
 532            if let Some(worktree) = project.read(cx).visible_worktrees(cx).next() {
 533                return worktree.read(cx).abs_path().to_string_lossy().to_string();
 534            }
 535        };
 536        "~/".to_string()
 537    }
 538
 539    pub fn list_directory(&self, path: String, cx: &mut AppContext) -> Task<Result<Vec<PathBuf>>> {
 540        match self {
 541            DirectoryLister::Project(project) => {
 542                project.update(cx, |project, cx| project.list_directory(path, cx))
 543            }
 544            DirectoryLister::Local(fs) => {
 545                let fs = fs.clone();
 546                cx.background_executor().spawn(async move {
 547                    let mut results = vec![];
 548                    let expanded = shellexpand::tilde(&path);
 549                    let query = Path::new(expanded.as_ref());
 550                    let mut response = fs.read_dir(query).await?;
 551                    while let Some(path) = response.next().await {
 552                        if let Some(file_name) = path?.file_name() {
 553                            results.push(PathBuf::from(file_name.to_os_string()));
 554                        }
 555                    }
 556                    Ok(results)
 557                })
 558            }
 559        }
 560    }
 561}
 562
 563#[cfg(any(test, feature = "test-support"))]
 564pub const DEFAULT_COMPLETION_CONTEXT: CompletionContext = CompletionContext {
 565    trigger_kind: lsp::CompletionTriggerKind::INVOKED,
 566    trigger_character: None,
 567};
 568
 569impl Project {
 570    pub fn init_settings(cx: &mut AppContext) {
 571        WorktreeSettings::register(cx);
 572        ProjectSettings::register(cx);
 573    }
 574
 575    pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
 576        connection_manager::init(client.clone(), cx);
 577        Self::init_settings(cx);
 578
 579        let client: AnyProtoClient = client.clone().into();
 580        client.add_model_message_handler(Self::handle_add_collaborator);
 581        client.add_model_message_handler(Self::handle_update_project_collaborator);
 582        client.add_model_message_handler(Self::handle_remove_collaborator);
 583        client.add_model_message_handler(Self::handle_update_project);
 584        client.add_model_message_handler(Self::handle_unshare_project);
 585        client.add_model_request_handler(Self::handle_update_buffer);
 586        client.add_model_message_handler(Self::handle_update_worktree);
 587        client.add_model_message_handler(Self::handle_update_worktree_settings);
 588        client.add_model_request_handler(Self::handle_reload_buffers);
 589        client.add_model_request_handler(Self::handle_synchronize_buffers);
 590        client.add_model_request_handler(Self::handle_format_buffers);
 591        client.add_model_request_handler(Self::handle_search_project);
 592        client.add_model_request_handler(Self::handle_search_candidate_buffers);
 593        client.add_model_request_handler(Self::handle_open_buffer_by_id);
 594        client.add_model_request_handler(Self::handle_open_buffer_by_path);
 595        client.add_model_request_handler(Self::handle_open_new_buffer);
 596        client.add_model_request_handler(Self::handle_task_context_for_location);
 597        client.add_model_request_handler(Self::handle_task_templates);
 598        client.add_model_message_handler(Self::handle_create_buffer_for_peer);
 599
 600        WorktreeStore::init(&client);
 601        BufferStore::init(&client);
 602        LspStore::init(&client);
 603    }
 604
 605    pub fn local(
 606        client: Arc<Client>,
 607        node: Arc<dyn NodeRuntime>,
 608        user_store: Model<UserStore>,
 609        languages: Arc<LanguageRegistry>,
 610        fs: Arc<dyn Fs>,
 611        env: Option<HashMap<String, String>>,
 612        cx: &mut AppContext,
 613    ) -> Model<Self> {
 614        cx.new_model(|cx: &mut ModelContext<Self>| {
 615            let (tx, rx) = mpsc::unbounded();
 616            cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
 617                .detach();
 618            let tasks = Inventory::new(cx);
 619            let global_snippets_dir = paths::config_dir().join("snippets");
 620            let snippets =
 621                SnippetProvider::new(fs.clone(), BTreeSet::from_iter([global_snippets_dir]), cx);
 622
 623            let worktree_store = cx.new_model(|_| WorktreeStore::new(false, fs.clone()));
 624            cx.subscribe(&worktree_store, Self::on_worktree_store_event)
 625                .detach();
 626
 627            let buffer_store =
 628                cx.new_model(|cx| BufferStore::new(worktree_store.clone(), None, cx));
 629            cx.subscribe(&buffer_store, Self::on_buffer_store_event)
 630                .detach();
 631
 632            let environment = ProjectEnvironment::new(env, cx);
 633            let lsp_store = cx.new_model(|cx| {
 634                LspStore::new(
 635                    buffer_store.clone(),
 636                    worktree_store.clone(),
 637                    Some(environment.clone()),
 638                    languages.clone(),
 639                    client.http_client(),
 640                    fs.clone(),
 641                    None,
 642                    None,
 643                    None,
 644                    cx,
 645                )
 646            });
 647            cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
 648
 649            Self {
 650                buffer_ordered_messages_tx: tx,
 651                collaborators: Default::default(),
 652                worktree_store,
 653                buffer_store,
 654                lsp_store,
 655                current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(),
 656                join_project_response_message_id: 0,
 657                client_state: ProjectClientState::Local,
 658                client_subscriptions: Vec::new(),
 659                _subscriptions: vec![
 660                    cx.observe_global::<SettingsStore>(Self::on_settings_changed),
 661                    cx.on_release(Self::release),
 662                ],
 663                active_entry: None,
 664                snippets,
 665                languages,
 666                client,
 667                user_store,
 668                fs,
 669                ssh_session: None,
 670                buffers_needing_diff: Default::default(),
 671                git_diff_debouncer: DebouncedDelay::new(),
 672                terminals: Terminals {
 673                    local_handles: Vec::new(),
 674                },
 675                node: Some(node),
 676                default_prettier: DefaultPrettier::default(),
 677                prettiers_per_worktree: HashMap::default(),
 678                prettier_instances: HashMap::default(),
 679                tasks,
 680                hosted_project_id: None,
 681                dev_server_project_id: None,
 682                search_history: Self::new_search_history(),
 683                environment,
 684                remotely_created_buffers: Default::default(),
 685                last_formatting_failure: None,
 686                buffers_being_formatted: Default::default(),
 687            }
 688        })
 689    }
 690
 691    pub fn ssh(
 692        ssh: Arc<SshSession>,
 693        client: Arc<Client>,
 694        node: Arc<dyn NodeRuntime>,
 695        user_store: Model<UserStore>,
 696        languages: Arc<LanguageRegistry>,
 697        fs: Arc<dyn Fs>,
 698        cx: &mut AppContext,
 699    ) -> Model<Self> {
 700        let this = Self::local(client, node, user_store, languages, fs, None, cx);
 701        this.update(cx, |this, cx| {
 702            let client: AnyProtoClient = ssh.clone().into();
 703
 704            this.worktree_store.update(cx, |store, _cx| {
 705                store.set_upstream_client(client.clone());
 706            });
 707
 708            ssh.subscribe_to_entity(SSH_PROJECT_ID, &cx.handle());
 709            ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.buffer_store);
 710            ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.worktree_store);
 711            client.add_model_message_handler(Self::handle_update_worktree);
 712            client.add_model_message_handler(Self::handle_create_buffer_for_peer);
 713            client.add_model_message_handler(BufferStore::handle_update_buffer_file);
 714            client.add_model_message_handler(BufferStore::handle_update_diff_base);
 715
 716            this.ssh_session = Some(ssh);
 717        });
 718        this
 719    }
 720
 721    pub async fn remote(
 722        remote_id: u64,
 723        client: Arc<Client>,
 724        user_store: Model<UserStore>,
 725        languages: Arc<LanguageRegistry>,
 726        fs: Arc<dyn Fs>,
 727        cx: AsyncAppContext,
 728    ) -> Result<Model<Self>> {
 729        let project =
 730            Self::in_room(remote_id, client, user_store, languages, fs, cx.clone()).await?;
 731        cx.update(|cx| {
 732            connection_manager::Manager::global(cx).update(cx, |manager, cx| {
 733                manager.maintain_project_connection(&project, cx)
 734            })
 735        })?;
 736        Ok(project)
 737    }
 738
 739    pub async fn in_room(
 740        remote_id: u64,
 741        client: Arc<Client>,
 742        user_store: Model<UserStore>,
 743        languages: Arc<LanguageRegistry>,
 744        fs: Arc<dyn Fs>,
 745        cx: AsyncAppContext,
 746    ) -> Result<Model<Self>> {
 747        client.authenticate_and_connect(true, &cx).await?;
 748
 749        let subscriptions = (
 750            client.subscribe_to_entity::<Self>(remote_id)?,
 751            client.subscribe_to_entity::<BufferStore>(remote_id)?,
 752            client.subscribe_to_entity::<WorktreeStore>(remote_id)?,
 753            client.subscribe_to_entity::<LspStore>(remote_id)?,
 754        );
 755        let response = client
 756            .request_envelope(proto::JoinProject {
 757                project_id: remote_id,
 758            })
 759            .await?;
 760        Self::from_join_project_response(
 761            response,
 762            subscriptions,
 763            client,
 764            user_store,
 765            languages,
 766            fs,
 767            cx,
 768        )
 769        .await
 770    }
 771
 772    async fn from_join_project_response(
 773        response: TypedEnvelope<proto::JoinProjectResponse>,
 774        subscription: (
 775            PendingEntitySubscription<Project>,
 776            PendingEntitySubscription<BufferStore>,
 777            PendingEntitySubscription<WorktreeStore>,
 778            PendingEntitySubscription<LspStore>,
 779        ),
 780        client: Arc<Client>,
 781        user_store: Model<UserStore>,
 782        languages: Arc<LanguageRegistry>,
 783        fs: Arc<dyn Fs>,
 784        mut cx: AsyncAppContext,
 785    ) -> Result<Model<Self>> {
 786        let remote_id = response.payload.project_id;
 787        let role = response.payload.role();
 788
 789        let worktree_store = cx.new_model(|_| {
 790            let mut store = WorktreeStore::new(true, fs.clone());
 791            store.set_upstream_client(client.clone().into());
 792            if let Some(dev_server_project_id) = response.payload.dev_server_project_id {
 793                store.set_dev_server_project_id(DevServerProjectId(dev_server_project_id));
 794            }
 795            store
 796        })?;
 797        let buffer_store =
 798            cx.new_model(|cx| BufferStore::new(worktree_store.clone(), Some(remote_id), cx))?;
 799
 800        let lsp_store = cx.new_model(|cx| {
 801            let mut lsp_store = LspStore::new(
 802                buffer_store.clone(),
 803                worktree_store.clone(),
 804                None,
 805                languages.clone(),
 806                client.http_client(),
 807                fs.clone(),
 808                None,
 809                Some(client.clone().into()),
 810                Some(remote_id),
 811                cx,
 812            );
 813            lsp_store.set_language_server_statuses_from_proto(response.payload.language_servers);
 814            lsp_store
 815        })?;
 816
 817        let this = cx.new_model(|cx| {
 818            let replica_id = response.payload.replica_id as ReplicaId;
 819            let tasks = Inventory::new(cx);
 820            let global_snippets_dir = paths::config_dir().join("snippets");
 821            let snippets =
 822                SnippetProvider::new(fs.clone(), BTreeSet::from_iter([global_snippets_dir]), cx);
 823
 824            let mut worktrees = Vec::new();
 825            for worktree in response.payload.worktrees {
 826                let worktree =
 827                    Worktree::remote(remote_id, replica_id, worktree, client.clone().into(), cx);
 828                worktrees.push(worktree);
 829            }
 830
 831            let (tx, rx) = mpsc::unbounded();
 832            cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
 833                .detach();
 834
 835            cx.subscribe(&buffer_store, Self::on_buffer_store_event)
 836                .detach();
 837            cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
 838
 839            let mut this = Self {
 840                buffer_ordered_messages_tx: tx,
 841                buffer_store: buffer_store.clone(),
 842                worktree_store: worktree_store.clone(),
 843                lsp_store: lsp_store.clone(),
 844                current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(),
 845                active_entry: None,
 846                collaborators: Default::default(),
 847                join_project_response_message_id: response.message_id,
 848                languages,
 849                user_store: user_store.clone(),
 850                snippets,
 851                fs,
 852                ssh_session: None,
 853                client_subscriptions: Default::default(),
 854                _subscriptions: vec![cx.on_release(Self::release)],
 855                client: client.clone(),
 856                client_state: ProjectClientState::Remote {
 857                    sharing_has_stopped: false,
 858                    capability: Capability::ReadWrite,
 859                    remote_id,
 860                    replica_id,
 861                    in_room: response.payload.dev_server_project_id.is_none(),
 862                },
 863                buffers_needing_diff: Default::default(),
 864                git_diff_debouncer: DebouncedDelay::new(),
 865                terminals: Terminals {
 866                    local_handles: Vec::new(),
 867                },
 868                node: None,
 869                default_prettier: DefaultPrettier::default(),
 870                prettiers_per_worktree: HashMap::default(),
 871                prettier_instances: HashMap::default(),
 872                tasks,
 873                hosted_project_id: None,
 874                dev_server_project_id: response
 875                    .payload
 876                    .dev_server_project_id
 877                    .map(|dev_server_project_id| DevServerProjectId(dev_server_project_id)),
 878                search_history: Self::new_search_history(),
 879                environment: ProjectEnvironment::new(None, cx),
 880                remotely_created_buffers: Arc::new(Mutex::new(RemotelyCreatedBuffers::default())),
 881                last_formatting_failure: None,
 882                buffers_being_formatted: Default::default(),
 883            };
 884            this.set_role(role, cx);
 885            for worktree in worktrees {
 886                let _ = this.add_worktree(&worktree, cx);
 887            }
 888            this
 889        })?;
 890
 891        let subscriptions = [
 892            subscription.0.set_model(&this, &mut cx),
 893            subscription.1.set_model(&buffer_store, &mut cx),
 894            subscription.2.set_model(&worktree_store, &mut cx),
 895            subscription.3.set_model(&lsp_store, &mut cx),
 896        ];
 897
 898        let user_ids = response
 899            .payload
 900            .collaborators
 901            .iter()
 902            .map(|peer| peer.user_id)
 903            .collect();
 904        user_store
 905            .update(&mut cx, |user_store, cx| user_store.get_users(user_ids, cx))?
 906            .await?;
 907
 908        this.update(&mut cx, |this, cx| {
 909            this.set_collaborators_from_proto(response.payload.collaborators, cx)?;
 910            this.client_subscriptions.extend(subscriptions);
 911            anyhow::Ok(())
 912        })??;
 913
 914        Ok(this)
 915    }
 916
 917    pub async fn hosted(
 918        remote_id: ProjectId,
 919        user_store: Model<UserStore>,
 920        client: Arc<Client>,
 921        languages: Arc<LanguageRegistry>,
 922        fs: Arc<dyn Fs>,
 923        cx: AsyncAppContext,
 924    ) -> Result<Model<Self>> {
 925        client.authenticate_and_connect(true, &cx).await?;
 926
 927        let subscriptions = (
 928            client.subscribe_to_entity::<Self>(remote_id.0)?,
 929            client.subscribe_to_entity::<BufferStore>(remote_id.0)?,
 930            client.subscribe_to_entity::<WorktreeStore>(remote_id.0)?,
 931            client.subscribe_to_entity::<LspStore>(remote_id.0)?,
 932        );
 933        let response = client
 934            .request_envelope(proto::JoinHostedProject {
 935                project_id: remote_id.0,
 936            })
 937            .await?;
 938        Self::from_join_project_response(
 939            response,
 940            subscriptions,
 941            client,
 942            user_store,
 943            languages,
 944            fs,
 945            cx,
 946        )
 947        .await
 948    }
 949
 950    fn new_search_history() -> SearchHistory {
 951        SearchHistory::new(
 952            Some(MAX_PROJECT_SEARCH_HISTORY_SIZE),
 953            search_history::QueryInsertionBehavior::AlwaysInsert,
 954        )
 955    }
 956
 957    fn release(&mut self, cx: &mut AppContext) {
 958        match &self.client_state {
 959            ProjectClientState::Local => {}
 960            ProjectClientState::Shared { .. } => {
 961                let _ = self.unshare_internal(cx);
 962            }
 963            ProjectClientState::Remote { remote_id, .. } => {
 964                let _ = self.client.send(proto::LeaveProject {
 965                    project_id: *remote_id,
 966                });
 967                self.disconnected_from_host_internal(cx);
 968            }
 969        }
 970    }
 971
 972    #[cfg(any(test, feature = "test-support"))]
 973    pub async fn example(
 974        root_paths: impl IntoIterator<Item = &Path>,
 975        cx: &mut AsyncAppContext,
 976    ) -> Model<Project> {
 977        use clock::FakeSystemClock;
 978
 979        let fs = Arc::new(RealFs::default());
 980        let languages = LanguageRegistry::test(cx.background_executor().clone());
 981        let clock = Arc::new(FakeSystemClock::default());
 982        let http_client = http_client::FakeHttpClient::with_404_response();
 983        let client = cx
 984            .update(|cx| client::Client::new(clock, http_client.clone(), cx))
 985            .unwrap();
 986        let user_store = cx
 987            .new_model(|cx| UserStore::new(client.clone(), cx))
 988            .unwrap();
 989        let project = cx
 990            .update(|cx| {
 991                Project::local(
 992                    client,
 993                    node_runtime::FakeNodeRuntime::new(),
 994                    user_store,
 995                    Arc::new(languages),
 996                    fs,
 997                    None,
 998                    cx,
 999                )
1000            })
1001            .unwrap();
1002        for path in root_paths {
1003            let (tree, _) = project
1004                .update(cx, |project, cx| {
1005                    project.find_or_create_worktree(path, true, cx)
1006                })
1007                .unwrap()
1008                .await
1009                .unwrap();
1010            tree.update(cx, |tree, _| tree.as_local().unwrap().scan_complete())
1011                .unwrap()
1012                .await;
1013        }
1014        project
1015    }
1016
1017    #[cfg(any(test, feature = "test-support"))]
1018    pub async fn test(
1019        fs: Arc<dyn Fs>,
1020        root_paths: impl IntoIterator<Item = &Path>,
1021        cx: &mut gpui::TestAppContext,
1022    ) -> Model<Project> {
1023        use clock::FakeSystemClock;
1024
1025        let languages = LanguageRegistry::test(cx.executor());
1026        let clock = Arc::new(FakeSystemClock::default());
1027        let http_client = http_client::FakeHttpClient::with_404_response();
1028        let client = cx.update(|cx| client::Client::new(clock, http_client.clone(), cx));
1029        let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
1030        let project = cx.update(|cx| {
1031            Project::local(
1032                client,
1033                node_runtime::FakeNodeRuntime::new(),
1034                user_store,
1035                Arc::new(languages),
1036                fs,
1037                None,
1038                cx,
1039            )
1040        });
1041        for path in root_paths {
1042            let (tree, _) = project
1043                .update(cx, |project, cx| {
1044                    project.find_or_create_worktree(path, true, cx)
1045                })
1046                .await
1047                .unwrap();
1048
1049            project.update(cx, |project, cx| {
1050                // In tests we always populate the environment to be empty so we don't run the shell
1051                let tree_id = tree.read(cx).id();
1052                let environment = ProjectEnvironment::test(&[(tree_id, HashMap::default())], cx);
1053                project.environment = environment.clone();
1054                project
1055                    .lsp_store
1056                    .update(cx, |lsp_store, _| lsp_store.set_environment(environment));
1057            });
1058
1059            tree.update(cx, |tree, _| tree.as_local().unwrap().scan_complete())
1060                .await;
1061        }
1062        project
1063    }
1064
1065    pub fn lsp_store(&self) -> Model<LspStore> {
1066        self.lsp_store.clone()
1067    }
1068
1069    fn on_settings_changed(&mut self, cx: &mut ModelContext<Self>) {
1070        let mut language_servers_to_start = Vec::new();
1071        let mut language_formatters_to_check = Vec::new();
1072        for buffer in self.buffer_store.read(cx).buffers() {
1073            let buffer = buffer.read(cx);
1074            let buffer_file = File::from_dyn(buffer.file());
1075            let buffer_language = buffer.language();
1076            let settings = language_settings(buffer_language, buffer.file(), cx);
1077            if let Some(language) = buffer_language {
1078                if settings.enable_language_server {
1079                    if let Some(file) = buffer_file {
1080                        language_servers_to_start
1081                            .push((file.worktree.clone(), Arc::clone(language)));
1082                    }
1083                }
1084                language_formatters_to_check
1085                    .push((buffer_file.map(|f| f.worktree_id(cx)), settings.clone()));
1086            }
1087        }
1088
1089        let mut language_servers_to_stop = Vec::new();
1090        let mut language_servers_to_restart = Vec::new();
1091        let languages = self.languages.to_vec();
1092
1093        let new_lsp_settings = ProjectSettings::get_global(cx).lsp.clone();
1094        let current_lsp_settings = &self.current_lsp_settings;
1095        for (worktree_id, started_lsp_name) in self.lsp_store.read(cx).started_language_servers() {
1096            let language = languages.iter().find_map(|l| {
1097                let adapter = self
1098                    .languages
1099                    .lsp_adapters(l)
1100                    .iter()
1101                    .find(|adapter| adapter.name == started_lsp_name)?
1102                    .clone();
1103                Some((l, adapter))
1104            });
1105            if let Some((language, adapter)) = language {
1106                let worktree = self.worktree_for_id(worktree_id, cx);
1107                let file = worktree.as_ref().and_then(|tree| {
1108                    tree.update(cx, |tree, cx| tree.root_file(cx).map(|f| f as _))
1109                });
1110                if !language_settings(Some(language), file.as_ref(), cx).enable_language_server {
1111                    language_servers_to_stop.push((worktree_id, started_lsp_name.clone()));
1112                } else if let Some(worktree) = worktree {
1113                    let server_name = &adapter.name.0;
1114                    match (
1115                        current_lsp_settings.get(server_name),
1116                        new_lsp_settings.get(server_name),
1117                    ) {
1118                        (None, None) => {}
1119                        (Some(_), None) | (None, Some(_)) => {
1120                            language_servers_to_restart.push((worktree, Arc::clone(language)));
1121                        }
1122                        (Some(current_lsp_settings), Some(new_lsp_settings)) => {
1123                            if current_lsp_settings != new_lsp_settings {
1124                                language_servers_to_restart.push((worktree, Arc::clone(language)));
1125                            }
1126                        }
1127                    }
1128                }
1129            }
1130        }
1131        self.current_lsp_settings = new_lsp_settings;
1132
1133        // Stop all newly-disabled language servers.
1134        self.lsp_store.update(cx, |lsp_store, cx| {
1135            for (worktree_id, adapter_name) in language_servers_to_stop {
1136                lsp_store
1137                    .stop_language_server(worktree_id, adapter_name, cx)
1138                    .detach();
1139            }
1140        });
1141
1142        let mut prettier_plugins_by_worktree = HashMap::default();
1143        for (worktree, language_settings) in language_formatters_to_check {
1144            if let Some(plugins) =
1145                prettier_support::prettier_plugins_for_language(&language_settings)
1146            {
1147                prettier_plugins_by_worktree
1148                    .entry(worktree)
1149                    .or_insert_with(|| HashSet::default())
1150                    .extend(plugins.iter().cloned());
1151            }
1152        }
1153        for (worktree, prettier_plugins) in prettier_plugins_by_worktree {
1154            self.install_default_prettier(
1155                worktree,
1156                prettier_plugins.into_iter().map(Arc::from),
1157                cx,
1158            );
1159        }
1160
1161        // Start all the newly-enabled language servers.
1162        self.lsp_store.update(cx, |lsp_store, cx| {
1163            for (worktree, language) in language_servers_to_start {
1164                lsp_store.start_language_servers(&worktree, language, cx);
1165            }
1166
1167            // Restart all language servers with changed initialization options.
1168            for (worktree, language) in language_servers_to_restart {
1169                lsp_store.restart_language_servers(worktree, language, cx);
1170            }
1171        });
1172
1173        cx.notify();
1174    }
1175
1176    pub fn buffer_for_id(&self, remote_id: BufferId, cx: &AppContext) -> Option<Model<Buffer>> {
1177        self.buffer_store.read(cx).get(remote_id)
1178    }
1179
1180    pub fn languages(&self) -> &Arc<LanguageRegistry> {
1181        &self.languages
1182    }
1183
1184    pub fn client(&self) -> Arc<Client> {
1185        self.client.clone()
1186    }
1187
1188    pub fn user_store(&self) -> Model<UserStore> {
1189        self.user_store.clone()
1190    }
1191
1192    pub fn node_runtime(&self) -> Option<&Arc<dyn NodeRuntime>> {
1193        self.node.as_ref()
1194    }
1195
1196    pub fn opened_buffers(&self, cx: &AppContext) -> Vec<Model<Buffer>> {
1197        self.buffer_store.read(cx).buffers().collect()
1198    }
1199
1200    pub fn cli_environment(&self, cx: &AppContext) -> Option<HashMap<String, String>> {
1201        self.environment.read(cx).get_cli_environment()
1202    }
1203
1204    #[cfg(any(test, feature = "test-support"))]
1205    pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &AppContext) -> bool {
1206        self.buffer_store
1207            .read(cx)
1208            .get_by_path(&path.into(), cx)
1209            .is_some()
1210    }
1211
1212    pub fn fs(&self) -> &Arc<dyn Fs> {
1213        &self.fs
1214    }
1215
1216    pub fn remote_id(&self) -> Option<u64> {
1217        match self.client_state {
1218            ProjectClientState::Local => None,
1219            ProjectClientState::Shared { remote_id, .. }
1220            | ProjectClientState::Remote { remote_id, .. } => Some(remote_id),
1221        }
1222    }
1223
1224    pub fn hosted_project_id(&self) -> Option<ProjectId> {
1225        self.hosted_project_id
1226    }
1227
1228    pub fn dev_server_project_id(&self) -> Option<DevServerProjectId> {
1229        self.dev_server_project_id
1230    }
1231
1232    pub fn supports_remote_terminal(&self, cx: &AppContext) -> bool {
1233        let Some(id) = self.dev_server_project_id else {
1234            return false;
1235        };
1236        let Some(server) = dev_server_projects::Store::global(cx)
1237            .read(cx)
1238            .dev_server_for_project(id)
1239        else {
1240            return false;
1241        };
1242        server.ssh_connection_string.is_some()
1243    }
1244
1245    pub fn ssh_connection_string(&self, cx: &ModelContext<Self>) -> Option<SharedString> {
1246        if self.is_local_or_ssh() {
1247            return None;
1248        }
1249
1250        let dev_server_id = self.dev_server_project_id()?;
1251        dev_server_projects::Store::global(cx)
1252            .read(cx)
1253            .dev_server_for_project(dev_server_id)?
1254            .ssh_connection_string
1255            .clone()
1256    }
1257
1258    pub fn replica_id(&self) -> ReplicaId {
1259        match self.client_state {
1260            ProjectClientState::Remote { replica_id, .. } => replica_id,
1261            _ => 0,
1262        }
1263    }
1264
1265    fn metadata_changed(&mut self, cx: &mut ModelContext<Self>) {
1266        cx.notify();
1267
1268        let ProjectClientState::Shared { remote_id } = self.client_state else {
1269            return;
1270        };
1271        let project_id = remote_id;
1272
1273        let update_project = self.client.request(proto::UpdateProject {
1274            project_id,
1275            worktrees: self.worktree_metadata_protos(cx),
1276        });
1277        cx.spawn(|this, mut cx| async move {
1278            update_project.await?;
1279            this.update(&mut cx, |this, cx| {
1280                let client = this.client.clone();
1281                let worktrees = this.worktree_store.read(cx).worktrees().collect::<Vec<_>>();
1282
1283                for worktree in worktrees {
1284                    worktree.update(cx, |worktree, cx| {
1285                        let client = client.clone();
1286                        worktree.observe_updates(project_id, cx, {
1287                            move |update| client.request(update).map(|result| result.is_ok())
1288                        });
1289
1290                        this.lsp_store.update(cx, |lsp_store, _| {
1291                            lsp_store.send_diagnostic_summaries(worktree)
1292                        })
1293                    })?;
1294                }
1295
1296                anyhow::Ok(())
1297            })
1298        })
1299        .detach_and_log_err(cx);
1300    }
1301
1302    pub fn task_inventory(&self) -> &Model<Inventory> {
1303        &self.tasks
1304    }
1305
1306    pub fn snippets(&self) -> &Model<SnippetProvider> {
1307        &self.snippets
1308    }
1309
1310    pub fn search_history(&self) -> &SearchHistory {
1311        &self.search_history
1312    }
1313
1314    pub fn search_history_mut(&mut self) -> &mut SearchHistory {
1315        &mut self.search_history
1316    }
1317
1318    pub fn collaborators(&self) -> &HashMap<proto::PeerId, Collaborator> {
1319        &self.collaborators
1320    }
1321
1322    pub fn host(&self) -> Option<&Collaborator> {
1323        self.collaborators.values().find(|c| c.replica_id == 0)
1324    }
1325
1326    pub fn set_worktrees_reordered(&mut self, worktrees_reordered: bool, cx: &mut AppContext) {
1327        self.worktree_store.update(cx, |store, _| {
1328            store.set_worktrees_reordered(worktrees_reordered);
1329        });
1330    }
1331
1332    /// Collect all worktrees, including ones that don't appear in the project panel
1333    pub fn worktrees<'a>(
1334        &self,
1335        cx: &'a AppContext,
1336    ) -> impl 'a + DoubleEndedIterator<Item = Model<Worktree>> {
1337        self.worktree_store.read(cx).worktrees()
1338    }
1339
1340    /// Collect all user-visible worktrees, the ones that appear in the project panel.
1341    pub fn visible_worktrees<'a>(
1342        &'a self,
1343        cx: &'a AppContext,
1344    ) -> impl 'a + DoubleEndedIterator<Item = Model<Worktree>> {
1345        self.worktree_store.read(cx).visible_worktrees(cx)
1346    }
1347
1348    pub fn worktree_root_names<'a>(&'a self, cx: &'a AppContext) -> impl Iterator<Item = &'a str> {
1349        self.visible_worktrees(cx)
1350            .map(|tree| tree.read(cx).root_name())
1351    }
1352
1353    pub fn worktree_for_id(&self, id: WorktreeId, cx: &AppContext) -> Option<Model<Worktree>> {
1354        self.worktree_store.read(cx).worktree_for_id(id, cx)
1355    }
1356
1357    pub fn worktree_for_entry(
1358        &self,
1359        entry_id: ProjectEntryId,
1360        cx: &AppContext,
1361    ) -> Option<Model<Worktree>> {
1362        self.worktree_store
1363            .read(cx)
1364            .worktree_for_entry(entry_id, cx)
1365    }
1366
1367    pub fn worktree_id_for_entry(
1368        &self,
1369        entry_id: ProjectEntryId,
1370        cx: &AppContext,
1371    ) -> Option<WorktreeId> {
1372        self.worktree_for_entry(entry_id, cx)
1373            .map(|worktree| worktree.read(cx).id())
1374    }
1375
1376    /// Checks if the entry is the root of a worktree.
1377    pub fn entry_is_worktree_root(&self, entry_id: ProjectEntryId, cx: &AppContext) -> bool {
1378        self.worktree_for_entry(entry_id, cx)
1379            .map(|worktree| {
1380                worktree
1381                    .read(cx)
1382                    .root_entry()
1383                    .is_some_and(|e| e.id == entry_id)
1384            })
1385            .unwrap_or(false)
1386    }
1387
1388    pub fn visibility_for_paths(&self, paths: &[PathBuf], cx: &AppContext) -> Option<bool> {
1389        paths
1390            .iter()
1391            .map(|path| self.visibility_for_path(path, cx))
1392            .max()
1393            .flatten()
1394    }
1395
1396    pub fn visibility_for_path(&self, path: &Path, cx: &AppContext) -> Option<bool> {
1397        self.worktrees(cx)
1398            .filter_map(|worktree| {
1399                let worktree = worktree.read(cx);
1400                worktree
1401                    .as_local()?
1402                    .contains_abs_path(path)
1403                    .then(|| worktree.is_visible())
1404            })
1405            .max()
1406    }
1407
1408    pub fn create_entry(
1409        &mut self,
1410        project_path: impl Into<ProjectPath>,
1411        is_directory: bool,
1412        cx: &mut ModelContext<Self>,
1413    ) -> Task<Result<CreatedEntry>> {
1414        let project_path = project_path.into();
1415        let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) else {
1416            return Task::ready(Err(anyhow!(format!(
1417                "No worktree for path {project_path:?}"
1418            ))));
1419        };
1420        worktree.update(cx, |worktree, cx| {
1421            worktree.create_entry(project_path.path, is_directory, cx)
1422        })
1423    }
1424
1425    pub fn copy_entry(
1426        &mut self,
1427        entry_id: ProjectEntryId,
1428        relative_worktree_source_path: Option<PathBuf>,
1429        new_path: impl Into<Arc<Path>>,
1430        cx: &mut ModelContext<Self>,
1431    ) -> Task<Result<Option<Entry>>> {
1432        let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
1433            return Task::ready(Ok(None));
1434        };
1435        worktree.update(cx, |worktree, cx| {
1436            worktree.copy_entry(entry_id, relative_worktree_source_path, new_path, cx)
1437        })
1438    }
1439
1440    pub fn rename_entry(
1441        &mut self,
1442        entry_id: ProjectEntryId,
1443        new_path: impl Into<Arc<Path>>,
1444        cx: &mut ModelContext<Self>,
1445    ) -> Task<Result<CreatedEntry>> {
1446        let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
1447            return Task::ready(Err(anyhow!(format!("No worktree for entry {entry_id:?}"))));
1448        };
1449        worktree.update(cx, |worktree, cx| {
1450            worktree.rename_entry(entry_id, new_path, cx)
1451        })
1452    }
1453
1454    pub fn delete_entry(
1455        &mut self,
1456        entry_id: ProjectEntryId,
1457        trash: bool,
1458        cx: &mut ModelContext<Self>,
1459    ) -> Option<Task<Result<()>>> {
1460        let worktree = self.worktree_for_entry(entry_id, cx)?;
1461        worktree.update(cx, |worktree, cx| {
1462            worktree.delete_entry(entry_id, trash, cx)
1463        })
1464    }
1465
1466    pub fn expand_entry(
1467        &mut self,
1468        worktree_id: WorktreeId,
1469        entry_id: ProjectEntryId,
1470        cx: &mut ModelContext<Self>,
1471    ) -> Option<Task<Result<()>>> {
1472        let worktree = self.worktree_for_id(worktree_id, cx)?;
1473        worktree.update(cx, |worktree, cx| worktree.expand_entry(entry_id, cx))
1474    }
1475
1476    pub fn shared(&mut self, project_id: u64, cx: &mut ModelContext<Self>) -> Result<()> {
1477        if !matches!(self.client_state, ProjectClientState::Local) {
1478            if let ProjectClientState::Remote { in_room, .. } = &mut self.client_state {
1479                if *in_room || self.dev_server_project_id.is_none() {
1480                    return Err(anyhow!("project was already shared"));
1481                } else {
1482                    *in_room = true;
1483                    return Ok(());
1484                }
1485            } else {
1486                return Err(anyhow!("project was already shared"));
1487            }
1488        }
1489        self.client_subscriptions.extend([
1490            self.client
1491                .subscribe_to_entity(project_id)?
1492                .set_model(&cx.handle(), &mut cx.to_async()),
1493            self.client
1494                .subscribe_to_entity(project_id)?
1495                .set_model(&self.worktree_store, &mut cx.to_async()),
1496            self.client
1497                .subscribe_to_entity(project_id)?
1498                .set_model(&self.buffer_store, &mut cx.to_async()),
1499            self.client
1500                .subscribe_to_entity(project_id)?
1501                .set_model(&self.lsp_store, &mut cx.to_async()),
1502        ]);
1503
1504        self.buffer_store.update(cx, |buffer_store, cx| {
1505            buffer_store.shared(project_id, self.client.clone().into(), cx)
1506        });
1507        self.worktree_store.update(cx, |worktree_store, cx| {
1508            worktree_store.set_shared(true, cx);
1509        });
1510        self.lsp_store.update(cx, |lsp_store, cx| {
1511            lsp_store.shared(project_id, self.client.clone().into(), cx)
1512        });
1513
1514        let store = cx.global::<SettingsStore>();
1515        for worktree in self.worktrees(cx) {
1516            let worktree_id = worktree.read(cx).id().to_proto();
1517            for (path, content) in store.local_settings(worktree.entity_id().as_u64() as usize) {
1518                self.client
1519                    .send(proto::UpdateWorktreeSettings {
1520                        project_id,
1521                        worktree_id,
1522                        path: path.to_string_lossy().into(),
1523                        content: Some(content),
1524                    })
1525                    .log_err();
1526            }
1527        }
1528
1529        self.client_state = ProjectClientState::Shared {
1530            remote_id: project_id,
1531        };
1532
1533        self.metadata_changed(cx);
1534        cx.emit(Event::RemoteIdChanged(Some(project_id)));
1535        cx.notify();
1536        Ok(())
1537    }
1538
1539    pub fn reshared(
1540        &mut self,
1541        message: proto::ResharedProject,
1542        cx: &mut ModelContext<Self>,
1543    ) -> Result<()> {
1544        self.buffer_store
1545            .update(cx, |buffer_store, _| buffer_store.forget_shared_buffers());
1546        self.set_collaborators_from_proto(message.collaborators, cx)?;
1547        self.metadata_changed(cx);
1548        cx.emit(Event::Reshared);
1549        Ok(())
1550    }
1551
1552    pub fn rejoined(
1553        &mut self,
1554        message: proto::RejoinedProject,
1555        message_id: u32,
1556        cx: &mut ModelContext<Self>,
1557    ) -> Result<()> {
1558        cx.update_global::<SettingsStore, _>(|store, cx| {
1559            self.worktree_store.update(cx, |worktree_store, cx| {
1560                for worktree in worktree_store.worktrees() {
1561                    store
1562                        .clear_local_settings(worktree.entity_id().as_u64() as usize, cx)
1563                        .log_err();
1564                }
1565            });
1566        });
1567
1568        self.join_project_response_message_id = message_id;
1569        self.set_worktrees_from_proto(message.worktrees, cx)?;
1570        self.set_collaborators_from_proto(message.collaborators, cx)?;
1571        self.lsp_store.update(cx, |lsp_store, _| {
1572            lsp_store.set_language_server_statuses_from_proto(message.language_servers)
1573        });
1574        self.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
1575            .unwrap();
1576        cx.emit(Event::Rejoined);
1577        cx.notify();
1578        Ok(())
1579    }
1580
1581    pub fn unshare(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
1582        self.unshare_internal(cx)?;
1583        self.metadata_changed(cx);
1584        cx.notify();
1585        Ok(())
1586    }
1587
1588    fn unshare_internal(&mut self, cx: &mut AppContext) -> Result<()> {
1589        if self.is_via_collab() {
1590            if self.dev_server_project_id().is_some() {
1591                if let ProjectClientState::Remote { in_room, .. } = &mut self.client_state {
1592                    *in_room = false
1593                }
1594                return Ok(());
1595            } else {
1596                return Err(anyhow!("attempted to unshare a remote project"));
1597            }
1598        }
1599
1600        if let ProjectClientState::Shared { remote_id, .. } = self.client_state {
1601            self.client_state = ProjectClientState::Local;
1602            self.collaborators.clear();
1603            self.client_subscriptions.clear();
1604            self.worktree_store.update(cx, |store, cx| {
1605                store.set_shared(false, cx);
1606            });
1607            self.buffer_store.update(cx, |buffer_store, cx| {
1608                buffer_store.forget_shared_buffers();
1609                buffer_store.unshared(cx)
1610            });
1611            self.client
1612                .send(proto::UnshareProject {
1613                    project_id: remote_id,
1614                })
1615                .ok();
1616            Ok(())
1617        } else {
1618            Err(anyhow!("attempted to unshare an unshared project"))
1619        }
1620    }
1621
1622    pub fn disconnected_from_host(&mut self, cx: &mut ModelContext<Self>) {
1623        if self.is_disconnected() {
1624            return;
1625        }
1626        self.disconnected_from_host_internal(cx);
1627        cx.emit(Event::DisconnectedFromHost);
1628        cx.notify();
1629    }
1630
1631    pub fn set_role(&mut self, role: proto::ChannelRole, cx: &mut ModelContext<Self>) {
1632        let new_capability =
1633            if role == proto::ChannelRole::Member || role == proto::ChannelRole::Admin {
1634                Capability::ReadWrite
1635            } else {
1636                Capability::ReadOnly
1637            };
1638        if let ProjectClientState::Remote { capability, .. } = &mut self.client_state {
1639            if *capability == new_capability {
1640                return;
1641            }
1642
1643            *capability = new_capability;
1644            for buffer in self.opened_buffers(cx) {
1645                buffer.update(cx, |buffer, cx| buffer.set_capability(new_capability, cx));
1646            }
1647        }
1648    }
1649
1650    fn disconnected_from_host_internal(&mut self, cx: &mut AppContext) {
1651        if let ProjectClientState::Remote {
1652            sharing_has_stopped,
1653            ..
1654        } = &mut self.client_state
1655        {
1656            *sharing_has_stopped = true;
1657            self.collaborators.clear();
1658            self.worktree_store.update(cx, |store, cx| {
1659                store.disconnected_from_host(cx);
1660            });
1661            self.buffer_store.update(cx, |buffer_store, cx| {
1662                buffer_store.disconnected_from_host(cx)
1663            });
1664            self.lsp_store
1665                .update(cx, |lsp_store, _cx| lsp_store.disconnected_from_host());
1666        }
1667    }
1668
1669    pub fn close(&mut self, cx: &mut ModelContext<Self>) {
1670        cx.emit(Event::Closed);
1671    }
1672
1673    pub fn is_disconnected(&self) -> bool {
1674        match &self.client_state {
1675            ProjectClientState::Remote {
1676                sharing_has_stopped,
1677                ..
1678            } => *sharing_has_stopped,
1679            _ => false,
1680        }
1681    }
1682
1683    pub fn capability(&self) -> Capability {
1684        match &self.client_state {
1685            ProjectClientState::Remote { capability, .. } => *capability,
1686            ProjectClientState::Shared { .. } | ProjectClientState::Local => Capability::ReadWrite,
1687        }
1688    }
1689
1690    pub fn is_read_only(&self) -> bool {
1691        self.is_disconnected() || self.capability() == Capability::ReadOnly
1692    }
1693
1694    pub fn is_local(&self) -> bool {
1695        match &self.client_state {
1696            ProjectClientState::Local | ProjectClientState::Shared { .. } => {
1697                self.ssh_session.is_none()
1698            }
1699            ProjectClientState::Remote { .. } => false,
1700        }
1701    }
1702
1703    pub fn is_local_or_ssh(&self) -> bool {
1704        match &self.client_state {
1705            ProjectClientState::Local | ProjectClientState::Shared { .. } => true,
1706            ProjectClientState::Remote { .. } => false,
1707        }
1708    }
1709
1710    pub fn is_via_collab(&self) -> bool {
1711        match &self.client_state {
1712            ProjectClientState::Local | ProjectClientState::Shared { .. } => false,
1713            ProjectClientState::Remote { .. } => true,
1714        }
1715    }
1716
1717    pub fn create_buffer(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<Model<Buffer>>> {
1718        self.buffer_store.update(cx, |buffer_store, cx| {
1719            buffer_store.create_buffer(
1720                if self.is_via_collab() {
1721                    Some((self.client.clone().into(), self.remote_id().unwrap()))
1722                } else {
1723                    None
1724                },
1725                cx,
1726            )
1727        })
1728    }
1729
1730    pub fn create_local_buffer(
1731        &mut self,
1732        text: &str,
1733        language: Option<Arc<Language>>,
1734        cx: &mut ModelContext<Self>,
1735    ) -> Model<Buffer> {
1736        if self.is_via_collab() {
1737            panic!("called create_local_buffer on a remote project")
1738        }
1739        self.buffer_store.update(cx, |buffer_store, cx| {
1740            buffer_store.create_local_buffer(text, language, cx)
1741        })
1742    }
1743
1744    pub fn open_path(
1745        &mut self,
1746        path: ProjectPath,
1747        cx: &mut ModelContext<Self>,
1748    ) -> Task<Result<(Option<ProjectEntryId>, AnyModel)>> {
1749        let task = self.open_buffer(path.clone(), cx);
1750        cx.spawn(move |_, cx| async move {
1751            let buffer = task.await?;
1752            let project_entry_id = buffer.read_with(&cx, |buffer, cx| {
1753                File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
1754            })?;
1755
1756            let buffer: &AnyModel = &buffer;
1757            Ok((project_entry_id, buffer.clone()))
1758        })
1759    }
1760
1761    pub fn open_local_buffer(
1762        &mut self,
1763        abs_path: impl AsRef<Path>,
1764        cx: &mut ModelContext<Self>,
1765    ) -> Task<Result<Model<Buffer>>> {
1766        if let Some((worktree, relative_path)) = self.find_worktree(abs_path.as_ref(), cx) {
1767            self.open_buffer((worktree.read(cx).id(), relative_path), cx)
1768        } else {
1769            Task::ready(Err(anyhow!("no such path")))
1770        }
1771    }
1772
1773    pub fn open_buffer(
1774        &mut self,
1775        path: impl Into<ProjectPath>,
1776        cx: &mut ModelContext<Self>,
1777    ) -> Task<Result<Model<Buffer>>> {
1778        if self.is_via_collab() && self.is_disconnected() {
1779            return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
1780        }
1781
1782        self.buffer_store.update(cx, |buffer_store, cx| {
1783            buffer_store.open_buffer(path.into(), cx)
1784        })
1785    }
1786
1787    pub fn open_buffer_by_id(
1788        &mut self,
1789        id: BufferId,
1790        cx: &mut ModelContext<Self>,
1791    ) -> Task<Result<Model<Buffer>>> {
1792        if let Some(buffer) = self.buffer_for_id(id, cx) {
1793            Task::ready(Ok(buffer))
1794        } else if self.is_local_or_ssh() {
1795            Task::ready(Err(anyhow!("buffer {} does not exist", id)))
1796        } else if let Some(project_id) = self.remote_id() {
1797            let request = self.client.request(proto::OpenBufferById {
1798                project_id,
1799                id: id.into(),
1800            });
1801            cx.spawn(move |this, mut cx| async move {
1802                let buffer_id = BufferId::new(request.await?.buffer_id)?;
1803                this.update(&mut cx, |this, cx| {
1804                    this.wait_for_remote_buffer(buffer_id, cx)
1805                })?
1806                .await
1807            })
1808        } else {
1809            Task::ready(Err(anyhow!("cannot open buffer while disconnected")))
1810        }
1811    }
1812
1813    pub fn save_buffers(
1814        &self,
1815        buffers: HashSet<Model<Buffer>>,
1816        cx: &mut ModelContext<Self>,
1817    ) -> Task<Result<()>> {
1818        cx.spawn(move |this, mut cx| async move {
1819            let save_tasks = buffers.into_iter().filter_map(|buffer| {
1820                this.update(&mut cx, |this, cx| this.save_buffer(buffer, cx))
1821                    .ok()
1822            });
1823            try_join_all(save_tasks).await?;
1824            Ok(())
1825        })
1826    }
1827
1828    pub fn save_buffer(
1829        &self,
1830        buffer: Model<Buffer>,
1831        cx: &mut ModelContext<Self>,
1832    ) -> Task<Result<()>> {
1833        self.buffer_store
1834            .update(cx, |buffer_store, cx| buffer_store.save_buffer(buffer, cx))
1835    }
1836
1837    pub fn save_buffer_as(
1838        &mut self,
1839        buffer: Model<Buffer>,
1840        path: ProjectPath,
1841        cx: &mut ModelContext<Self>,
1842    ) -> Task<Result<()>> {
1843        self.buffer_store.update(cx, |buffer_store, cx| {
1844            buffer_store.save_buffer_as(buffer.clone(), path, cx)
1845        })
1846    }
1847
1848    pub fn get_open_buffer(
1849        &mut self,
1850        path: &ProjectPath,
1851        cx: &mut ModelContext<Self>,
1852    ) -> Option<Model<Buffer>> {
1853        self.buffer_store.read(cx).get_by_path(path, cx)
1854    }
1855
1856    fn register_buffer(
1857        &mut self,
1858        buffer: &Model<Buffer>,
1859        cx: &mut ModelContext<Self>,
1860    ) -> Result<()> {
1861        {
1862            let mut remotely_created_buffers = self.remotely_created_buffers.lock();
1863            if remotely_created_buffers.retain_count > 0 {
1864                remotely_created_buffers.buffers.push(buffer.clone())
1865            }
1866        }
1867
1868        self.request_buffer_diff_recalculation(buffer, cx);
1869
1870        cx.subscribe(buffer, |this, buffer, event, cx| {
1871            this.on_buffer_event(buffer, event, cx);
1872        })
1873        .detach();
1874
1875        Ok(())
1876    }
1877
1878    async fn send_buffer_ordered_messages(
1879        this: WeakModel<Self>,
1880        rx: UnboundedReceiver<BufferOrderedMessage>,
1881        mut cx: AsyncAppContext,
1882    ) -> Result<()> {
1883        const MAX_BATCH_SIZE: usize = 128;
1884
1885        let mut operations_by_buffer_id = HashMap::default();
1886        async fn flush_operations(
1887            this: &WeakModel<Project>,
1888            operations_by_buffer_id: &mut HashMap<BufferId, Vec<proto::Operation>>,
1889            needs_resync_with_host: &mut bool,
1890            is_local: bool,
1891            cx: &mut AsyncAppContext,
1892        ) -> Result<()> {
1893            for (buffer_id, operations) in operations_by_buffer_id.drain() {
1894                let request = this.update(cx, |this, _| {
1895                    let project_id = this.remote_id()?;
1896                    Some(this.client.request(proto::UpdateBuffer {
1897                        buffer_id: buffer_id.into(),
1898                        project_id,
1899                        operations,
1900                    }))
1901                })?;
1902                if let Some(request) = request {
1903                    if request.await.is_err() && !is_local {
1904                        *needs_resync_with_host = true;
1905                        break;
1906                    }
1907                }
1908            }
1909            Ok(())
1910        }
1911
1912        let mut needs_resync_with_host = false;
1913        let mut changes = rx.ready_chunks(MAX_BATCH_SIZE);
1914
1915        while let Some(changes) = changes.next().await {
1916            let is_local = this.update(&mut cx, |this, _| this.is_local_or_ssh())?;
1917
1918            for change in changes {
1919                match change {
1920                    BufferOrderedMessage::Operation {
1921                        buffer_id,
1922                        operation,
1923                    } => {
1924                        if needs_resync_with_host {
1925                            continue;
1926                        }
1927
1928                        operations_by_buffer_id
1929                            .entry(buffer_id)
1930                            .or_insert(Vec::new())
1931                            .push(operation);
1932                    }
1933
1934                    BufferOrderedMessage::Resync => {
1935                        operations_by_buffer_id.clear();
1936                        if this
1937                            .update(&mut cx, |this, cx| this.synchronize_remote_buffers(cx))?
1938                            .await
1939                            .is_ok()
1940                        {
1941                            needs_resync_with_host = false;
1942                        }
1943                    }
1944
1945                    BufferOrderedMessage::LanguageServerUpdate {
1946                        language_server_id,
1947                        message,
1948                    } => {
1949                        flush_operations(
1950                            &this,
1951                            &mut operations_by_buffer_id,
1952                            &mut needs_resync_with_host,
1953                            is_local,
1954                            &mut cx,
1955                        )
1956                        .await?;
1957
1958                        this.update(&mut cx, |this, _| {
1959                            if let Some(project_id) = this.remote_id() {
1960                                this.client
1961                                    .send(proto::UpdateLanguageServer {
1962                                        project_id,
1963                                        language_server_id: language_server_id.0 as u64,
1964                                        variant: Some(message),
1965                                    })
1966                                    .log_err();
1967                            }
1968                        })?;
1969                    }
1970                }
1971            }
1972
1973            flush_operations(
1974                &this,
1975                &mut operations_by_buffer_id,
1976                &mut needs_resync_with_host,
1977                is_local,
1978                &mut cx,
1979            )
1980            .await?;
1981        }
1982
1983        Ok(())
1984    }
1985
1986    fn on_buffer_store_event(
1987        &mut self,
1988        _: Model<BufferStore>,
1989        event: &BufferStoreEvent,
1990        cx: &mut ModelContext<Self>,
1991    ) {
1992        match event {
1993            BufferStoreEvent::BufferAdded(buffer) => {
1994                self.register_buffer(buffer, cx).log_err();
1995            }
1996            BufferStoreEvent::BufferChangedFilePath { .. } => {}
1997            BufferStoreEvent::BufferDropped(buffer_id) => {
1998                if let Some(ref ssh_session) = self.ssh_session {
1999                    ssh_session
2000                        .send(proto::CloseBuffer {
2001                            project_id: 0,
2002                            buffer_id: buffer_id.to_proto(),
2003                        })
2004                        .log_err();
2005                }
2006            }
2007        }
2008    }
2009
2010    fn on_lsp_store_event(
2011        &mut self,
2012        _: Model<LspStore>,
2013        event: &LspStoreEvent,
2014        cx: &mut ModelContext<Self>,
2015    ) {
2016        match event {
2017            LspStoreEvent::DiagnosticsUpdated {
2018                language_server_id,
2019                path,
2020            } => cx.emit(Event::DiagnosticsUpdated {
2021                path: path.clone(),
2022                language_server_id: *language_server_id,
2023            }),
2024            LspStoreEvent::LanguageServerAdded(language_server_id) => {
2025                cx.emit(Event::LanguageServerAdded(*language_server_id))
2026            }
2027            LspStoreEvent::LanguageServerRemoved(language_server_id) => {
2028                cx.emit(Event::LanguageServerAdded(*language_server_id))
2029            }
2030            LspStoreEvent::LanguageServerLog(server_id, log_type, string) => cx.emit(
2031                Event::LanguageServerLog(*server_id, log_type.clone(), string.clone()),
2032            ),
2033            LspStoreEvent::LanguageDetected {
2034                buffer,
2035                new_language,
2036            } => {
2037                let Some(new_language) = new_language else {
2038                    cx.emit(Event::LanguageNotFound(buffer.clone()));
2039                    return;
2040                };
2041                let buffer_file = buffer.read(cx).file().cloned();
2042                let settings =
2043                    language_settings(Some(&new_language), buffer_file.as_ref(), cx).clone();
2044                let buffer_file = File::from_dyn(buffer_file.as_ref());
2045                let worktree = buffer_file.as_ref().map(|f| f.worktree_id(cx));
2046                if let Some(prettier_plugins) =
2047                    prettier_support::prettier_plugins_for_language(&settings)
2048                {
2049                    self.install_default_prettier(
2050                        worktree,
2051                        prettier_plugins.iter().map(|s| Arc::from(s.as_str())),
2052                        cx,
2053                    );
2054                };
2055            }
2056            LspStoreEvent::RefreshInlayHints => cx.emit(Event::RefreshInlayHints),
2057            LspStoreEvent::LanguageServerPrompt(prompt) => {
2058                cx.emit(Event::LanguageServerPrompt(prompt.clone()))
2059            }
2060            LspStoreEvent::DiskBasedDiagnosticsStarted { language_server_id } => {
2061                cx.emit(Event::DiskBasedDiagnosticsStarted {
2062                    language_server_id: *language_server_id,
2063                });
2064                if self.is_local_or_ssh() {
2065                    self.enqueue_buffer_ordered_message(BufferOrderedMessage::LanguageServerUpdate {
2066                        language_server_id: *language_server_id,
2067                        message: proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(
2068                            Default::default(),
2069                        ),
2070                    })
2071                    .ok();
2072                }
2073            }
2074            LspStoreEvent::DiskBasedDiagnosticsFinished { language_server_id } => {
2075                cx.emit(Event::DiskBasedDiagnosticsFinished {
2076                    language_server_id: *language_server_id,
2077                });
2078                if self.is_local_or_ssh() {
2079                    self.enqueue_buffer_ordered_message(
2080                        BufferOrderedMessage::LanguageServerUpdate {
2081                            language_server_id: *language_server_id,
2082                            message:
2083                                proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
2084                                    Default::default(),
2085                                ),
2086                        },
2087                    )
2088                    .ok();
2089                }
2090            }
2091            LspStoreEvent::LanguageServerUpdate {
2092                language_server_id,
2093                message,
2094            } => {
2095                if self.is_local_or_ssh() {
2096                    self.enqueue_buffer_ordered_message(
2097                        BufferOrderedMessage::LanguageServerUpdate {
2098                            language_server_id: *language_server_id,
2099                            message: message.clone(),
2100                        },
2101                    )
2102                    .ok();
2103                }
2104            }
2105            LspStoreEvent::Notification(message) => cx.emit(Event::Notification(message.clone())),
2106            LspStoreEvent::SnippetEdit {
2107                buffer_id,
2108                edits,
2109                most_recent_edit,
2110            } => {
2111                if most_recent_edit.replica_id == self.replica_id() {
2112                    cx.emit(Event::SnippetEdit(*buffer_id, edits.clone()))
2113                }
2114            }
2115            LspStoreEvent::StartFormattingLocalBuffer(buffer_id) => {
2116                self.buffers_being_formatted.insert(*buffer_id);
2117            }
2118            LspStoreEvent::FinishFormattingLocalBuffer(buffer_id) => {
2119                self.buffers_being_formatted.remove(buffer_id);
2120            }
2121        }
2122    }
2123
2124    fn on_worktree_store_event(
2125        &mut self,
2126        _: Model<WorktreeStore>,
2127        event: &WorktreeStoreEvent,
2128        cx: &mut ModelContext<Self>,
2129    ) {
2130        match event {
2131            WorktreeStoreEvent::WorktreeAdded(worktree) => {
2132                self.on_worktree_added(worktree, cx);
2133                cx.emit(Event::WorktreeAdded);
2134            }
2135            WorktreeStoreEvent::WorktreeRemoved(_, id) => {
2136                self.on_worktree_removed(*id, cx);
2137                cx.emit(Event::WorktreeRemoved(*id));
2138            }
2139            WorktreeStoreEvent::WorktreeOrderChanged => cx.emit(Event::WorktreeOrderChanged),
2140        }
2141    }
2142
2143    fn on_worktree_added(&mut self, worktree: &Model<Worktree>, cx: &mut ModelContext<Self>) {
2144        cx.observe(worktree, |_, _, cx| cx.notify()).detach();
2145        cx.subscribe(worktree, |this, worktree, event, cx| {
2146            let is_local = worktree.read(cx).is_local();
2147            match event {
2148                worktree::Event::UpdatedEntries(changes) => {
2149                    if is_local {
2150                        this.lsp_store.update(cx, |lsp_store, cx| {
2151                            lsp_store
2152                                .update_local_worktree_language_servers(&worktree, changes, cx);
2153                        });
2154                        this.update_local_worktree_settings(&worktree, changes, cx);
2155                        this.update_prettier_settings(&worktree, changes, cx);
2156                    }
2157
2158                    cx.emit(Event::WorktreeUpdatedEntries(
2159                        worktree.read(cx).id(),
2160                        changes.clone(),
2161                    ));
2162
2163                    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
2164                    this.client()
2165                        .telemetry()
2166                        .report_discovered_project_events(worktree_id, changes);
2167                }
2168                worktree::Event::UpdatedGitRepositories(_) => {
2169                    cx.emit(Event::WorktreeUpdatedGitRepositories);
2170                }
2171                worktree::Event::DeletedEntry(id) => cx.emit(Event::DeletedEntry(*id)),
2172            }
2173        })
2174        .detach();
2175        self.metadata_changed(cx);
2176    }
2177
2178    fn on_worktree_removed(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
2179        if let Some(dev_server_project_id) = self.dev_server_project_id {
2180            let paths: Vec<String> = self
2181                .visible_worktrees(cx)
2182                .filter_map(|worktree| {
2183                    if worktree.read(cx).id() == id_to_remove {
2184                        None
2185                    } else {
2186                        Some(worktree.read(cx).abs_path().to_string_lossy().to_string())
2187                    }
2188                })
2189                .collect();
2190            if paths.len() > 0 {
2191                let request = self.client.request(proto::UpdateDevServerProject {
2192                    dev_server_project_id: dev_server_project_id.0,
2193                    paths,
2194                });
2195                cx.background_executor()
2196                    .spawn(request)
2197                    .detach_and_log_err(cx);
2198            }
2199            return;
2200        }
2201        self.environment.update(cx, |environment, _| {
2202            environment.remove_worktree_environment(id_to_remove);
2203        });
2204        self.lsp_store.update(cx, |lsp_store, cx| {
2205            lsp_store.remove_worktree(id_to_remove, cx);
2206        });
2207
2208        let mut prettier_instances_to_clean = FuturesUnordered::new();
2209        if let Some(prettier_paths) = self.prettiers_per_worktree.remove(&id_to_remove) {
2210            for path in prettier_paths.iter().flatten() {
2211                if let Some(prettier_instance) = self.prettier_instances.remove(path) {
2212                    prettier_instances_to_clean.push(async move {
2213                        prettier_instance
2214                            .server()
2215                            .await
2216                            .map(|server| server.server_id())
2217                    });
2218                }
2219            }
2220        }
2221        cx.spawn(|project, mut cx| async move {
2222            while let Some(prettier_server_id) = prettier_instances_to_clean.next().await {
2223                if let Some(prettier_server_id) = prettier_server_id {
2224                    project
2225                        .update(&mut cx, |project, cx| {
2226                            project.lsp_store.update(cx, |lsp_store, cx| {
2227                                lsp_store.unregister_supplementary_language_server(
2228                                    prettier_server_id,
2229                                    cx,
2230                                );
2231                            });
2232                        })
2233                        .ok();
2234                }
2235            }
2236        })
2237        .detach();
2238
2239        self.task_inventory().update(cx, |inventory, _| {
2240            inventory.remove_worktree_sources(id_to_remove);
2241        });
2242
2243        self.metadata_changed(cx);
2244    }
2245
2246    fn on_buffer_event(
2247        &mut self,
2248        buffer: Model<Buffer>,
2249        event: &BufferEvent,
2250        cx: &mut ModelContext<Self>,
2251    ) -> Option<()> {
2252        if matches!(
2253            event,
2254            BufferEvent::Edited { .. } | BufferEvent::Reloaded | BufferEvent::DiffBaseChanged
2255        ) {
2256            self.request_buffer_diff_recalculation(&buffer, cx);
2257        }
2258
2259        let buffer_id = buffer.read(cx).remote_id();
2260        match event {
2261            BufferEvent::Operation(operation) => {
2262                let operation = language::proto::serialize_operation(operation);
2263
2264                if let Some(ssh) = &self.ssh_session {
2265                    ssh.send(proto::UpdateBuffer {
2266                        project_id: 0,
2267                        buffer_id: buffer_id.to_proto(),
2268                        operations: vec![operation.clone()],
2269                    })
2270                    .ok();
2271                }
2272
2273                self.enqueue_buffer_ordered_message(BufferOrderedMessage::Operation {
2274                    buffer_id,
2275                    operation,
2276                })
2277                .ok();
2278            }
2279
2280            BufferEvent::Reloaded => {
2281                if self.is_local_or_ssh() {
2282                    if let Some(project_id) = self.remote_id() {
2283                        let buffer = buffer.read(cx);
2284                        self.client
2285                            .send(proto::BufferReloaded {
2286                                project_id,
2287                                buffer_id: buffer.remote_id().to_proto(),
2288                                version: serialize_version(&buffer.version()),
2289                                mtime: buffer.saved_mtime().map(|t| t.into()),
2290                                line_ending: serialize_line_ending(buffer.line_ending()) as i32,
2291                            })
2292                            .log_err();
2293                    }
2294                }
2295            }
2296
2297            _ => {}
2298        }
2299
2300        None
2301    }
2302
2303    fn request_buffer_diff_recalculation(
2304        &mut self,
2305        buffer: &Model<Buffer>,
2306        cx: &mut ModelContext<Self>,
2307    ) {
2308        self.buffers_needing_diff.insert(buffer.downgrade());
2309        let first_insertion = self.buffers_needing_diff.len() == 1;
2310
2311        let settings = ProjectSettings::get_global(cx);
2312        let delay = if let Some(delay) = settings.git.gutter_debounce {
2313            delay
2314        } else {
2315            if first_insertion {
2316                let this = cx.weak_model();
2317                cx.defer(move |cx| {
2318                    if let Some(this) = this.upgrade() {
2319                        this.update(cx, |this, cx| {
2320                            this.recalculate_buffer_diffs(cx).detach();
2321                        });
2322                    }
2323                });
2324            }
2325            return;
2326        };
2327
2328        const MIN_DELAY: u64 = 50;
2329        let delay = delay.max(MIN_DELAY);
2330        let duration = Duration::from_millis(delay);
2331
2332        self.git_diff_debouncer
2333            .fire_new(duration, cx, move |this, cx| {
2334                this.recalculate_buffer_diffs(cx)
2335            });
2336    }
2337
2338    fn recalculate_buffer_diffs(&mut self, cx: &mut ModelContext<Self>) -> Task<()> {
2339        let buffers = self.buffers_needing_diff.drain().collect::<Vec<_>>();
2340        cx.spawn(move |this, mut cx| async move {
2341            let tasks: Vec<_> = buffers
2342                .iter()
2343                .filter_map(|buffer| {
2344                    let buffer = buffer.upgrade()?;
2345                    buffer
2346                        .update(&mut cx, |buffer, cx| buffer.git_diff_recalc(cx))
2347                        .ok()
2348                        .flatten()
2349                })
2350                .collect();
2351
2352            futures::future::join_all(tasks).await;
2353
2354            this.update(&mut cx, |this, cx| {
2355                if this.buffers_needing_diff.is_empty() {
2356                    // TODO: Would a `ModelContext<Project>.notify()` suffice here?
2357                    for buffer in buffers {
2358                        if let Some(buffer) = buffer.upgrade() {
2359                            buffer.update(cx, |_, cx| cx.notify());
2360                        }
2361                    }
2362                } else {
2363                    this.recalculate_buffer_diffs(cx).detach();
2364                }
2365            })
2366            .ok();
2367        })
2368    }
2369
2370    pub fn set_language_for_buffer(
2371        &mut self,
2372        buffer: &Model<Buffer>,
2373        new_language: Arc<Language>,
2374        cx: &mut ModelContext<Self>,
2375    ) {
2376        self.lsp_store.update(cx, |lsp_store, cx| {
2377            lsp_store.set_language_for_buffer(buffer, new_language, cx)
2378        })
2379    }
2380
2381    pub fn restart_language_servers_for_buffers(
2382        &mut self,
2383        buffers: impl IntoIterator<Item = Model<Buffer>>,
2384        cx: &mut ModelContext<Self>,
2385    ) {
2386        self.lsp_store.update(cx, |lsp_store, cx| {
2387            lsp_store.restart_language_servers_for_buffers(buffers, cx)
2388        })
2389    }
2390
2391    pub fn cancel_language_server_work_for_buffers(
2392        &mut self,
2393        buffers: impl IntoIterator<Item = Model<Buffer>>,
2394        cx: &mut ModelContext<Self>,
2395    ) {
2396        self.lsp_store.update(cx, |lsp_store, cx| {
2397            lsp_store.cancel_language_server_work_for_buffers(buffers, cx)
2398        })
2399    }
2400
2401    pub fn cancel_language_server_work(
2402        &mut self,
2403        server_id: LanguageServerId,
2404        token_to_cancel: Option<String>,
2405        cx: &mut ModelContext<Self>,
2406    ) {
2407        self.lsp_store.update(cx, |lsp_store, cx| {
2408            lsp_store.cancel_language_server_work(server_id, token_to_cancel, cx)
2409        })
2410    }
2411
2412    fn enqueue_buffer_ordered_message(&mut self, message: BufferOrderedMessage) -> Result<()> {
2413        self.buffer_ordered_messages_tx
2414            .unbounded_send(message)
2415            .map_err(|e| anyhow!(e))
2416    }
2417
2418    pub fn language_server_statuses<'a>(
2419        &'a self,
2420        cx: &'a AppContext,
2421    ) -> impl DoubleEndedIterator<Item = (LanguageServerId, &'a LanguageServerStatus)> {
2422        self.lsp_store.read(cx).language_server_statuses()
2423    }
2424
2425    pub fn last_formatting_failure(&self) -> Option<&str> {
2426        self.last_formatting_failure.as_deref()
2427    }
2428
2429    pub fn update_diagnostics(
2430        &mut self,
2431        language_server_id: LanguageServerId,
2432        params: lsp::PublishDiagnosticsParams,
2433        disk_based_sources: &[String],
2434        cx: &mut ModelContext<Self>,
2435    ) -> Result<()> {
2436        self.lsp_store.update(cx, |lsp_store, cx| {
2437            lsp_store.update_diagnostics(language_server_id, params, disk_based_sources, cx)
2438        })
2439    }
2440
2441    pub fn update_diagnostic_entries(
2442        &mut self,
2443        server_id: LanguageServerId,
2444        abs_path: PathBuf,
2445        version: Option<i32>,
2446        diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
2447        cx: &mut ModelContext<Project>,
2448    ) -> Result<(), anyhow::Error> {
2449        self.lsp_store.update(cx, |lsp_store, cx| {
2450            lsp_store.update_diagnostic_entries(server_id, abs_path, version, diagnostics, cx)
2451        })
2452    }
2453
2454    pub fn reload_buffers(
2455        &self,
2456        buffers: HashSet<Model<Buffer>>,
2457        push_to_history: bool,
2458        cx: &mut ModelContext<Self>,
2459    ) -> Task<Result<ProjectTransaction>> {
2460        let mut local_buffers = Vec::new();
2461        let mut remote_buffers = None;
2462        for buffer_handle in buffers {
2463            let buffer = buffer_handle.read(cx);
2464            if buffer.is_dirty() {
2465                if let Some(file) = File::from_dyn(buffer.file()) {
2466                    if file.is_local() {
2467                        local_buffers.push(buffer_handle);
2468                    } else {
2469                        remote_buffers.get_or_insert(Vec::new()).push(buffer_handle);
2470                    }
2471                }
2472            }
2473        }
2474
2475        let remote_buffers = self.remote_id().zip(remote_buffers);
2476        let client = self.client.clone();
2477
2478        cx.spawn(move |this, mut cx| async move {
2479            let mut project_transaction = ProjectTransaction::default();
2480
2481            if let Some((project_id, remote_buffers)) = remote_buffers {
2482                let response = client
2483                    .request(proto::ReloadBuffers {
2484                        project_id,
2485                        buffer_ids: remote_buffers
2486                            .iter()
2487                            .filter_map(|buffer| {
2488                                buffer
2489                                    .update(&mut cx, |buffer, _| buffer.remote_id().into())
2490                                    .ok()
2491                            })
2492                            .collect(),
2493                    })
2494                    .await?
2495                    .transaction
2496                    .ok_or_else(|| anyhow!("missing transaction"))?;
2497                BufferStore::deserialize_project_transaction(
2498                    this.read_with(&cx, |this, _| this.buffer_store.downgrade())?,
2499                    response,
2500                    push_to_history,
2501                    cx.clone(),
2502                )
2503                .await?;
2504            }
2505
2506            for buffer in local_buffers {
2507                let transaction = buffer
2508                    .update(&mut cx, |buffer, cx| buffer.reload(cx))?
2509                    .await?;
2510                buffer.update(&mut cx, |buffer, cx| {
2511                    if let Some(transaction) = transaction {
2512                        if !push_to_history {
2513                            buffer.forget_transaction(transaction.id);
2514                        }
2515                        project_transaction.0.insert(cx.handle(), transaction);
2516                    }
2517                })?;
2518            }
2519
2520            Ok(project_transaction)
2521        })
2522    }
2523
2524    pub fn format(
2525        &mut self,
2526        buffers: HashSet<Model<Buffer>>,
2527        push_to_history: bool,
2528        trigger: FormatTrigger,
2529        cx: &mut ModelContext<Project>,
2530    ) -> Task<anyhow::Result<ProjectTransaction>> {
2531        if self.is_local_or_ssh() {
2532            let buffers_with_paths = buffers
2533                .into_iter()
2534                .map(|buffer_handle| {
2535                    let buffer = buffer_handle.read(cx);
2536                    let buffer_abs_path = File::from_dyn(buffer.file())
2537                        .and_then(|file| file.as_local().map(|f| f.abs_path(cx)));
2538                    (buffer_handle, buffer_abs_path)
2539                })
2540                .collect::<Vec<_>>();
2541
2542            cx.spawn(move |project, mut cx| async move {
2543                let result = Self::format_locally(
2544                    project.clone(),
2545                    buffers_with_paths,
2546                    push_to_history,
2547                    trigger,
2548                    cx.clone(),
2549                )
2550                .await;
2551
2552                project.update(&mut cx, |project, _| match &result {
2553                    Ok(_) => project.last_formatting_failure = None,
2554                    Err(error) => {
2555                        project.last_formatting_failure.replace(error.to_string());
2556                    }
2557                })?;
2558
2559                result
2560            })
2561        } else {
2562            let remote_id = self.remote_id();
2563            let client = self.client.clone();
2564            cx.spawn(move |this, mut cx| async move {
2565                if let Some(project_id) = remote_id {
2566                    let response = client
2567                        .request(proto::FormatBuffers {
2568                            project_id,
2569                            trigger: trigger as i32,
2570                            buffer_ids: buffers
2571                                .iter()
2572                                .map(|buffer| {
2573                                    buffer.update(&mut cx, |buffer, _| buffer.remote_id().into())
2574                                })
2575                                .collect::<Result<_>>()?,
2576                        })
2577                        .await?
2578                        .transaction
2579                        .ok_or_else(|| anyhow!("missing transaction"))?;
2580                    BufferStore::deserialize_project_transaction(
2581                        this.read_with(&cx, |this, _| this.buffer_store.downgrade())?,
2582                        response,
2583                        push_to_history,
2584                        cx,
2585                    )
2586                    .await
2587                } else {
2588                    Ok(ProjectTransaction::default())
2589                }
2590            })
2591        }
2592    }
2593
2594    async fn format_locally(
2595        project: WeakModel<Project>,
2596        mut buffers_with_paths: Vec<(Model<Buffer>, Option<PathBuf>)>,
2597        push_to_history: bool,
2598        trigger: FormatTrigger,
2599        mut cx: AsyncAppContext,
2600    ) -> anyhow::Result<ProjectTransaction> {
2601        // Do not allow multiple concurrent formatting requests for the
2602        // same buffer.
2603        let lsp_store = project.update(&mut cx, |this, cx| {
2604            buffers_with_paths.retain(|(buffer, _)| {
2605                this.buffers_being_formatted
2606                    .insert(buffer.read(cx).remote_id())
2607            });
2608            this.lsp_store.downgrade()
2609        })?;
2610
2611        let _cleanup = defer({
2612            let this = project.clone();
2613            let mut cx = cx.clone();
2614            let buffers = &buffers_with_paths;
2615            move || {
2616                this.update(&mut cx, |this, cx| {
2617                    for (buffer, _) in buffers {
2618                        this.buffers_being_formatted
2619                            .remove(&buffer.read(cx).remote_id());
2620                    }
2621                })
2622                .ok();
2623            }
2624        });
2625
2626        let mut project_transaction = ProjectTransaction::default();
2627        for (buffer, buffer_abs_path) in &buffers_with_paths {
2628            let (primary_adapter_and_server, adapters_and_servers) =
2629                project.update(&mut cx, |project, cx| {
2630                    let buffer = buffer.read(cx);
2631
2632                    let adapters_and_servers = project
2633                        .language_servers_for_buffer(buffer, cx)
2634                        .map(|(adapter, lsp)| (adapter.clone(), lsp.clone()))
2635                        .collect::<Vec<_>>();
2636
2637                    let primary_adapter = project
2638                        .lsp_store
2639                        .read(cx)
2640                        .primary_language_server_for_buffer(buffer, cx)
2641                        .map(|(adapter, lsp)| (adapter.clone(), lsp.clone()));
2642
2643                    (primary_adapter, adapters_and_servers)
2644                })?;
2645
2646            let settings = buffer.update(&mut cx, |buffer, cx| {
2647                language_settings(buffer.language(), buffer.file(), cx).clone()
2648            })?;
2649
2650            let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save;
2651            let ensure_final_newline = settings.ensure_final_newline_on_save;
2652
2653            // First, format buffer's whitespace according to the settings.
2654            let trailing_whitespace_diff = if remove_trailing_whitespace {
2655                Some(
2656                    buffer
2657                        .update(&mut cx, |b, cx| b.remove_trailing_whitespace(cx))?
2658                        .await,
2659                )
2660            } else {
2661                None
2662            };
2663            let whitespace_transaction_id = buffer.update(&mut cx, |buffer, cx| {
2664                buffer.finalize_last_transaction();
2665                buffer.start_transaction();
2666                if let Some(diff) = trailing_whitespace_diff {
2667                    buffer.apply_diff(diff, cx);
2668                }
2669                if ensure_final_newline {
2670                    buffer.ensure_final_newline(cx);
2671                }
2672                buffer.end_transaction(cx)
2673            })?;
2674
2675            // Apply the `code_actions_on_format` before we run the formatter.
2676            let code_actions = deserialize_code_actions(&settings.code_actions_on_format);
2677            #[allow(clippy::nonminimal_bool)]
2678            if !code_actions.is_empty()
2679                && !(trigger == FormatTrigger::Save && settings.format_on_save == FormatOnSave::Off)
2680            {
2681                LspStore::execute_code_actions_on_servers(
2682                    &lsp_store,
2683                    &adapters_and_servers,
2684                    code_actions,
2685                    buffer,
2686                    push_to_history,
2687                    &mut project_transaction,
2688                    &mut cx,
2689                )
2690                .await?;
2691            }
2692
2693            // Apply language-specific formatting using either the primary language server
2694            // or external command.
2695            // Except for code actions, which are applied with all connected language servers.
2696            let primary_language_server =
2697                primary_adapter_and_server.map(|(_adapter, server)| server.clone());
2698            let server_and_buffer = primary_language_server
2699                .as_ref()
2700                .zip(buffer_abs_path.as_ref());
2701
2702            let prettier_settings = buffer.read_with(&mut cx, |buffer, cx| {
2703                language_settings(buffer.language(), buffer.file(), cx)
2704                    .prettier
2705                    .clone()
2706            })?;
2707
2708            let mut format_operations: Vec<FormatOperation> = vec![];
2709            {
2710                match trigger {
2711                    FormatTrigger::Save => {
2712                        match &settings.format_on_save {
2713                            FormatOnSave::Off => {
2714                                // nothing
2715                            }
2716                            FormatOnSave::On => {
2717                                match &settings.formatter {
2718                                    SelectedFormatter::Auto => {
2719                                        // do the auto-format: prefer prettier, fallback to primary language server
2720                                        let diff = {
2721                                            if prettier_settings.allowed {
2722                                                Self::perform_format(
2723                                                    &Formatter::Prettier,
2724                                                    server_and_buffer,
2725                                                    project.clone(),
2726                                                    buffer,
2727                                                    buffer_abs_path,
2728                                                    &settings,
2729                                                    &adapters_and_servers,
2730                                                    push_to_history,
2731                                                    &mut project_transaction,
2732                                                    &mut cx,
2733                                                )
2734                                                .await
2735                                            } else {
2736                                                Self::perform_format(
2737                                                    &Formatter::LanguageServer { name: None },
2738                                                    server_and_buffer,
2739                                                    project.clone(),
2740                                                    buffer,
2741                                                    buffer_abs_path,
2742                                                    &settings,
2743                                                    &adapters_and_servers,
2744                                                    push_to_history,
2745                                                    &mut project_transaction,
2746                                                    &mut cx,
2747                                                )
2748                                                .await
2749                                            }
2750                                        }
2751                                        .log_err()
2752                                        .flatten();
2753                                        if let Some(op) = diff {
2754                                            format_operations.push(op);
2755                                        }
2756                                    }
2757                                    SelectedFormatter::List(formatters) => {
2758                                        for formatter in formatters.as_ref() {
2759                                            let diff = Self::perform_format(
2760                                                formatter,
2761                                                server_and_buffer,
2762                                                project.clone(),
2763                                                buffer,
2764                                                buffer_abs_path,
2765                                                &settings,
2766                                                &adapters_and_servers,
2767                                                push_to_history,
2768                                                &mut project_transaction,
2769                                                &mut cx,
2770                                            )
2771                                            .await
2772                                            .log_err()
2773                                            .flatten();
2774                                            if let Some(op) = diff {
2775                                                format_operations.push(op);
2776                                            }
2777
2778                                            // format with formatter
2779                                        }
2780                                    }
2781                                }
2782                            }
2783                            FormatOnSave::List(formatters) => {
2784                                for formatter in formatters.as_ref() {
2785                                    let diff = Self::perform_format(
2786                                        &formatter,
2787                                        server_and_buffer,
2788                                        project.clone(),
2789                                        buffer,
2790                                        buffer_abs_path,
2791                                        &settings,
2792                                        &adapters_and_servers,
2793                                        push_to_history,
2794                                        &mut project_transaction,
2795                                        &mut cx,
2796                                    )
2797                                    .await
2798                                    .log_err()
2799                                    .flatten();
2800                                    if let Some(op) = diff {
2801                                        format_operations.push(op);
2802                                    }
2803                                }
2804                            }
2805                        }
2806                    }
2807                    FormatTrigger::Manual => {
2808                        match &settings.formatter {
2809                            SelectedFormatter::Auto => {
2810                                // do the auto-format: prefer prettier, fallback to primary language server
2811                                let diff = {
2812                                    if prettier_settings.allowed {
2813                                        Self::perform_format(
2814                                            &Formatter::Prettier,
2815                                            server_and_buffer,
2816                                            project.clone(),
2817                                            buffer,
2818                                            buffer_abs_path,
2819                                            &settings,
2820                                            &adapters_and_servers,
2821                                            push_to_history,
2822                                            &mut project_transaction,
2823                                            &mut cx,
2824                                        )
2825                                        .await
2826                                    } else {
2827                                        Self::perform_format(
2828                                            &Formatter::LanguageServer { name: None },
2829                                            server_and_buffer,
2830                                            project.clone(),
2831                                            buffer,
2832                                            buffer_abs_path,
2833                                            &settings,
2834                                            &adapters_and_servers,
2835                                            push_to_history,
2836                                            &mut project_transaction,
2837                                            &mut cx,
2838                                        )
2839                                        .await
2840                                    }
2841                                }
2842                                .log_err()
2843                                .flatten();
2844
2845                                if let Some(op) = diff {
2846                                    format_operations.push(op)
2847                                }
2848                            }
2849                            SelectedFormatter::List(formatters) => {
2850                                for formatter in formatters.as_ref() {
2851                                    // format with formatter
2852                                    let diff = Self::perform_format(
2853                                        formatter,
2854                                        server_and_buffer,
2855                                        project.clone(),
2856                                        buffer,
2857                                        buffer_abs_path,
2858                                        &settings,
2859                                        &adapters_and_servers,
2860                                        push_to_history,
2861                                        &mut project_transaction,
2862                                        &mut cx,
2863                                    )
2864                                    .await
2865                                    .log_err()
2866                                    .flatten();
2867                                    if let Some(op) = diff {
2868                                        format_operations.push(op);
2869                                    }
2870                                }
2871                            }
2872                        }
2873                    }
2874                }
2875            }
2876
2877            buffer.update(&mut cx, |b, cx| {
2878                // If the buffer had its whitespace formatted and was edited while the language-specific
2879                // formatting was being computed, avoid applying the language-specific formatting, because
2880                // it can't be grouped with the whitespace formatting in the undo history.
2881                if let Some(transaction_id) = whitespace_transaction_id {
2882                    if b.peek_undo_stack()
2883                        .map_or(true, |e| e.transaction_id() != transaction_id)
2884                    {
2885                        format_operations.clear();
2886                    }
2887                }
2888
2889                // Apply any language-specific formatting, and group the two formatting operations
2890                // in the buffer's undo history.
2891                for operation in format_operations {
2892                    match operation {
2893                        FormatOperation::Lsp(edits) => {
2894                            b.edit(edits, None, cx);
2895                        }
2896                        FormatOperation::External(diff) => {
2897                            b.apply_diff(diff, cx);
2898                        }
2899                        FormatOperation::Prettier(diff) => {
2900                            b.apply_diff(diff, cx);
2901                        }
2902                    }
2903
2904                    if let Some(transaction_id) = whitespace_transaction_id {
2905                        b.group_until_transaction(transaction_id);
2906                    } else if let Some(transaction) = project_transaction.0.get(buffer) {
2907                        b.group_until_transaction(transaction.id)
2908                    }
2909                }
2910
2911                if let Some(transaction) = b.finalize_last_transaction().cloned() {
2912                    if !push_to_history {
2913                        b.forget_transaction(transaction.id);
2914                    }
2915                    project_transaction.0.insert(buffer.clone(), transaction);
2916                }
2917            })?;
2918        }
2919
2920        Ok(project_transaction)
2921    }
2922
2923    #[allow(clippy::too_many_arguments)]
2924    async fn perform_format(
2925        formatter: &Formatter,
2926        primary_server_and_buffer: Option<(&Arc<LanguageServer>, &PathBuf)>,
2927        project: WeakModel<Project>,
2928        buffer: &Model<Buffer>,
2929        buffer_abs_path: &Option<PathBuf>,
2930        settings: &LanguageSettings,
2931        adapters_and_servers: &Vec<(Arc<CachedLspAdapter>, Arc<LanguageServer>)>,
2932        push_to_history: bool,
2933        transaction: &mut ProjectTransaction,
2934        mut cx: &mut AsyncAppContext,
2935    ) -> Result<Option<FormatOperation>, anyhow::Error> {
2936        let result = match formatter {
2937            Formatter::LanguageServer { name } => {
2938                if let Some((language_server, buffer_abs_path)) = primary_server_and_buffer {
2939                    let language_server = if let Some(name) = name {
2940                        adapters_and_servers
2941                            .iter()
2942                            .find_map(|(adapter, server)| {
2943                                adapter.name.0.as_ref().eq(name.as_str()).then_some(server)
2944                            })
2945                            .unwrap_or_else(|| language_server)
2946                    } else {
2947                        language_server
2948                    };
2949
2950                    let lsp_store = project.update(cx, |p, _| p.lsp_store.downgrade())?;
2951                    Some(FormatOperation::Lsp(
2952                        LspStore::format_via_lsp(
2953                            &lsp_store,
2954                            buffer,
2955                            buffer_abs_path,
2956                            language_server,
2957                            settings,
2958                            cx,
2959                        )
2960                        .await
2961                        .context("failed to format via language server")?,
2962                    ))
2963                } else {
2964                    None
2965                }
2966            }
2967            Formatter::Prettier => {
2968                prettier_support::format_with_prettier(&project, buffer, &mut cx)
2969                    .await
2970                    .transpose()
2971                    .ok()
2972                    .flatten()
2973            }
2974            Formatter::External { command, arguments } => {
2975                let buffer_abs_path = buffer_abs_path.as_ref().map(|path| path.as_path());
2976                Self::format_via_external_command(
2977                    buffer,
2978                    buffer_abs_path,
2979                    &command,
2980                    &arguments,
2981                    &mut cx,
2982                )
2983                .await
2984                .context(format!(
2985                    "failed to format via external command {:?}",
2986                    command
2987                ))?
2988                .map(FormatOperation::External)
2989            }
2990            Formatter::CodeActions(code_actions) => {
2991                let code_actions = deserialize_code_actions(&code_actions);
2992                let lsp_store = project.update(cx, |p, _| p.lsp_store.downgrade())?;
2993                if !code_actions.is_empty() {
2994                    LspStore::execute_code_actions_on_servers(
2995                        &lsp_store,
2996                        &adapters_and_servers,
2997                        code_actions,
2998                        buffer,
2999                        push_to_history,
3000                        transaction,
3001                        cx,
3002                    )
3003                    .await?;
3004                }
3005                None
3006            }
3007        };
3008        anyhow::Ok(result)
3009    }
3010
3011    async fn format_via_external_command(
3012        buffer: &Model<Buffer>,
3013        buffer_abs_path: Option<&Path>,
3014        command: &str,
3015        arguments: &[String],
3016        cx: &mut AsyncAppContext,
3017    ) -> Result<Option<Diff>> {
3018        let working_dir_path = buffer.update(cx, |buffer, cx| {
3019            let file = File::from_dyn(buffer.file())?;
3020            let worktree = file.worktree.read(cx);
3021            let mut worktree_path = worktree.abs_path().to_path_buf();
3022            if worktree.root_entry()?.is_file() {
3023                worktree_path.pop();
3024            }
3025            Some(worktree_path)
3026        })?;
3027
3028        let mut child = smol::process::Command::new(command);
3029        #[cfg(target_os = "windows")]
3030        {
3031            use smol::process::windows::CommandExt;
3032            child.creation_flags(windows::Win32::System::Threading::CREATE_NO_WINDOW.0);
3033        }
3034
3035        if let Some(working_dir_path) = working_dir_path {
3036            child.current_dir(working_dir_path);
3037        }
3038
3039        let mut child = child
3040            .args(arguments.iter().map(|arg| {
3041                if let Some(buffer_abs_path) = buffer_abs_path {
3042                    arg.replace("{buffer_path}", &buffer_abs_path.to_string_lossy())
3043                } else {
3044                    arg.replace("{buffer_path}", "Untitled")
3045                }
3046            }))
3047            .stdin(smol::process::Stdio::piped())
3048            .stdout(smol::process::Stdio::piped())
3049            .stderr(smol::process::Stdio::piped())
3050            .spawn()?;
3051
3052        let stdin = child
3053            .stdin
3054            .as_mut()
3055            .ok_or_else(|| anyhow!("failed to acquire stdin"))?;
3056        let text = buffer.update(cx, |buffer, _| buffer.as_rope().clone())?;
3057        for chunk in text.chunks() {
3058            stdin.write_all(chunk.as_bytes()).await?;
3059        }
3060        stdin.flush().await?;
3061
3062        let output = child.output().await?;
3063        if !output.status.success() {
3064            return Err(anyhow!(
3065                "command failed with exit code {:?}:\nstdout: {}\nstderr: {}",
3066                output.status.code(),
3067                String::from_utf8_lossy(&output.stdout),
3068                String::from_utf8_lossy(&output.stderr),
3069            ));
3070        }
3071
3072        let stdout = String::from_utf8(output.stdout)?;
3073        Ok(Some(
3074            buffer
3075                .update(cx, |buffer, cx| buffer.diff(stdout, cx))?
3076                .await,
3077        ))
3078    }
3079
3080    #[inline(never)]
3081    fn definition_impl(
3082        &self,
3083        buffer: &Model<Buffer>,
3084        position: PointUtf16,
3085        cx: &mut ModelContext<Self>,
3086    ) -> Task<Result<Vec<LocationLink>>> {
3087        self.request_lsp(
3088            buffer.clone(),
3089            LanguageServerToQuery::Primary,
3090            GetDefinition { position },
3091            cx,
3092        )
3093    }
3094    pub fn definition<T: ToPointUtf16>(
3095        &self,
3096        buffer: &Model<Buffer>,
3097        position: T,
3098        cx: &mut ModelContext<Self>,
3099    ) -> Task<Result<Vec<LocationLink>>> {
3100        let position = position.to_point_utf16(buffer.read(cx));
3101        self.definition_impl(buffer, position, cx)
3102    }
3103
3104    fn declaration_impl(
3105        &self,
3106        buffer: &Model<Buffer>,
3107        position: PointUtf16,
3108        cx: &mut ModelContext<Self>,
3109    ) -> Task<Result<Vec<LocationLink>>> {
3110        self.request_lsp(
3111            buffer.clone(),
3112            LanguageServerToQuery::Primary,
3113            GetDeclaration { position },
3114            cx,
3115        )
3116    }
3117
3118    pub fn declaration<T: ToPointUtf16>(
3119        &self,
3120        buffer: &Model<Buffer>,
3121        position: T,
3122        cx: &mut ModelContext<Self>,
3123    ) -> Task<Result<Vec<LocationLink>>> {
3124        let position = position.to_point_utf16(buffer.read(cx));
3125        self.declaration_impl(buffer, position, cx)
3126    }
3127
3128    fn type_definition_impl(
3129        &self,
3130        buffer: &Model<Buffer>,
3131        position: PointUtf16,
3132        cx: &mut ModelContext<Self>,
3133    ) -> Task<Result<Vec<LocationLink>>> {
3134        self.request_lsp(
3135            buffer.clone(),
3136            LanguageServerToQuery::Primary,
3137            GetTypeDefinition { position },
3138            cx,
3139        )
3140    }
3141
3142    pub fn type_definition<T: ToPointUtf16>(
3143        &self,
3144        buffer: &Model<Buffer>,
3145        position: T,
3146        cx: &mut ModelContext<Self>,
3147    ) -> Task<Result<Vec<LocationLink>>> {
3148        let position = position.to_point_utf16(buffer.read(cx));
3149        self.type_definition_impl(buffer, position, cx)
3150    }
3151
3152    pub fn implementation<T: ToPointUtf16>(
3153        &self,
3154        buffer: &Model<Buffer>,
3155        position: T,
3156        cx: &mut ModelContext<Self>,
3157    ) -> Task<Result<Vec<LocationLink>>> {
3158        let position = position.to_point_utf16(buffer.read(cx));
3159        self.request_lsp(
3160            buffer.clone(),
3161            LanguageServerToQuery::Primary,
3162            GetImplementation { position },
3163            cx,
3164        )
3165    }
3166
3167    pub fn references<T: ToPointUtf16>(
3168        &self,
3169        buffer: &Model<Buffer>,
3170        position: T,
3171        cx: &mut ModelContext<Self>,
3172    ) -> Task<Result<Vec<Location>>> {
3173        let position = position.to_point_utf16(buffer.read(cx));
3174        self.request_lsp(
3175            buffer.clone(),
3176            LanguageServerToQuery::Primary,
3177            GetReferences { position },
3178            cx,
3179        )
3180    }
3181
3182    fn document_highlights_impl(
3183        &self,
3184        buffer: &Model<Buffer>,
3185        position: PointUtf16,
3186        cx: &mut ModelContext<Self>,
3187    ) -> Task<Result<Vec<DocumentHighlight>>> {
3188        self.request_lsp(
3189            buffer.clone(),
3190            LanguageServerToQuery::Primary,
3191            GetDocumentHighlights { position },
3192            cx,
3193        )
3194    }
3195
3196    pub fn document_highlights<T: ToPointUtf16>(
3197        &self,
3198        buffer: &Model<Buffer>,
3199        position: T,
3200        cx: &mut ModelContext<Self>,
3201    ) -> Task<Result<Vec<DocumentHighlight>>> {
3202        let position = position.to_point_utf16(buffer.read(cx));
3203        self.document_highlights_impl(buffer, position, cx)
3204    }
3205
3206    pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
3207        self.lsp_store
3208            .update(cx, |lsp_store, cx| lsp_store.symbols(query, cx))
3209    }
3210
3211    pub fn open_buffer_for_symbol(
3212        &mut self,
3213        symbol: &Symbol,
3214        cx: &mut ModelContext<Self>,
3215    ) -> Task<Result<Model<Buffer>>> {
3216        self.lsp_store.update(cx, |lsp_store, cx| {
3217            lsp_store.open_buffer_for_symbol(symbol, cx)
3218        })
3219    }
3220
3221    pub fn open_local_buffer_via_lsp(
3222        &mut self,
3223        abs_path: lsp::Url,
3224        language_server_id: LanguageServerId,
3225        language_server_name: LanguageServerName,
3226        cx: &mut ModelContext<Self>,
3227    ) -> Task<Result<Model<Buffer>>> {
3228        self.lsp_store.update(cx, |lsp_store, cx| {
3229            lsp_store.open_local_buffer_via_lsp(
3230                abs_path,
3231                language_server_id,
3232                language_server_name,
3233                cx,
3234            )
3235        })
3236    }
3237
3238    pub fn signature_help<T: ToPointUtf16>(
3239        &self,
3240        buffer: &Model<Buffer>,
3241        position: T,
3242        cx: &mut ModelContext<Self>,
3243    ) -> Task<Vec<SignatureHelp>> {
3244        self.lsp_store.update(cx, |lsp_store, cx| {
3245            lsp_store.signature_help(buffer, position, cx)
3246        })
3247    }
3248
3249    pub fn hover<T: ToPointUtf16>(
3250        &self,
3251        buffer: &Model<Buffer>,
3252        position: T,
3253        cx: &mut ModelContext<Self>,
3254    ) -> Task<Vec<Hover>> {
3255        let position = position.to_point_utf16(buffer.read(cx));
3256        self.lsp_store
3257            .update(cx, |lsp_store, cx| lsp_store.hover(buffer, position, cx))
3258    }
3259
3260    pub fn linked_edit(
3261        &self,
3262        buffer: &Model<Buffer>,
3263        position: Anchor,
3264        cx: &mut ModelContext<Self>,
3265    ) -> Task<Result<Vec<Range<Anchor>>>> {
3266        self.lsp_store.update(cx, |lsp_store, cx| {
3267            lsp_store.linked_edit(buffer, position, cx)
3268        })
3269    }
3270
3271    pub fn completions<T: ToOffset + ToPointUtf16>(
3272        &self,
3273        buffer: &Model<Buffer>,
3274        position: T,
3275        context: CompletionContext,
3276        cx: &mut ModelContext<Self>,
3277    ) -> Task<Result<Vec<Completion>>> {
3278        let position = position.to_point_utf16(buffer.read(cx));
3279        self.lsp_store.update(cx, |lsp_store, cx| {
3280            lsp_store.completions(buffer, position, context, cx)
3281        })
3282    }
3283
3284    pub fn resolve_completions(
3285        &self,
3286        buffer: Model<Buffer>,
3287        completion_indices: Vec<usize>,
3288        completions: Arc<RwLock<Box<[Completion]>>>,
3289        cx: &mut ModelContext<Self>,
3290    ) -> Task<Result<bool>> {
3291        self.lsp_store.update(cx, |lsp_store, cx| {
3292            lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
3293        })
3294    }
3295
3296    pub fn apply_additional_edits_for_completion(
3297        &self,
3298        buffer_handle: Model<Buffer>,
3299        completion: Completion,
3300        push_to_history: bool,
3301        cx: &mut ModelContext<Self>,
3302    ) -> Task<Result<Option<Transaction>>> {
3303        self.lsp_store.update(cx, |lsp_store, cx| {
3304            lsp_store.apply_additional_edits_for_completion(
3305                buffer_handle,
3306                completion,
3307                push_to_history,
3308                cx,
3309            )
3310        })
3311    }
3312
3313    pub fn code_actions<T: Clone + ToOffset>(
3314        &mut self,
3315        buffer_handle: &Model<Buffer>,
3316        range: Range<T>,
3317        cx: &mut ModelContext<Self>,
3318    ) -> Task<Vec<CodeAction>> {
3319        let buffer = buffer_handle.read(cx);
3320        let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
3321        self.lsp_store.update(cx, |lsp_store, cx| {
3322            lsp_store.code_actions(buffer_handle, range, cx)
3323        })
3324    }
3325
3326    pub fn apply_code_action(
3327        &self,
3328        buffer_handle: Model<Buffer>,
3329        action: CodeAction,
3330        push_to_history: bool,
3331        cx: &mut ModelContext<Self>,
3332    ) -> Task<Result<ProjectTransaction>> {
3333        self.lsp_store.update(cx, |lsp_store, cx| {
3334            lsp_store.apply_code_action(buffer_handle, action, push_to_history, cx)
3335        })
3336    }
3337
3338    fn prepare_rename_impl(
3339        &mut self,
3340        buffer: Model<Buffer>,
3341        position: PointUtf16,
3342        cx: &mut ModelContext<Self>,
3343    ) -> Task<Result<Option<Range<Anchor>>>> {
3344        self.request_lsp(
3345            buffer,
3346            LanguageServerToQuery::Primary,
3347            PrepareRename { position },
3348            cx,
3349        )
3350    }
3351    pub fn prepare_rename<T: ToPointUtf16>(
3352        &mut self,
3353        buffer: Model<Buffer>,
3354        position: T,
3355        cx: &mut ModelContext<Self>,
3356    ) -> Task<Result<Option<Range<Anchor>>>> {
3357        let position = position.to_point_utf16(buffer.read(cx));
3358        self.prepare_rename_impl(buffer, position, cx)
3359    }
3360
3361    fn perform_rename_impl(
3362        &mut self,
3363        buffer: Model<Buffer>,
3364        position: PointUtf16,
3365        new_name: String,
3366        push_to_history: bool,
3367        cx: &mut ModelContext<Self>,
3368    ) -> Task<Result<ProjectTransaction>> {
3369        let position = position.to_point_utf16(buffer.read(cx));
3370        self.request_lsp(
3371            buffer,
3372            LanguageServerToQuery::Primary,
3373            PerformRename {
3374                position,
3375                new_name,
3376                push_to_history,
3377            },
3378            cx,
3379        )
3380    }
3381    pub fn perform_rename<T: ToPointUtf16>(
3382        &mut self,
3383        buffer: Model<Buffer>,
3384        position: T,
3385        new_name: String,
3386        push_to_history: bool,
3387        cx: &mut ModelContext<Self>,
3388    ) -> Task<Result<ProjectTransaction>> {
3389        let position = position.to_point_utf16(buffer.read(cx));
3390        self.perform_rename_impl(buffer, position, new_name, push_to_history, cx)
3391    }
3392
3393    pub fn on_type_format<T: ToPointUtf16>(
3394        &mut self,
3395        buffer: Model<Buffer>,
3396        position: T,
3397        trigger: String,
3398        push_to_history: bool,
3399        cx: &mut ModelContext<Self>,
3400    ) -> Task<Result<Option<Transaction>>> {
3401        self.lsp_store.update(cx, |lsp_store, cx| {
3402            lsp_store.on_type_format(buffer, position, trigger, push_to_history, cx)
3403        })
3404    }
3405
3406    pub fn inlay_hints<T: ToOffset>(
3407        &mut self,
3408        buffer_handle: Model<Buffer>,
3409        range: Range<T>,
3410        cx: &mut ModelContext<Self>,
3411    ) -> Task<anyhow::Result<Vec<InlayHint>>> {
3412        let buffer = buffer_handle.read(cx);
3413        let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
3414        self.lsp_store.update(cx, |lsp_store, cx| {
3415            lsp_store.inlay_hints(buffer_handle, range, cx)
3416        })
3417    }
3418
3419    pub fn resolve_inlay_hint(
3420        &self,
3421        hint: InlayHint,
3422        buffer_handle: Model<Buffer>,
3423        server_id: LanguageServerId,
3424        cx: &mut ModelContext<Self>,
3425    ) -> Task<anyhow::Result<InlayHint>> {
3426        self.lsp_store.update(cx, |lsp_store, cx| {
3427            lsp_store.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
3428        })
3429    }
3430
3431    pub fn search(
3432        &mut self,
3433        query: SearchQuery,
3434        cx: &mut ModelContext<Self>,
3435    ) -> Receiver<SearchResult> {
3436        let (result_tx, result_rx) = smol::channel::unbounded();
3437
3438        let matching_buffers_rx = if query.is_opened_only() {
3439            self.sort_candidate_buffers(&query, cx)
3440        } else {
3441            self.search_for_candidate_buffers(&query, MAX_SEARCH_RESULT_FILES + 1, cx)
3442        };
3443
3444        cx.spawn(|_, cx| async move {
3445            let mut range_count = 0;
3446            let mut buffer_count = 0;
3447            let mut limit_reached = false;
3448            let query = Arc::new(query);
3449            let mut chunks = matching_buffers_rx.ready_chunks(64);
3450
3451            // Now that we know what paths match the query, we will load at most
3452            // 64 buffers at a time to avoid overwhelming the main thread. For each
3453            // opened buffer, we will spawn a background task that retrieves all the
3454            // ranges in the buffer matched by the query.
3455            'outer: while let Some(matching_buffer_chunk) = chunks.next().await {
3456                let mut chunk_results = Vec::new();
3457                for buffer in matching_buffer_chunk {
3458                    let buffer = buffer.clone();
3459                    let query = query.clone();
3460                    let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot())?;
3461                    chunk_results.push(cx.background_executor().spawn(async move {
3462                        let ranges = query
3463                            .search(&snapshot, None)
3464                            .await
3465                            .iter()
3466                            .map(|range| {
3467                                snapshot.anchor_before(range.start)
3468                                    ..snapshot.anchor_after(range.end)
3469                            })
3470                            .collect::<Vec<_>>();
3471                        anyhow::Ok((buffer, ranges))
3472                    }));
3473                }
3474
3475                let chunk_results = futures::future::join_all(chunk_results).await;
3476                for result in chunk_results {
3477                    if let Some((buffer, ranges)) = result.log_err() {
3478                        range_count += ranges.len();
3479                        buffer_count += 1;
3480                        result_tx
3481                            .send(SearchResult::Buffer { buffer, ranges })
3482                            .await?;
3483                        if buffer_count > MAX_SEARCH_RESULT_FILES
3484                            || range_count > MAX_SEARCH_RESULT_RANGES
3485                        {
3486                            limit_reached = true;
3487                            break 'outer;
3488                        }
3489                    }
3490                }
3491            }
3492
3493            if limit_reached {
3494                result_tx.send(SearchResult::LimitReached).await?;
3495            }
3496
3497            anyhow::Ok(())
3498        })
3499        .detach();
3500
3501        result_rx
3502    }
3503
3504    fn search_for_candidate_buffers(
3505        &mut self,
3506        query: &SearchQuery,
3507        limit: usize,
3508        cx: &mut ModelContext<Project>,
3509    ) -> Receiver<Model<Buffer>> {
3510        if self.is_local() {
3511            let fs = self.fs.clone();
3512            return self.buffer_store.update(cx, |buffer_store, cx| {
3513                buffer_store.find_search_candidates(query, limit, fs, cx)
3514            });
3515        } else {
3516            self.search_for_candidate_buffers_remote(query, limit, cx)
3517        }
3518    }
3519
3520    fn sort_candidate_buffers(
3521        &mut self,
3522        search_query: &SearchQuery,
3523        cx: &mut ModelContext<Project>,
3524    ) -> Receiver<Model<Buffer>> {
3525        let worktree_store = self.worktree_store.read(cx);
3526        let mut buffers = search_query
3527            .buffers()
3528            .into_iter()
3529            .flatten()
3530            .filter(|buffer| {
3531                let b = buffer.read(cx);
3532                if let Some(file) = b.file() {
3533                    if !search_query.file_matches(file.path()) {
3534                        return false;
3535                    }
3536                    if let Some(entry) = b
3537                        .entry_id(cx)
3538                        .and_then(|entry_id| worktree_store.entry_for_id(entry_id, cx))
3539                    {
3540                        if entry.is_ignored && !search_query.include_ignored() {
3541                            return false;
3542                        }
3543                    }
3544                }
3545                return true;
3546            })
3547            .collect::<Vec<_>>();
3548        let (tx, rx) = smol::channel::unbounded();
3549        buffers.sort_by(|a, b| match (a.read(cx).file(), b.read(cx).file()) {
3550            (None, None) => a.read(cx).remote_id().cmp(&b.read(cx).remote_id()),
3551            (None, Some(_)) => std::cmp::Ordering::Less,
3552            (Some(_), None) => std::cmp::Ordering::Greater,
3553            (Some(a), Some(b)) => compare_paths((a.path(), true), (b.path(), true)),
3554        });
3555        for buffer in buffers {
3556            tx.send_blocking(buffer.clone()).unwrap()
3557        }
3558
3559        rx
3560    }
3561
3562    fn search_for_candidate_buffers_remote(
3563        &mut self,
3564        query: &SearchQuery,
3565        limit: usize,
3566        cx: &mut ModelContext<Project>,
3567    ) -> Receiver<Model<Buffer>> {
3568        let (tx, rx) = smol::channel::unbounded();
3569
3570        let (client, remote_id): (AnyProtoClient, _) =
3571            if let Some(ssh_session) = self.ssh_session.clone() {
3572                (ssh_session.into(), 0)
3573            } else if let Some(remote_id) = self.remote_id() {
3574                (self.client.clone().into(), remote_id)
3575            } else {
3576                return rx;
3577            };
3578
3579        let request = client.request(proto::FindSearchCandidates {
3580            project_id: remote_id,
3581            query: Some(query.to_proto()),
3582            limit: limit as _,
3583        });
3584        let guard = self.retain_remotely_created_buffers(cx);
3585
3586        cx.spawn(move |this, mut cx| async move {
3587            let response = request.await?;
3588            for buffer_id in response.buffer_ids {
3589                let buffer_id = BufferId::new(buffer_id)?;
3590                let buffer = this
3591                    .update(&mut cx, |this, cx| {
3592                        this.wait_for_remote_buffer(buffer_id, cx)
3593                    })?
3594                    .await?;
3595                let _ = tx.send(buffer).await;
3596            }
3597
3598            drop(guard);
3599            anyhow::Ok(())
3600        })
3601        .detach_and_log_err(cx);
3602        rx
3603    }
3604
3605    pub fn request_lsp<R: LspCommand>(
3606        &self,
3607        buffer_handle: Model<Buffer>,
3608        server: LanguageServerToQuery,
3609        request: R,
3610        cx: &mut ModelContext<Self>,
3611    ) -> Task<Result<R::Response>>
3612    where
3613        <R::LspRequest as lsp::request::Request>::Result: Send,
3614        <R::LspRequest as lsp::request::Request>::Params: Send,
3615    {
3616        self.lsp_store.update(cx, |lsp_store, cx| {
3617            lsp_store.request_lsp(buffer_handle, server, request, cx)
3618        })
3619    }
3620
3621    /// Move a worktree to a new position in the worktree order.
3622    ///
3623    /// The worktree will moved to the opposite side of the destination worktree.
3624    ///
3625    /// # Example
3626    ///
3627    /// Given the worktree order `[11, 22, 33]` and a call to move worktree `22` to `33`,
3628    /// worktree_order will be updated to produce the indexes `[11, 33, 22]`.
3629    ///
3630    /// Given the worktree order `[11, 22, 33]` and a call to move worktree `22` to `11`,
3631    /// worktree_order will be updated to produce the indexes `[22, 11, 33]`.
3632    ///
3633    /// # Errors
3634    ///
3635    /// An error will be returned if the worktree or destination worktree are not found.
3636    pub fn move_worktree(
3637        &mut self,
3638        source: WorktreeId,
3639        destination: WorktreeId,
3640        cx: &mut ModelContext<'_, Self>,
3641    ) -> Result<()> {
3642        self.worktree_store.update(cx, |worktree_store, cx| {
3643            worktree_store.move_worktree(source, destination, cx)
3644        })
3645    }
3646
3647    pub fn find_or_create_worktree(
3648        &mut self,
3649        abs_path: impl AsRef<Path>,
3650        visible: bool,
3651        cx: &mut ModelContext<Self>,
3652    ) -> Task<Result<(Model<Worktree>, PathBuf)>> {
3653        let abs_path = abs_path.as_ref();
3654        if let Some((tree, relative_path)) = self.find_worktree(abs_path, cx) {
3655            Task::ready(Ok((tree, relative_path)))
3656        } else {
3657            let worktree = self.create_worktree(abs_path, visible, cx);
3658            cx.background_executor()
3659                .spawn(async move { Ok((worktree.await?, PathBuf::new())) })
3660        }
3661    }
3662
3663    pub fn find_worktree(
3664        &self,
3665        abs_path: &Path,
3666        cx: &AppContext,
3667    ) -> Option<(Model<Worktree>, PathBuf)> {
3668        self.worktree_store.read_with(cx, |worktree_store, cx| {
3669            worktree_store.find_worktree(abs_path, cx)
3670        })
3671    }
3672
3673    pub fn is_shared(&self) -> bool {
3674        match &self.client_state {
3675            ProjectClientState::Shared { .. } => true,
3676            ProjectClientState::Local => false,
3677            ProjectClientState::Remote { in_room, .. } => *in_room,
3678        }
3679    }
3680
3681    // Returns the resolved version of `path`, that was found in `buffer`, if it exists.
3682    pub fn resolve_existing_file_path(
3683        &self,
3684        path: &str,
3685        buffer: &Model<Buffer>,
3686        cx: &mut ModelContext<Self>,
3687    ) -> Task<Option<ResolvedPath>> {
3688        // TODO: ssh based remoting.
3689        if self.ssh_session.is_some() {
3690            return Task::ready(None);
3691        }
3692
3693        if self.is_local_or_ssh() {
3694            let expanded = PathBuf::from(shellexpand::tilde(&path).into_owned());
3695
3696            if expanded.is_absolute() {
3697                let fs = self.fs.clone();
3698                cx.background_executor().spawn(async move {
3699                    let path = expanded.as_path();
3700                    let exists = fs.is_file(path).await;
3701
3702                    exists.then(|| ResolvedPath::AbsPath(expanded))
3703                })
3704            } else {
3705                self.resolve_path_in_worktrees(expanded, buffer, cx)
3706            }
3707        } else {
3708            let path = PathBuf::from(path);
3709            if path.is_absolute() || path.starts_with("~") {
3710                return Task::ready(None);
3711            }
3712
3713            self.resolve_path_in_worktrees(path, buffer, cx)
3714        }
3715    }
3716
3717    fn resolve_path_in_worktrees(
3718        &self,
3719        path: PathBuf,
3720        buffer: &Model<Buffer>,
3721        cx: &mut ModelContext<Self>,
3722    ) -> Task<Option<ResolvedPath>> {
3723        let mut candidates = vec![path.clone()];
3724
3725        if let Some(file) = buffer.read(cx).file() {
3726            if let Some(dir) = file.path().parent() {
3727                let joined = dir.to_path_buf().join(path);
3728                candidates.push(joined);
3729            }
3730        }
3731
3732        let worktrees = self.worktrees(cx).collect::<Vec<_>>();
3733        cx.spawn(|_, mut cx| async move {
3734            for worktree in worktrees {
3735                for candidate in candidates.iter() {
3736                    let path = worktree
3737                        .update(&mut cx, |worktree, _| {
3738                            let root_entry_path = &worktree.root_entry()?.path;
3739
3740                            let resolved = resolve_path(&root_entry_path, candidate);
3741
3742                            let stripped =
3743                                resolved.strip_prefix(&root_entry_path).unwrap_or(&resolved);
3744
3745                            worktree.entry_for_path(stripped).map(|entry| {
3746                                ResolvedPath::ProjectPath(ProjectPath {
3747                                    worktree_id: worktree.id(),
3748                                    path: entry.path.clone(),
3749                                })
3750                            })
3751                        })
3752                        .ok()?;
3753
3754                    if path.is_some() {
3755                        return path;
3756                    }
3757                }
3758            }
3759            None
3760        })
3761    }
3762
3763    pub fn list_directory(
3764        &self,
3765        query: String,
3766        cx: &mut ModelContext<Self>,
3767    ) -> Task<Result<Vec<PathBuf>>> {
3768        if self.is_local_or_ssh() {
3769            DirectoryLister::Local(self.fs.clone()).list_directory(query, cx)
3770        } else if let Some(dev_server) = self.dev_server_project_id().and_then(|id| {
3771            dev_server_projects::Store::global(cx)
3772                .read(cx)
3773                .dev_server_for_project(id)
3774        }) {
3775            let request = proto::ListRemoteDirectory {
3776                dev_server_id: dev_server.id.0,
3777                path: query,
3778            };
3779            let response = self.client.request(request);
3780            cx.background_executor().spawn(async move {
3781                let response = response.await?;
3782                Ok(response.entries.into_iter().map(PathBuf::from).collect())
3783            })
3784        } else {
3785            Task::ready(Err(anyhow!("cannot list directory in remote project")))
3786        }
3787    }
3788
3789    fn create_worktree(
3790        &mut self,
3791        abs_path: impl AsRef<Path>,
3792        visible: bool,
3793        cx: &mut ModelContext<Self>,
3794    ) -> Task<Result<Model<Worktree>>> {
3795        self.worktree_store.update(cx, |worktree_store, cx| {
3796            worktree_store.create_worktree(abs_path, visible, cx)
3797        })
3798    }
3799
3800    pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
3801        self.worktree_store.update(cx, |worktree_store, cx| {
3802            worktree_store.remove_worktree(id_to_remove, cx);
3803        });
3804    }
3805
3806    fn add_worktree(&mut self, worktree: &Model<Worktree>, cx: &mut ModelContext<Self>) {
3807        self.worktree_store.update(cx, |worktree_store, cx| {
3808            worktree_store.add(worktree, cx);
3809        });
3810    }
3811
3812    fn update_local_worktree_settings(
3813        &mut self,
3814        worktree: &Model<Worktree>,
3815        changes: &UpdatedEntriesSet,
3816        cx: &mut ModelContext<Self>,
3817    ) {
3818        if worktree.read(cx).is_remote() {
3819            return;
3820        }
3821        let project_id = self.remote_id();
3822        let worktree_id = worktree.entity_id();
3823        let remote_worktree_id = worktree.read(cx).id();
3824
3825        let mut settings_contents = Vec::new();
3826        for (path, _, change) in changes.iter() {
3827            let removed = change == &PathChange::Removed;
3828            let abs_path = match worktree.read(cx).absolutize(path) {
3829                Ok(abs_path) => abs_path,
3830                Err(e) => {
3831                    log::warn!("Cannot absolutize {path:?} received as {change:?} FS change: {e}");
3832                    continue;
3833                }
3834            };
3835
3836            if path.ends_with(local_settings_file_relative_path()) {
3837                let settings_dir = Arc::from(
3838                    path.ancestors()
3839                        .nth(local_settings_file_relative_path().components().count())
3840                        .unwrap(),
3841                );
3842                let fs = self.fs.clone();
3843                settings_contents.push(async move {
3844                    (
3845                        settings_dir,
3846                        if removed {
3847                            None
3848                        } else {
3849                            Some(async move { fs.load(&abs_path).await }.await)
3850                        },
3851                    )
3852                });
3853            } else if path.ends_with(local_tasks_file_relative_path()) {
3854                self.task_inventory().update(cx, |task_inventory, cx| {
3855                    if removed {
3856                        task_inventory.remove_local_static_source(&abs_path);
3857                    } else {
3858                        let fs = self.fs.clone();
3859                        let task_abs_path = abs_path.clone();
3860                        let tasks_file_rx =
3861                            watch_config_file(&cx.background_executor(), fs, task_abs_path);
3862                        task_inventory.add_source(
3863                            TaskSourceKind::Worktree {
3864                                id: remote_worktree_id,
3865                                abs_path,
3866                                id_base: "local_tasks_for_worktree".into(),
3867                            },
3868                            |tx, cx| StaticSource::new(TrackedFile::new(tasks_file_rx, tx, cx)),
3869                            cx,
3870                        );
3871                    }
3872                })
3873            } else if path.ends_with(local_vscode_tasks_file_relative_path()) {
3874                self.task_inventory().update(cx, |task_inventory, cx| {
3875                    if removed {
3876                        task_inventory.remove_local_static_source(&abs_path);
3877                    } else {
3878                        let fs = self.fs.clone();
3879                        let task_abs_path = abs_path.clone();
3880                        let tasks_file_rx =
3881                            watch_config_file(&cx.background_executor(), fs, task_abs_path);
3882                        task_inventory.add_source(
3883                            TaskSourceKind::Worktree {
3884                                id: remote_worktree_id,
3885                                abs_path,
3886                                id_base: "local_vscode_tasks_for_worktree".into(),
3887                            },
3888                            |tx, cx| {
3889                                StaticSource::new(TrackedFile::new_convertible::<
3890                                    task::VsCodeTaskFile,
3891                                >(
3892                                    tasks_file_rx, tx, cx
3893                                ))
3894                            },
3895                            cx,
3896                        );
3897                    }
3898                })
3899            }
3900        }
3901
3902        if settings_contents.is_empty() {
3903            return;
3904        }
3905
3906        let client = self.client.clone();
3907        cx.spawn(move |_, cx| async move {
3908            let settings_contents: Vec<(Arc<Path>, _)> =
3909                futures::future::join_all(settings_contents).await;
3910            cx.update(|cx| {
3911                cx.update_global::<SettingsStore, _>(|store, cx| {
3912                    for (directory, file_content) in settings_contents {
3913                        let file_content = file_content.and_then(|content| content.log_err());
3914                        store
3915                            .set_local_settings(
3916                                worktree_id.as_u64() as usize,
3917                                directory.clone(),
3918                                file_content.as_deref(),
3919                                cx,
3920                            )
3921                            .log_err();
3922                        if let Some(remote_id) = project_id {
3923                            client
3924                                .send(proto::UpdateWorktreeSettings {
3925                                    project_id: remote_id,
3926                                    worktree_id: remote_worktree_id.to_proto(),
3927                                    path: directory.to_string_lossy().into_owned(),
3928                                    content: file_content,
3929                                })
3930                                .log_err();
3931                        }
3932                    }
3933                });
3934            })
3935            .ok();
3936        })
3937        .detach();
3938    }
3939
3940    pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
3941        let new_active_entry = entry.and_then(|project_path| {
3942            let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
3943            let entry = worktree.read(cx).entry_for_path(project_path.path)?;
3944            Some(entry.id)
3945        });
3946        if new_active_entry != self.active_entry {
3947            self.active_entry = new_active_entry;
3948            self.lsp_store.update(cx, |lsp_store, _| {
3949                lsp_store.set_active_entry(new_active_entry);
3950            });
3951            cx.emit(Event::ActiveEntryChanged(new_active_entry));
3952        }
3953    }
3954
3955    pub fn language_servers_running_disk_based_diagnostics<'a>(
3956        &'a self,
3957        cx: &'a AppContext,
3958    ) -> impl Iterator<Item = LanguageServerId> + 'a {
3959        self.lsp_store
3960            .read(cx)
3961            .language_servers_running_disk_based_diagnostics()
3962    }
3963
3964    pub fn diagnostic_summary(&self, include_ignored: bool, cx: &AppContext) -> DiagnosticSummary {
3965        let mut summary = DiagnosticSummary::default();
3966        for (_, _, path_summary) in self.diagnostic_summaries(include_ignored, cx) {
3967            summary.error_count += path_summary.error_count;
3968            summary.warning_count += path_summary.warning_count;
3969        }
3970        summary
3971    }
3972
3973    pub fn diagnostic_summaries<'a>(
3974        &'a self,
3975        include_ignored: bool,
3976        cx: &'a AppContext,
3977    ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
3978        self.lsp_store
3979            .read(cx)
3980            .diagnostic_summaries(include_ignored, cx)
3981    }
3982
3983    pub fn active_entry(&self) -> Option<ProjectEntryId> {
3984        self.active_entry
3985    }
3986
3987    pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Entry> {
3988        self.worktree_store.read(cx).entry_for_path(path, cx)
3989    }
3990
3991    pub fn path_for_entry(&self, entry_id: ProjectEntryId, cx: &AppContext) -> Option<ProjectPath> {
3992        let worktree = self.worktree_for_entry(entry_id, cx)?;
3993        let worktree = worktree.read(cx);
3994        let worktree_id = worktree.id();
3995        let path = worktree.entry_for_id(entry_id)?.path.clone();
3996        Some(ProjectPath { worktree_id, path })
3997    }
3998
3999    pub fn absolute_path(&self, project_path: &ProjectPath, cx: &AppContext) -> Option<PathBuf> {
4000        let workspace_root = self
4001            .worktree_for_id(project_path.worktree_id, cx)?
4002            .read(cx)
4003            .abs_path();
4004        let project_path = project_path.path.as_ref();
4005
4006        Some(if project_path == Path::new("") {
4007            workspace_root.to_path_buf()
4008        } else {
4009            workspace_root.join(project_path)
4010        })
4011    }
4012
4013    /// Attempts to find a `ProjectPath` corresponding to the given path. If the path
4014    /// is a *full path*, meaning it starts with the root name of a worktree, we'll locate
4015    /// it in that worktree. Otherwise, we'll attempt to find it as a relative path in
4016    /// the first visible worktree that has an entry for that relative path.
4017    ///
4018    /// We use this to resolve edit steps, when there's a chance an LLM may omit the workree
4019    /// root name from paths.
4020    ///
4021    /// # Arguments
4022    ///
4023    /// * `path` - A full path that starts with a worktree root name, or alternatively a
4024    ///            relative path within a visible worktree.
4025    /// * `cx` - A reference to the `AppContext`.
4026    ///
4027    /// # Returns
4028    ///
4029    /// Returns `Some(ProjectPath)` if a matching worktree is found, otherwise `None`.
4030    pub fn find_project_path(&self, path: &Path, cx: &AppContext) -> Option<ProjectPath> {
4031        let worktree_store = self.worktree_store.read(cx);
4032
4033        for worktree in worktree_store.visible_worktrees(cx) {
4034            let worktree_root_name = worktree.read(cx).root_name();
4035            if let Ok(relative_path) = path.strip_prefix(worktree_root_name) {
4036                return Some(ProjectPath {
4037                    worktree_id: worktree.read(cx).id(),
4038                    path: relative_path.into(),
4039                });
4040            }
4041        }
4042
4043        for worktree in worktree_store.visible_worktrees(cx) {
4044            let worktree = worktree.read(cx);
4045            if let Some(entry) = worktree.entry_for_path(path) {
4046                return Some(ProjectPath {
4047                    worktree_id: worktree.id(),
4048                    path: entry.path.clone(),
4049                });
4050            }
4051        }
4052
4053        None
4054    }
4055
4056    pub fn get_workspace_root(
4057        &self,
4058        project_path: &ProjectPath,
4059        cx: &AppContext,
4060    ) -> Option<PathBuf> {
4061        Some(
4062            self.worktree_for_id(project_path.worktree_id, cx)?
4063                .read(cx)
4064                .abs_path()
4065                .to_path_buf(),
4066        )
4067    }
4068
4069    pub fn get_repo(
4070        &self,
4071        project_path: &ProjectPath,
4072        cx: &AppContext,
4073    ) -> Option<Arc<dyn GitRepository>> {
4074        self.worktree_for_id(project_path.worktree_id, cx)?
4075            .read(cx)
4076            .as_local()?
4077            .local_git_repo(&project_path.path)
4078    }
4079
4080    pub fn get_first_worktree_root_repo(&self, cx: &AppContext) -> Option<Arc<dyn GitRepository>> {
4081        let worktree = self.visible_worktrees(cx).next()?.read(cx).as_local()?;
4082        let root_entry = worktree.root_git_entry()?;
4083        worktree.get_local_repo(&root_entry)?.repo().clone().into()
4084    }
4085
4086    pub fn blame_buffer(
4087        &self,
4088        buffer: &Model<Buffer>,
4089        version: Option<clock::Global>,
4090        cx: &AppContext,
4091    ) -> Task<Result<Blame>> {
4092        self.buffer_store.read(cx).blame_buffer(buffer, version, cx)
4093    }
4094
4095    // RPC message handlers
4096
4097    async fn handle_unshare_project(
4098        this: Model<Self>,
4099        _: TypedEnvelope<proto::UnshareProject>,
4100        mut cx: AsyncAppContext,
4101    ) -> Result<()> {
4102        this.update(&mut cx, |this, cx| {
4103            if this.is_local_or_ssh() {
4104                this.unshare(cx)?;
4105            } else {
4106                this.disconnected_from_host(cx);
4107            }
4108            Ok(())
4109        })?
4110    }
4111
4112    async fn handle_add_collaborator(
4113        this: Model<Self>,
4114        mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
4115        mut cx: AsyncAppContext,
4116    ) -> Result<()> {
4117        let collaborator = envelope
4118            .payload
4119            .collaborator
4120            .take()
4121            .ok_or_else(|| anyhow!("empty collaborator"))?;
4122
4123        let collaborator = Collaborator::from_proto(collaborator)?;
4124        this.update(&mut cx, |this, cx| {
4125            this.buffer_store.update(cx, |buffer_store, _| {
4126                buffer_store.forget_shared_buffers_for(&collaborator.peer_id);
4127            });
4128            cx.emit(Event::CollaboratorJoined(collaborator.peer_id));
4129            this.collaborators
4130                .insert(collaborator.peer_id, collaborator);
4131            cx.notify();
4132        })?;
4133
4134        Ok(())
4135    }
4136
4137    async fn handle_update_project_collaborator(
4138        this: Model<Self>,
4139        envelope: TypedEnvelope<proto::UpdateProjectCollaborator>,
4140        mut cx: AsyncAppContext,
4141    ) -> Result<()> {
4142        let old_peer_id = envelope
4143            .payload
4144            .old_peer_id
4145            .ok_or_else(|| anyhow!("missing old peer id"))?;
4146        let new_peer_id = envelope
4147            .payload
4148            .new_peer_id
4149            .ok_or_else(|| anyhow!("missing new peer id"))?;
4150        this.update(&mut cx, |this, cx| {
4151            let collaborator = this
4152                .collaborators
4153                .remove(&old_peer_id)
4154                .ok_or_else(|| anyhow!("received UpdateProjectCollaborator for unknown peer"))?;
4155            let is_host = collaborator.replica_id == 0;
4156            this.collaborators.insert(new_peer_id, collaborator);
4157
4158            log::info!("peer {} became {}", old_peer_id, new_peer_id,);
4159            this.buffer_store.update(cx, |buffer_store, _| {
4160                buffer_store.update_peer_id(&old_peer_id, new_peer_id)
4161            });
4162
4163            if is_host {
4164                this.buffer_store
4165                    .update(cx, |buffer_store, _| buffer_store.discard_incomplete());
4166                this.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
4167                    .unwrap();
4168                cx.emit(Event::HostReshared);
4169            }
4170
4171            cx.emit(Event::CollaboratorUpdated {
4172                old_peer_id,
4173                new_peer_id,
4174            });
4175            cx.notify();
4176            Ok(())
4177        })?
4178    }
4179
4180    async fn handle_remove_collaborator(
4181        this: Model<Self>,
4182        envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
4183        mut cx: AsyncAppContext,
4184    ) -> Result<()> {
4185        this.update(&mut cx, |this, cx| {
4186            let peer_id = envelope
4187                .payload
4188                .peer_id
4189                .ok_or_else(|| anyhow!("invalid peer id"))?;
4190            let replica_id = this
4191                .collaborators
4192                .remove(&peer_id)
4193                .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
4194                .replica_id;
4195            this.buffer_store.update(cx, |buffer_store, cx| {
4196                buffer_store.forget_shared_buffers_for(&peer_id);
4197                for buffer in buffer_store.buffers() {
4198                    buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
4199                }
4200            });
4201
4202            cx.emit(Event::CollaboratorLeft(peer_id));
4203            cx.notify();
4204            Ok(())
4205        })?
4206    }
4207
4208    async fn handle_update_project(
4209        this: Model<Self>,
4210        envelope: TypedEnvelope<proto::UpdateProject>,
4211        mut cx: AsyncAppContext,
4212    ) -> Result<()> {
4213        this.update(&mut cx, |this, cx| {
4214            // Don't handle messages that were sent before the response to us joining the project
4215            if envelope.message_id > this.join_project_response_message_id {
4216                this.set_worktrees_from_proto(envelope.payload.worktrees, cx)?;
4217            }
4218            Ok(())
4219        })?
4220    }
4221
4222    async fn handle_update_worktree(
4223        this: Model<Self>,
4224        envelope: TypedEnvelope<proto::UpdateWorktree>,
4225        mut cx: AsyncAppContext,
4226    ) -> Result<()> {
4227        this.update(&mut cx, |this, cx| {
4228            let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
4229            if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
4230                worktree.update(cx, |worktree, _| {
4231                    let worktree = worktree.as_remote_mut().unwrap();
4232                    worktree.update_from_remote(envelope.payload);
4233                });
4234            }
4235            Ok(())
4236        })?
4237    }
4238
4239    async fn handle_update_worktree_settings(
4240        this: Model<Self>,
4241        envelope: TypedEnvelope<proto::UpdateWorktreeSettings>,
4242        mut cx: AsyncAppContext,
4243    ) -> Result<()> {
4244        this.update(&mut cx, |this, cx| {
4245            let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
4246            if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
4247                cx.update_global::<SettingsStore, _>(|store, cx| {
4248                    store
4249                        .set_local_settings(
4250                            worktree.entity_id().as_u64() as usize,
4251                            PathBuf::from(&envelope.payload.path).into(),
4252                            envelope.payload.content.as_deref(),
4253                            cx,
4254                        )
4255                        .log_err();
4256                });
4257            }
4258            Ok(())
4259        })?
4260    }
4261
4262    async fn handle_update_buffer(
4263        this: Model<Self>,
4264        envelope: TypedEnvelope<proto::UpdateBuffer>,
4265        cx: AsyncAppContext,
4266    ) -> Result<proto::Ack> {
4267        let buffer_store = this.read_with(&cx, |this, cx| {
4268            if let Some(ssh) = &this.ssh_session {
4269                let mut payload = envelope.payload.clone();
4270                payload.project_id = 0;
4271                cx.background_executor()
4272                    .spawn(ssh.request(payload))
4273                    .detach_and_log_err(cx);
4274            }
4275            this.buffer_store.clone()
4276        })?;
4277        BufferStore::handle_update_buffer(buffer_store, envelope, cx).await
4278    }
4279
4280    fn retain_remotely_created_buffers(
4281        &mut self,
4282        cx: &mut ModelContext<Self>,
4283    ) -> RemotelyCreatedBufferGuard {
4284        {
4285            let mut remotely_created_buffers = self.remotely_created_buffers.lock();
4286            if remotely_created_buffers.retain_count == 0 {
4287                remotely_created_buffers.buffers = self.buffer_store.read(cx).buffers().collect();
4288            }
4289            remotely_created_buffers.retain_count += 1;
4290        }
4291        RemotelyCreatedBufferGuard {
4292            remote_buffers: Arc::downgrade(&self.remotely_created_buffers),
4293        }
4294    }
4295
4296    async fn handle_create_buffer_for_peer(
4297        this: Model<Self>,
4298        envelope: TypedEnvelope<proto::CreateBufferForPeer>,
4299        mut cx: AsyncAppContext,
4300    ) -> Result<()> {
4301        this.update(&mut cx, |this, cx| {
4302            this.buffer_store.update(cx, |buffer_store, cx| {
4303                buffer_store.handle_create_buffer_for_peer(
4304                    envelope,
4305                    this.replica_id(),
4306                    this.capability(),
4307                    cx,
4308                )
4309            })
4310        })?
4311    }
4312
4313    async fn handle_reload_buffers(
4314        this: Model<Self>,
4315        envelope: TypedEnvelope<proto::ReloadBuffers>,
4316        mut cx: AsyncAppContext,
4317    ) -> Result<proto::ReloadBuffersResponse> {
4318        let sender_id = envelope.original_sender_id()?;
4319        let reload = this.update(&mut cx, |this, cx| {
4320            let mut buffers = HashSet::default();
4321            for buffer_id in &envelope.payload.buffer_ids {
4322                let buffer_id = BufferId::new(*buffer_id)?;
4323                buffers.insert(this.buffer_store.read(cx).get_existing(buffer_id)?);
4324            }
4325            Ok::<_, anyhow::Error>(this.reload_buffers(buffers, false, cx))
4326        })??;
4327
4328        let project_transaction = reload.await?;
4329        let project_transaction = this.update(&mut cx, |this, cx| {
4330            this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
4331        })?;
4332        Ok(proto::ReloadBuffersResponse {
4333            transaction: Some(project_transaction),
4334        })
4335    }
4336
4337    async fn handle_synchronize_buffers(
4338        this: Model<Self>,
4339        envelope: TypedEnvelope<proto::SynchronizeBuffers>,
4340        mut cx: AsyncAppContext,
4341    ) -> Result<proto::SynchronizeBuffersResponse> {
4342        let response = this.update(&mut cx, |this, cx| {
4343            let client = this.client.clone();
4344            this.buffer_store.update(cx, |this, cx| {
4345                this.handle_synchronize_buffers(envelope, cx, client)
4346            })
4347        })??;
4348
4349        Ok(response)
4350    }
4351
4352    async fn handle_format_buffers(
4353        this: Model<Self>,
4354        envelope: TypedEnvelope<proto::FormatBuffers>,
4355        mut cx: AsyncAppContext,
4356    ) -> Result<proto::FormatBuffersResponse> {
4357        let sender_id = envelope.original_sender_id()?;
4358        let format = this.update(&mut cx, |this, cx| {
4359            let mut buffers = HashSet::default();
4360            for buffer_id in &envelope.payload.buffer_ids {
4361                let buffer_id = BufferId::new(*buffer_id)?;
4362                buffers.insert(this.buffer_store.read(cx).get_existing(buffer_id)?);
4363            }
4364            let trigger = FormatTrigger::from_proto(envelope.payload.trigger);
4365            Ok::<_, anyhow::Error>(this.format(buffers, false, trigger, cx))
4366        })??;
4367
4368        let project_transaction = format.await?;
4369        let project_transaction = this.update(&mut cx, |this, cx| {
4370            this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
4371        })?;
4372        Ok(proto::FormatBuffersResponse {
4373            transaction: Some(project_transaction),
4374        })
4375    }
4376
4377    async fn handle_task_context_for_location(
4378        project: Model<Self>,
4379        envelope: TypedEnvelope<proto::TaskContextForLocation>,
4380        mut cx: AsyncAppContext,
4381    ) -> Result<proto::TaskContext> {
4382        let location = envelope
4383            .payload
4384            .location
4385            .context("no location given for task context handling")?;
4386        let location = cx
4387            .update(|cx| deserialize_location(&project, location, cx))?
4388            .await?;
4389        let context_task = project.update(&mut cx, |project, cx| {
4390            let captured_variables = {
4391                let mut variables = TaskVariables::default();
4392                for range in location
4393                    .buffer
4394                    .read(cx)
4395                    .snapshot()
4396                    .runnable_ranges(location.range.clone())
4397                {
4398                    for (capture_name, value) in range.extra_captures {
4399                        variables.insert(VariableName::Custom(capture_name.into()), value);
4400                    }
4401                }
4402                variables
4403            };
4404            project.task_context_for_location(captured_variables, location, cx)
4405        })?;
4406        let task_context = context_task.await.unwrap_or_default();
4407        Ok(proto::TaskContext {
4408            project_env: task_context.project_env.into_iter().collect(),
4409            cwd: task_context
4410                .cwd
4411                .map(|cwd| cwd.to_string_lossy().to_string()),
4412            task_variables: task_context
4413                .task_variables
4414                .into_iter()
4415                .map(|(variable_name, variable_value)| (variable_name.to_string(), variable_value))
4416                .collect(),
4417        })
4418    }
4419
4420    async fn handle_task_templates(
4421        project: Model<Self>,
4422        envelope: TypedEnvelope<proto::TaskTemplates>,
4423        mut cx: AsyncAppContext,
4424    ) -> Result<proto::TaskTemplatesResponse> {
4425        let worktree = envelope.payload.worktree_id.map(WorktreeId::from_proto);
4426        let location = match envelope.payload.location {
4427            Some(location) => Some(
4428                cx.update(|cx| deserialize_location(&project, location, cx))?
4429                    .await
4430                    .context("task templates request location deserializing")?,
4431            ),
4432            None => None,
4433        };
4434
4435        let templates = project
4436            .update(&mut cx, |project, cx| {
4437                project.task_templates(worktree, location, cx)
4438            })?
4439            .await
4440            .context("receiving task templates")?
4441            .into_iter()
4442            .map(|(kind, template)| {
4443                let kind = Some(match kind {
4444                    TaskSourceKind::UserInput => proto::task_source_kind::Kind::UserInput(
4445                        proto::task_source_kind::UserInput {},
4446                    ),
4447                    TaskSourceKind::Worktree {
4448                        id,
4449                        abs_path,
4450                        id_base,
4451                    } => {
4452                        proto::task_source_kind::Kind::Worktree(proto::task_source_kind::Worktree {
4453                            id: id.to_proto(),
4454                            abs_path: abs_path.to_string_lossy().to_string(),
4455                            id_base: id_base.to_string(),
4456                        })
4457                    }
4458                    TaskSourceKind::AbsPath { id_base, abs_path } => {
4459                        proto::task_source_kind::Kind::AbsPath(proto::task_source_kind::AbsPath {
4460                            abs_path: abs_path.to_string_lossy().to_string(),
4461                            id_base: id_base.to_string(),
4462                        })
4463                    }
4464                    TaskSourceKind::Language { name } => {
4465                        proto::task_source_kind::Kind::Language(proto::task_source_kind::Language {
4466                            name: name.to_string(),
4467                        })
4468                    }
4469                });
4470                let kind = Some(proto::TaskSourceKind { kind });
4471                let template = Some(proto::TaskTemplate {
4472                    label: template.label,
4473                    command: template.command,
4474                    args: template.args,
4475                    env: template.env.into_iter().collect(),
4476                    cwd: template.cwd,
4477                    use_new_terminal: template.use_new_terminal,
4478                    allow_concurrent_runs: template.allow_concurrent_runs,
4479                    reveal: match template.reveal {
4480                        RevealStrategy::Always => proto::RevealStrategy::RevealAlways as i32,
4481                        RevealStrategy::Never => proto::RevealStrategy::RevealNever as i32,
4482                    },
4483                    hide: match template.hide {
4484                        HideStrategy::Always => proto::HideStrategy::HideAlways as i32,
4485                        HideStrategy::Never => proto::HideStrategy::HideNever as i32,
4486                        HideStrategy::OnSuccess => proto::HideStrategy::HideOnSuccess as i32,
4487                    },
4488                    shell: Some(proto::Shell {
4489                        shell_type: Some(match template.shell {
4490                            Shell::System => proto::shell::ShellType::System(proto::System {}),
4491                            Shell::Program(program) => proto::shell::ShellType::Program(program),
4492                            Shell::WithArguments { program, args } => {
4493                                proto::shell::ShellType::WithArguments(
4494                                    proto::shell::WithArguments { program, args },
4495                                )
4496                            }
4497                        }),
4498                    }),
4499                    tags: template.tags,
4500                });
4501                proto::TemplatePair { kind, template }
4502            })
4503            .collect();
4504
4505        Ok(proto::TaskTemplatesResponse { templates })
4506    }
4507
4508    async fn handle_search_project(
4509        this: Model<Self>,
4510        envelope: TypedEnvelope<proto::SearchProject>,
4511        mut cx: AsyncAppContext,
4512    ) -> Result<proto::SearchProjectResponse> {
4513        let peer_id = envelope.original_sender_id()?;
4514        let query = SearchQuery::from_proto_v1(envelope.payload)?;
4515        let mut result = this.update(&mut cx, |this, cx| this.search(query, cx))?;
4516
4517        cx.spawn(move |mut cx| async move {
4518            let mut locations = Vec::new();
4519            let mut limit_reached = false;
4520            while let Some(result) = result.next().await {
4521                match result {
4522                    SearchResult::Buffer { buffer, ranges } => {
4523                        for range in ranges {
4524                            let start = serialize_anchor(&range.start);
4525                            let end = serialize_anchor(&range.end);
4526                            let buffer_id = this.update(&mut cx, |this, cx| {
4527                                this.create_buffer_for_peer(&buffer, peer_id, cx).into()
4528                            })?;
4529                            locations.push(proto::Location {
4530                                buffer_id,
4531                                start: Some(start),
4532                                end: Some(end),
4533                            });
4534                        }
4535                    }
4536                    SearchResult::LimitReached => limit_reached = true,
4537                }
4538            }
4539            Ok(proto::SearchProjectResponse {
4540                locations,
4541                limit_reached,
4542                // will restart
4543            })
4544        })
4545        .await
4546    }
4547
4548    async fn handle_search_candidate_buffers(
4549        this: Model<Self>,
4550        envelope: TypedEnvelope<proto::FindSearchCandidates>,
4551        mut cx: AsyncAppContext,
4552    ) -> Result<proto::FindSearchCandidatesResponse> {
4553        let peer_id = envelope.original_sender_id()?;
4554        let message = envelope.payload;
4555        let query = SearchQuery::from_proto(
4556            message
4557                .query
4558                .ok_or_else(|| anyhow!("missing query field"))?,
4559        )?;
4560        let mut results = this.update(&mut cx, |this, cx| {
4561            this.search_for_candidate_buffers(&query, message.limit as _, cx)
4562        })?;
4563
4564        let mut response = proto::FindSearchCandidatesResponse {
4565            buffer_ids: Vec::new(),
4566        };
4567
4568        while let Some(buffer) = results.next().await {
4569            this.update(&mut cx, |this, cx| {
4570                let buffer_id = this.create_buffer_for_peer(&buffer, peer_id, cx);
4571                response.buffer_ids.push(buffer_id.to_proto());
4572            })?;
4573        }
4574
4575        Ok(response)
4576    }
4577
4578    async fn handle_open_buffer_by_id(
4579        this: Model<Self>,
4580        envelope: TypedEnvelope<proto::OpenBufferById>,
4581        mut cx: AsyncAppContext,
4582    ) -> Result<proto::OpenBufferResponse> {
4583        let peer_id = envelope.original_sender_id()?;
4584        let buffer_id = BufferId::new(envelope.payload.id)?;
4585        let buffer = this
4586            .update(&mut cx, |this, cx| this.open_buffer_by_id(buffer_id, cx))?
4587            .await?;
4588        Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
4589    }
4590
4591    async fn handle_open_buffer_by_path(
4592        this: Model<Self>,
4593        envelope: TypedEnvelope<proto::OpenBufferByPath>,
4594        mut cx: AsyncAppContext,
4595    ) -> Result<proto::OpenBufferResponse> {
4596        let peer_id = envelope.original_sender_id()?;
4597        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
4598        let open_buffer = this.update(&mut cx, |this, cx| {
4599            this.open_buffer(
4600                ProjectPath {
4601                    worktree_id,
4602                    path: PathBuf::from(envelope.payload.path).into(),
4603                },
4604                cx,
4605            )
4606        })?;
4607
4608        let buffer = open_buffer.await?;
4609        Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
4610    }
4611
4612    async fn handle_open_new_buffer(
4613        this: Model<Self>,
4614        envelope: TypedEnvelope<proto::OpenNewBuffer>,
4615        mut cx: AsyncAppContext,
4616    ) -> Result<proto::OpenBufferResponse> {
4617        let buffer = this.update(&mut cx, |this, cx| this.create_local_buffer("", None, cx))?;
4618        let peer_id = envelope.original_sender_id()?;
4619
4620        Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
4621    }
4622
4623    fn respond_to_open_buffer_request(
4624        this: Model<Self>,
4625        buffer: Model<Buffer>,
4626        peer_id: proto::PeerId,
4627        cx: &mut AsyncAppContext,
4628    ) -> Result<proto::OpenBufferResponse> {
4629        this.update(cx, |this, cx| {
4630            let is_private = buffer
4631                .read(cx)
4632                .file()
4633                .map(|f| f.is_private())
4634                .unwrap_or_default();
4635            if is_private {
4636                Err(anyhow!(ErrorCode::UnsharedItem))
4637            } else {
4638                Ok(proto::OpenBufferResponse {
4639                    buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx).into(),
4640                })
4641            }
4642        })?
4643    }
4644
4645    fn serialize_project_transaction_for_peer(
4646        &mut self,
4647        project_transaction: ProjectTransaction,
4648        peer_id: proto::PeerId,
4649        cx: &mut AppContext,
4650    ) -> proto::ProjectTransaction {
4651        self.buffer_store.update(cx, |buffer_store, cx| {
4652            buffer_store.serialize_project_transaction_for_peer(project_transaction, peer_id, cx)
4653        })
4654    }
4655
4656    fn create_buffer_for_peer(
4657        &mut self,
4658        buffer: &Model<Buffer>,
4659        peer_id: proto::PeerId,
4660        cx: &mut AppContext,
4661    ) -> BufferId {
4662        self.buffer_store
4663            .update(cx, |buffer_store, cx| {
4664                buffer_store.create_buffer_for_peer(buffer, peer_id, cx)
4665            })
4666            .detach_and_log_err(cx);
4667        buffer.read(cx).remote_id()
4668    }
4669
4670    fn wait_for_remote_buffer(
4671        &mut self,
4672        id: BufferId,
4673        cx: &mut ModelContext<Self>,
4674    ) -> Task<Result<Model<Buffer>>> {
4675        self.buffer_store.update(cx, |buffer_store, cx| {
4676            buffer_store.wait_for_remote_buffer(id, cx)
4677        })
4678    }
4679
4680    fn synchronize_remote_buffers(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
4681        let project_id = match self.client_state {
4682            ProjectClientState::Remote {
4683                sharing_has_stopped,
4684                remote_id,
4685                ..
4686            } => {
4687                if sharing_has_stopped {
4688                    return Task::ready(Err(anyhow!(
4689                        "can't synchronize remote buffers on a readonly project"
4690                    )));
4691                } else {
4692                    remote_id
4693                }
4694            }
4695            ProjectClientState::Shared { .. } | ProjectClientState::Local => {
4696                return Task::ready(Err(anyhow!(
4697                    "can't synchronize remote buffers on a local project"
4698                )))
4699            }
4700        };
4701
4702        let client = self.client.clone();
4703        cx.spawn(move |this, mut cx| async move {
4704            let (buffers, incomplete_buffer_ids) = this.update(&mut cx, |this, cx| {
4705                this.buffer_store.read(cx).buffer_version_info(cx)
4706            })?;
4707            let response = client
4708                .request(proto::SynchronizeBuffers {
4709                    project_id,
4710                    buffers,
4711                })
4712                .await?;
4713
4714            let send_updates_for_buffers = this.update(&mut cx, |this, cx| {
4715                response
4716                    .buffers
4717                    .into_iter()
4718                    .map(|buffer| {
4719                        let client = client.clone();
4720                        let buffer_id = match BufferId::new(buffer.id) {
4721                            Ok(id) => id,
4722                            Err(e) => {
4723                                return Task::ready(Err(e));
4724                            }
4725                        };
4726                        let remote_version = language::proto::deserialize_version(&buffer.version);
4727                        if let Some(buffer) = this.buffer_for_id(buffer_id, cx) {
4728                            let operations =
4729                                buffer.read(cx).serialize_ops(Some(remote_version), cx);
4730                            cx.background_executor().spawn(async move {
4731                                let operations = operations.await;
4732                                for chunk in split_operations(operations) {
4733                                    client
4734                                        .request(proto::UpdateBuffer {
4735                                            project_id,
4736                                            buffer_id: buffer_id.into(),
4737                                            operations: chunk,
4738                                        })
4739                                        .await?;
4740                                }
4741                                anyhow::Ok(())
4742                            })
4743                        } else {
4744                            Task::ready(Ok(()))
4745                        }
4746                    })
4747                    .collect::<Vec<_>>()
4748            })?;
4749
4750            // Any incomplete buffers have open requests waiting. Request that the host sends
4751            // creates these buffers for us again to unblock any waiting futures.
4752            for id in incomplete_buffer_ids {
4753                cx.background_executor()
4754                    .spawn(client.request(proto::OpenBufferById {
4755                        project_id,
4756                        id: id.into(),
4757                    }))
4758                    .detach();
4759            }
4760
4761            futures::future::join_all(send_updates_for_buffers)
4762                .await
4763                .into_iter()
4764                .collect()
4765        })
4766    }
4767
4768    pub fn worktree_metadata_protos(&self, cx: &AppContext) -> Vec<proto::WorktreeMetadata> {
4769        self.worktrees(cx)
4770            .map(|worktree| {
4771                let worktree = worktree.read(cx);
4772                proto::WorktreeMetadata {
4773                    id: worktree.id().to_proto(),
4774                    root_name: worktree.root_name().into(),
4775                    visible: worktree.is_visible(),
4776                    abs_path: worktree.abs_path().to_string_lossy().into(),
4777                }
4778            })
4779            .collect()
4780    }
4781
4782    fn set_worktrees_from_proto(
4783        &mut self,
4784        worktrees: Vec<proto::WorktreeMetadata>,
4785        cx: &mut ModelContext<Project>,
4786    ) -> Result<()> {
4787        self.metadata_changed(cx);
4788        self.worktree_store.update(cx, |worktree_store, cx| {
4789            worktree_store.set_worktrees_from_proto(
4790                worktrees,
4791                self.replica_id(),
4792                self.remote_id().ok_or_else(|| anyhow!("invalid project"))?,
4793                self.client.clone().into(),
4794                cx,
4795            )
4796        })
4797    }
4798
4799    fn set_collaborators_from_proto(
4800        &mut self,
4801        messages: Vec<proto::Collaborator>,
4802        cx: &mut ModelContext<Self>,
4803    ) -> Result<()> {
4804        let mut collaborators = HashMap::default();
4805        for message in messages {
4806            let collaborator = Collaborator::from_proto(message)?;
4807            collaborators.insert(collaborator.peer_id, collaborator);
4808        }
4809        for old_peer_id in self.collaborators.keys() {
4810            if !collaborators.contains_key(old_peer_id) {
4811                cx.emit(Event::CollaboratorLeft(*old_peer_id));
4812            }
4813        }
4814        self.collaborators = collaborators;
4815        Ok(())
4816    }
4817
4818    pub fn language_servers<'a>(
4819        &'a self,
4820        cx: &'a AppContext,
4821    ) -> impl 'a + Iterator<Item = (LanguageServerId, LanguageServerName, WorktreeId)> {
4822        self.lsp_store.read(cx).language_servers()
4823    }
4824
4825    pub fn supplementary_language_servers<'a>(
4826        &'a self,
4827        cx: &'a AppContext,
4828    ) -> impl '_ + Iterator<Item = (&'a LanguageServerId, &'a LanguageServerName)> {
4829        self.lsp_store.read(cx).supplementary_language_servers()
4830    }
4831
4832    pub fn language_server_adapter_for_id(
4833        &self,
4834        id: LanguageServerId,
4835        cx: &AppContext,
4836    ) -> Option<Arc<CachedLspAdapter>> {
4837        self.lsp_store.read(cx).language_server_adapter_for_id(id)
4838    }
4839
4840    pub fn language_server_for_id(
4841        &self,
4842        id: LanguageServerId,
4843        cx: &AppContext,
4844    ) -> Option<Arc<LanguageServer>> {
4845        self.lsp_store.read(cx).language_server_for_id(id)
4846    }
4847
4848    pub fn language_servers_for_buffer<'a>(
4849        &'a self,
4850        buffer: &'a Buffer,
4851        cx: &'a AppContext,
4852    ) -> impl Iterator<Item = (&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
4853        self.lsp_store
4854            .read(cx)
4855            .language_servers_for_buffer(buffer, cx)
4856    }
4857
4858    pub fn language_server_for_buffer<'a>(
4859        &'a self,
4860        buffer: &'a Buffer,
4861        server_id: LanguageServerId,
4862        cx: &'a AppContext,
4863    ) -> Option<(&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
4864        self.lsp_store
4865            .read(cx)
4866            .language_server_for_buffer(buffer, server_id, cx)
4867    }
4868
4869    pub fn task_context_for_location(
4870        &self,
4871        captured_variables: TaskVariables,
4872        location: Location,
4873        cx: &mut ModelContext<'_, Project>,
4874    ) -> Task<Option<TaskContext>> {
4875        if self.is_local_or_ssh() {
4876            let (worktree_id, worktree_abs_path) = if let Some(worktree) = self.task_worktree(cx) {
4877                (
4878                    Some(worktree.read(cx).id()),
4879                    Some(worktree.read(cx).abs_path()),
4880                )
4881            } else {
4882                (None, None)
4883            };
4884
4885            cx.spawn(|project, mut cx| async move {
4886                let mut task_variables = cx
4887                    .update(|cx| {
4888                        combine_task_variables(
4889                            captured_variables,
4890                            location,
4891                            BasicContextProvider::new(project.upgrade()?),
4892                            cx,
4893                        )
4894                        .log_err()
4895                    })
4896                    .ok()
4897                    .flatten()?;
4898                // Remove all custom entries starting with _, as they're not intended for use by the end user.
4899                task_variables.sweep();
4900
4901                let project_env = project
4902                    .update(&mut cx, |project, cx| {
4903                        let worktree_abs_path = worktree_abs_path.clone();
4904                        project.environment.update(cx, |environment, cx| {
4905                            environment.get_environment(worktree_id, worktree_abs_path, cx)
4906                        })
4907                    })
4908                    .ok()?
4909                    .await;
4910
4911                Some(TaskContext {
4912                    project_env: project_env.unwrap_or_default(),
4913                    cwd: worktree_abs_path.map(|p| p.to_path_buf()),
4914                    task_variables,
4915                })
4916            })
4917        } else if let Some(project_id) = self
4918            .remote_id()
4919            .filter(|_| self.ssh_connection_string(cx).is_some())
4920        {
4921            let task_context = self.client().request(proto::TaskContextForLocation {
4922                project_id,
4923                location: Some(proto::Location {
4924                    buffer_id: location.buffer.read(cx).remote_id().into(),
4925                    start: Some(serialize_anchor(&location.range.start)),
4926                    end: Some(serialize_anchor(&location.range.end)),
4927                }),
4928            });
4929            cx.background_executor().spawn(async move {
4930                let task_context = task_context.await.log_err()?;
4931                Some(TaskContext {
4932                    project_env: task_context.project_env.into_iter().collect(),
4933                    cwd: task_context.cwd.map(PathBuf::from),
4934                    task_variables: task_context
4935                        .task_variables
4936                        .into_iter()
4937                        .filter_map(
4938                            |(variable_name, variable_value)| match variable_name.parse() {
4939                                Ok(variable_name) => Some((variable_name, variable_value)),
4940                                Err(()) => {
4941                                    log::error!("Unknown variable name: {variable_name}");
4942                                    None
4943                                }
4944                            },
4945                        )
4946                        .collect(),
4947                })
4948            })
4949        } else {
4950            Task::ready(None)
4951        }
4952    }
4953
4954    pub fn task_templates(
4955        &self,
4956        worktree: Option<WorktreeId>,
4957        location: Option<Location>,
4958        cx: &mut ModelContext<Self>,
4959    ) -> Task<Result<Vec<(TaskSourceKind, TaskTemplate)>>> {
4960        if self.is_local_or_ssh() {
4961            let (file, language) = location
4962                .map(|location| {
4963                    let buffer = location.buffer.read(cx);
4964                    (
4965                        buffer.file().cloned(),
4966                        buffer.language_at(location.range.start),
4967                    )
4968                })
4969                .unwrap_or_default();
4970            Task::ready(Ok(self
4971                .task_inventory()
4972                .read(cx)
4973                .list_tasks(file, language, worktree, cx)))
4974        } else if let Some(project_id) = self
4975            .remote_id()
4976            .filter(|_| self.ssh_connection_string(cx).is_some())
4977        {
4978            let remote_templates =
4979                self.query_remote_task_templates(project_id, worktree, location.as_ref(), cx);
4980            cx.background_executor().spawn(remote_templates)
4981        } else {
4982            Task::ready(Ok(Vec::new()))
4983        }
4984    }
4985
4986    pub fn query_remote_task_templates(
4987        &self,
4988        project_id: u64,
4989        worktree: Option<WorktreeId>,
4990        location: Option<&Location>,
4991        cx: &AppContext,
4992    ) -> Task<Result<Vec<(TaskSourceKind, TaskTemplate)>>> {
4993        let client = self.client();
4994        let location = location.map(|location| serialize_location(location, cx));
4995        cx.spawn(|_| async move {
4996            let response = client
4997                .request(proto::TaskTemplates {
4998                    project_id,
4999                    worktree_id: worktree.map(|id| id.to_proto()),
5000                    location,
5001                })
5002                .await?;
5003
5004            Ok(response
5005                .templates
5006                .into_iter()
5007                .filter_map(|template_pair| {
5008                    let task_source_kind = match template_pair.kind?.kind? {
5009                        proto::task_source_kind::Kind::UserInput(_) => TaskSourceKind::UserInput,
5010                        proto::task_source_kind::Kind::Worktree(worktree) => {
5011                            TaskSourceKind::Worktree {
5012                                id: WorktreeId::from_proto(worktree.id),
5013                                abs_path: PathBuf::from(worktree.abs_path),
5014                                id_base: Cow::Owned(worktree.id_base),
5015                            }
5016                        }
5017                        proto::task_source_kind::Kind::AbsPath(abs_path) => {
5018                            TaskSourceKind::AbsPath {
5019                                id_base: Cow::Owned(abs_path.id_base),
5020                                abs_path: PathBuf::from(abs_path.abs_path),
5021                            }
5022                        }
5023                        proto::task_source_kind::Kind::Language(language) => {
5024                            TaskSourceKind::Language {
5025                                name: language.name.into(),
5026                            }
5027                        }
5028                    };
5029
5030                    let proto_template = template_pair.template?;
5031                    let reveal = match proto::RevealStrategy::from_i32(proto_template.reveal)
5032                        .unwrap_or(proto::RevealStrategy::RevealAlways)
5033                    {
5034                        proto::RevealStrategy::RevealAlways => RevealStrategy::Always,
5035                        proto::RevealStrategy::RevealNever => RevealStrategy::Never,
5036                    };
5037                    let hide = match proto::HideStrategy::from_i32(proto_template.hide)
5038                        .unwrap_or(proto::HideStrategy::HideNever)
5039                    {
5040                        proto::HideStrategy::HideAlways => HideStrategy::Always,
5041                        proto::HideStrategy::HideNever => HideStrategy::Never,
5042                        proto::HideStrategy::HideOnSuccess => HideStrategy::OnSuccess,
5043                    };
5044                    let shell = match proto_template
5045                        .shell
5046                        .and_then(|shell| shell.shell_type)
5047                        .unwrap_or(proto::shell::ShellType::System(proto::System {}))
5048                    {
5049                        proto::shell::ShellType::System(_) => Shell::System,
5050                        proto::shell::ShellType::Program(program) => Shell::Program(program),
5051                        proto::shell::ShellType::WithArguments(with_arguments) => {
5052                            Shell::WithArguments {
5053                                program: with_arguments.program,
5054                                args: with_arguments.args,
5055                            }
5056                        }
5057                    };
5058                    let task_template = TaskTemplate {
5059                        label: proto_template.label,
5060                        command: proto_template.command,
5061                        args: proto_template.args,
5062                        env: proto_template.env.into_iter().collect(),
5063                        cwd: proto_template.cwd,
5064                        use_new_terminal: proto_template.use_new_terminal,
5065                        allow_concurrent_runs: proto_template.allow_concurrent_runs,
5066                        reveal,
5067                        hide,
5068                        shell,
5069                        tags: proto_template.tags,
5070                    };
5071                    Some((task_source_kind, task_template))
5072                })
5073                .collect())
5074        })
5075    }
5076
5077    fn task_worktree(&self, cx: &AppContext) -> Option<Model<Worktree>> {
5078        let available_worktrees = self
5079            .worktrees(cx)
5080            .filter(|worktree| {
5081                let worktree = worktree.read(cx);
5082                worktree.is_visible()
5083                    && worktree.is_local()
5084                    && worktree.root_entry().map_or(false, |e| e.is_dir())
5085            })
5086            .collect::<Vec<_>>();
5087
5088        match available_worktrees.len() {
5089            0 => None,
5090            1 => Some(available_worktrees[0].clone()),
5091            _ => self.active_entry().and_then(|entry_id| {
5092                available_worktrees.into_iter().find_map(|worktree| {
5093                    if worktree.read(cx).contains_entry(entry_id) {
5094                        Some(worktree)
5095                    } else {
5096                        None
5097                    }
5098                })
5099            }),
5100        }
5101    }
5102}
5103
5104fn combine_task_variables(
5105    mut captured_variables: TaskVariables,
5106    location: Location,
5107    baseline: BasicContextProvider,
5108    cx: &mut AppContext,
5109) -> anyhow::Result<TaskVariables> {
5110    let language_context_provider = location
5111        .buffer
5112        .read(cx)
5113        .language()
5114        .and_then(|language| language.context_provider());
5115    let baseline = baseline
5116        .build_context(&captured_variables, &location, cx)
5117        .context("building basic default context")?;
5118    captured_variables.extend(baseline);
5119    if let Some(provider) = language_context_provider {
5120        captured_variables.extend(
5121            provider
5122                .build_context(&captured_variables, &location, cx)
5123                .context("building provider context")?,
5124        );
5125    }
5126    Ok(captured_variables)
5127}
5128
5129fn deserialize_code_actions(code_actions: &HashMap<String, bool>) -> Vec<lsp::CodeActionKind> {
5130    code_actions
5131        .iter()
5132        .flat_map(|(kind, enabled)| {
5133            if *enabled {
5134                Some(kind.clone().into())
5135            } else {
5136                None
5137            }
5138        })
5139        .collect()
5140}
5141
5142pub struct PathMatchCandidateSet {
5143    pub snapshot: Snapshot,
5144    pub include_ignored: bool,
5145    pub include_root_name: bool,
5146    pub candidates: Candidates,
5147}
5148
5149pub enum Candidates {
5150    /// Only consider directories.
5151    Directories,
5152    /// Only consider files.
5153    Files,
5154    /// Consider directories and files.
5155    Entries,
5156}
5157
5158impl<'a> fuzzy::PathMatchCandidateSet<'a> for PathMatchCandidateSet {
5159    type Candidates = PathMatchCandidateSetIter<'a>;
5160
5161    fn id(&self) -> usize {
5162        self.snapshot.id().to_usize()
5163    }
5164
5165    fn len(&self) -> usize {
5166        match self.candidates {
5167            Candidates::Files => {
5168                if self.include_ignored {
5169                    self.snapshot.file_count()
5170                } else {
5171                    self.snapshot.visible_file_count()
5172                }
5173            }
5174
5175            Candidates::Directories => {
5176                if self.include_ignored {
5177                    self.snapshot.dir_count()
5178                } else {
5179                    self.snapshot.visible_dir_count()
5180                }
5181            }
5182
5183            Candidates::Entries => {
5184                if self.include_ignored {
5185                    self.snapshot.entry_count()
5186                } else {
5187                    self.snapshot.visible_entry_count()
5188                }
5189            }
5190        }
5191    }
5192
5193    fn prefix(&self) -> Arc<str> {
5194        if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
5195            self.snapshot.root_name().into()
5196        } else if self.include_root_name {
5197            format!("{}/", self.snapshot.root_name()).into()
5198        } else {
5199            Arc::default()
5200        }
5201    }
5202
5203    fn candidates(&'a self, start: usize) -> Self::Candidates {
5204        PathMatchCandidateSetIter {
5205            traversal: match self.candidates {
5206                Candidates::Directories => self.snapshot.directories(self.include_ignored, start),
5207                Candidates::Files => self.snapshot.files(self.include_ignored, start),
5208                Candidates::Entries => self.snapshot.entries(self.include_ignored, start),
5209            },
5210        }
5211    }
5212}
5213
5214pub struct PathMatchCandidateSetIter<'a> {
5215    traversal: Traversal<'a>,
5216}
5217
5218impl<'a> Iterator for PathMatchCandidateSetIter<'a> {
5219    type Item = fuzzy::PathMatchCandidate<'a>;
5220
5221    fn next(&mut self) -> Option<Self::Item> {
5222        self.traversal
5223            .next()
5224            .map(|entry| fuzzy::PathMatchCandidate {
5225                is_dir: entry.kind.is_dir(),
5226                path: &entry.path,
5227                char_bag: entry.char_bag,
5228            })
5229    }
5230}
5231
5232impl EventEmitter<Event> for Project {}
5233
5234impl<'a> Into<SettingsLocation<'a>> for &'a ProjectPath {
5235    fn into(self) -> SettingsLocation<'a> {
5236        SettingsLocation {
5237            worktree_id: self.worktree_id.to_usize(),
5238            path: self.path.as_ref(),
5239        }
5240    }
5241}
5242
5243impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
5244    fn from((worktree_id, path): (WorktreeId, P)) -> Self {
5245        Self {
5246            worktree_id,
5247            path: path.as_ref().into(),
5248        }
5249    }
5250}
5251
5252pub fn relativize_path(base: &Path, path: &Path) -> PathBuf {
5253    let mut path_components = path.components();
5254    let mut base_components = base.components();
5255    let mut components: Vec<Component> = Vec::new();
5256    loop {
5257        match (path_components.next(), base_components.next()) {
5258            (None, None) => break,
5259            (Some(a), None) => {
5260                components.push(a);
5261                components.extend(path_components.by_ref());
5262                break;
5263            }
5264            (None, _) => components.push(Component::ParentDir),
5265            (Some(a), Some(b)) if components.is_empty() && a == b => (),
5266            (Some(a), Some(Component::CurDir)) => components.push(a),
5267            (Some(a), Some(_)) => {
5268                components.push(Component::ParentDir);
5269                for _ in base_components {
5270                    components.push(Component::ParentDir);
5271                }
5272                components.push(a);
5273                components.extend(path_components.by_ref());
5274                break;
5275            }
5276        }
5277    }
5278    components.iter().map(|c| c.as_os_str()).collect()
5279}
5280
5281fn resolve_path(base: &Path, path: &Path) -> PathBuf {
5282    let mut result = base.to_path_buf();
5283    for component in path.components() {
5284        match component {
5285            Component::ParentDir => {
5286                result.pop();
5287            }
5288            Component::CurDir => (),
5289            _ => result.push(component),
5290        }
5291    }
5292    result
5293}
5294
5295/// ResolvedPath is a path that has been resolved to either a ProjectPath
5296/// or an AbsPath and that *exists*.
5297#[derive(Debug, Clone)]
5298pub enum ResolvedPath {
5299    ProjectPath(ProjectPath),
5300    AbsPath(PathBuf),
5301}
5302
5303impl Item for Buffer {
5304    fn try_open(
5305        project: &Model<Project>,
5306        path: &ProjectPath,
5307        cx: &mut AppContext,
5308    ) -> Option<Task<Result<Model<Self>>>> {
5309        Some(project.update(cx, |project, cx| project.open_buffer(path.clone(), cx)))
5310    }
5311
5312    fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
5313        File::from_dyn(self.file()).and_then(|file| file.project_entry_id(cx))
5314    }
5315
5316    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
5317        File::from_dyn(self.file()).map(|file| ProjectPath {
5318            worktree_id: file.worktree_id(cx),
5319            path: file.path().clone(),
5320        })
5321    }
5322}
5323
5324impl Completion {
5325    /// A key that can be used to sort completions when displaying
5326    /// them to the user.
5327    pub fn sort_key(&self) -> (usize, &str) {
5328        let kind_key = match self.lsp_completion.kind {
5329            Some(lsp::CompletionItemKind::KEYWORD) => 0,
5330            Some(lsp::CompletionItemKind::VARIABLE) => 1,
5331            _ => 2,
5332        };
5333        (kind_key, &self.label.text[self.label.filter_range.clone()])
5334    }
5335
5336    /// Whether this completion is a snippet.
5337    pub fn is_snippet(&self) -> bool {
5338        self.lsp_completion.insert_text_format == Some(lsp::InsertTextFormat::SNIPPET)
5339    }
5340}
5341
5342#[derive(Debug)]
5343pub struct NoRepositoryError {}
5344
5345impl std::fmt::Display for NoRepositoryError {
5346    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5347        write!(f, "no git repository for worktree found")
5348    }
5349}
5350
5351impl std::error::Error for NoRepositoryError {}
5352
5353fn serialize_location(location: &Location, cx: &AppContext) -> proto::Location {
5354    proto::Location {
5355        buffer_id: location.buffer.read(cx).remote_id().into(),
5356        start: Some(serialize_anchor(&location.range.start)),
5357        end: Some(serialize_anchor(&location.range.end)),
5358    }
5359}
5360
5361fn deserialize_location(
5362    project: &Model<Project>,
5363    location: proto::Location,
5364    cx: &mut AppContext,
5365) -> Task<Result<Location>> {
5366    let buffer_id = match BufferId::new(location.buffer_id) {
5367        Ok(id) => id,
5368        Err(e) => return Task::ready(Err(e)),
5369    };
5370    let buffer_task = project.update(cx, |project, cx| {
5371        project.wait_for_remote_buffer(buffer_id, cx)
5372    });
5373    cx.spawn(|_| async move {
5374        let buffer = buffer_task.await?;
5375        let start = location
5376            .start
5377            .and_then(deserialize_anchor)
5378            .context("missing task context location start")?;
5379        let end = location
5380            .end
5381            .and_then(deserialize_anchor)
5382            .context("missing task context location end")?;
5383        Ok(Location {
5384            buffer,
5385            range: start..end,
5386        })
5387    })
5388}
5389
5390pub fn sort_worktree_entries(entries: &mut Vec<Entry>) {
5391    entries.sort_by(|entry_a, entry_b| {
5392        compare_paths(
5393            (&entry_a.path, entry_a.is_file()),
5394            (&entry_b.path, entry_b.is_file()),
5395        )
5396    });
5397}