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