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