project.rs

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