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