project.rs

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