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