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