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