project.rs

   1pub mod agent_registry_store;
   2pub mod agent_server_store;
   3pub mod buffer_store;
   4pub mod color_extractor;
   5pub mod connection_manager;
   6pub mod context_server_store;
   7pub mod debounced_delay;
   8pub mod debugger;
   9pub mod git_store;
  10pub mod image_store;
  11pub mod lsp_command;
  12pub mod lsp_store;
  13pub mod manifest_tree;
  14pub mod prettier_store;
  15pub mod project_search;
  16pub mod project_settings;
  17pub mod search;
  18pub mod task_inventory;
  19pub mod task_store;
  20pub mod telemetry_snapshot;
  21pub mod terminals;
  22pub mod toolchain_store;
  23pub mod trusted_worktrees;
  24pub mod worktree_store;
  25
  26mod environment;
  27use buffer_diff::BufferDiff;
  28use context_server_store::ContextServerStore;
  29pub use environment::ProjectEnvironmentEvent;
  30use git::repository::get_git_committer;
  31use git_store::{Repository, RepositoryId};
  32pub mod search_history;
  33pub mod yarn;
  34
  35use dap::inline_value::{InlineValueLocation, VariableLookupKind, VariableScope};
  36use itertools::{Either, Itertools};
  37
  38use crate::{
  39    git_store::GitStore,
  40    lsp_store::{SymbolLocation, log_store::LogKind},
  41    project_search::SearchResultsHandle,
  42    trusted_worktrees::{PathTrust, RemoteHostLocation, TrustedWorktrees},
  43    worktree_store::WorktreeIdCounter,
  44};
  45pub use agent_registry_store::{AgentRegistryStore, RegistryAgent};
  46pub use agent_server_store::{AgentId, AgentServerStore, AgentServersUpdated, ExternalAgentSource};
  47pub use git_store::{
  48    ConflictRegion, ConflictSet, ConflictSetSnapshot, ConflictSetUpdate,
  49    git_traversal::{ChildEntriesGitIter, GitEntry, GitEntryRef, GitTraversal},
  50    linked_worktree_short_name, worktrees_directory_for_repo,
  51};
  52pub use manifest_tree::ManifestTree;
  53pub use project_search::{Search, SearchResults};
  54
  55use anyhow::{Context as _, Result, anyhow};
  56use buffer_store::{BufferStore, BufferStoreEvent};
  57use client::{
  58    Client, Collaborator, PendingEntitySubscription, ProjectId, TypedEnvelope, UserStore, proto,
  59};
  60use clock::ReplicaId;
  61
  62use dap::client::DebugAdapterClient;
  63
  64use collections::{BTreeSet, HashMap, HashSet, IndexSet};
  65use debounced_delay::DebouncedDelay;
  66pub use debugger::breakpoint_store::BreakpointWithPosition;
  67use debugger::{
  68    breakpoint_store::{ActiveStackFrame, BreakpointStore},
  69    dap_store::{DapStore, DapStoreEvent},
  70    session::Session,
  71};
  72
  73pub use environment::ProjectEnvironment;
  74
  75use futures::{
  76    StreamExt,
  77    channel::mpsc::{self, UnboundedReceiver},
  78    future::try_join_all,
  79};
  80pub use image_store::{ImageItem, ImageStore};
  81use image_store::{ImageItemEvent, ImageStoreEvent};
  82
  83use ::git::{blame::Blame, status::FileStatus};
  84use gpui::{
  85    App, AppContext, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Hsla, SharedString,
  86    Task, WeakEntity, Window,
  87};
  88use language::{
  89    Buffer, BufferEvent, Capability, CodeLabel, CursorShape, DiskState, Language, LanguageName,
  90    LanguageRegistry, PointUtf16, ToOffset, ToPointUtf16, Toolchain, ToolchainMetadata,
  91    ToolchainScope, Transaction, Unclipped, language_settings::InlayHintKind,
  92    proto::split_operations,
  93};
  94use lsp::{
  95    CodeActionKind, CompletionContext, CompletionItemKind, DocumentHighlightKind, InsertTextMode,
  96    LanguageServerBinary, LanguageServerId, LanguageServerName, LanguageServerSelector,
  97    MessageActionItem,
  98};
  99use lsp_command::*;
 100use lsp_store::{CompletionDocumentation, LspFormatTarget, OpenLspBufferHandle};
 101pub use manifest_tree::ManifestProvidersStore;
 102use node_runtime::NodeRuntime;
 103use parking_lot::Mutex;
 104pub use prettier_store::PrettierStore;
 105use project_settings::{ProjectSettings, SettingsObserver, SettingsObserverEvent};
 106#[cfg(target_os = "windows")]
 107use remote::wsl_path_to_windows_path;
 108use remote::{RemoteClient, RemoteConnectionOptions};
 109use rpc::{
 110    AnyProtoClient, ErrorCode,
 111    proto::{LanguageServerPromptResponse, REMOTE_SERVER_PROJECT_ID},
 112};
 113use search::{SearchInputKind, SearchQuery, SearchResult};
 114use search_history::SearchHistory;
 115use settings::{InvalidSettingsError, RegisterSetting, Settings, SettingsLocation, SettingsStore};
 116use snippet::Snippet;
 117pub use snippet_provider;
 118use snippet_provider::SnippetProvider;
 119use std::{
 120    borrow::Cow,
 121    collections::BTreeMap,
 122    ffi::OsString,
 123    future::Future,
 124    ops::{Not as _, Range},
 125    path::{Path, PathBuf},
 126    pin::pin,
 127    str::{self, FromStr},
 128    sync::Arc,
 129    time::Duration,
 130};
 131
 132use task_store::TaskStore;
 133use terminals::Terminals;
 134use text::{Anchor, BufferId, OffsetRangeExt, Point, Rope};
 135use toolchain_store::EmptyToolchainStore;
 136use util::{
 137    ResultExt as _, maybe,
 138    path_list::PathList,
 139    paths::{PathStyle, SanitizedPath, is_absolute},
 140    rel_path::RelPath,
 141};
 142use worktree::{CreatedEntry, Snapshot, Traversal};
 143pub use worktree::{
 144    Entry, EntryKind, FS_WATCH_LATENCY, File, LocalWorktree, PathChange, ProjectEntryId,
 145    UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings,
 146};
 147use worktree_store::{WorktreeStore, WorktreeStoreEvent};
 148
 149pub use fs::*;
 150pub use language::Location;
 151#[cfg(any(test, feature = "test-support"))]
 152pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
 153#[cfg(any(test, feature = "test-support"))]
 154pub use prettier::RANGE_FORMAT_SUFFIX as TEST_PRETTIER_RANGE_FORMAT_SUFFIX;
 155pub use task_inventory::{
 156    BasicContextProvider, ContextProviderWithTasks, DebugScenarioContext, Inventory, TaskContexts,
 157    TaskSourceKind,
 158};
 159
 160pub use buffer_store::ProjectTransaction;
 161pub use lsp_store::{
 162    DiagnosticSummary, InvalidationStrategy, LanguageServerLogType, LanguageServerProgress,
 163    LanguageServerPromptRequest, LanguageServerStatus, LanguageServerToQuery, LspStore,
 164    LspStoreEvent, ProgressToken, SERVER_PROGRESS_THROTTLE_TIMEOUT,
 165};
 166pub use toolchain_store::{ToolchainStore, Toolchains};
 167const MAX_PROJECT_SEARCH_HISTORY_SIZE: usize = 500;
 168
 169#[derive(Clone, Copy, Debug)]
 170pub struct LocalProjectFlags {
 171    pub init_worktree_trust: bool,
 172    pub watch_global_configs: bool,
 173}
 174
 175impl Default for LocalProjectFlags {
 176    fn default() -> Self {
 177        Self {
 178            init_worktree_trust: true,
 179            watch_global_configs: true,
 180        }
 181    }
 182}
 183
 184pub trait ProjectItem: 'static {
 185    fn try_open(
 186        project: &Entity<Project>,
 187        path: &ProjectPath,
 188        cx: &mut App,
 189    ) -> Option<Task<Result<Entity<Self>>>>
 190    where
 191        Self: Sized;
 192    fn entry_id(&self, cx: &App) -> Option<ProjectEntryId>;
 193    fn project_path(&self, cx: &App) -> Option<ProjectPath>;
 194    fn is_dirty(&self) -> bool;
 195}
 196
 197#[derive(Clone)]
 198pub enum OpenedBufferEvent {
 199    Disconnected,
 200    Ok(BufferId),
 201    Err(BufferId, Arc<anyhow::Error>),
 202}
 203
 204/// Semantics-aware entity that is relevant to one or more [`Worktree`] with the files.
 205/// `Project` is responsible for tasks, LSP and collab queries, synchronizing worktree states accordingly.
 206/// Maps [`Worktree`] entries with its own logic using [`ProjectEntryId`] and [`ProjectPath`] structs.
 207///
 208/// Can be either local (for the project opened on the same host) or remote.(for collab projects, browsed by multiple remote users).
 209pub struct Project {
 210    active_entry: Option<ProjectEntryId>,
 211    buffer_ordered_messages_tx: mpsc::UnboundedSender<BufferOrderedMessage>,
 212    languages: Arc<LanguageRegistry>,
 213    dap_store: Entity<DapStore>,
 214    agent_server_store: Entity<AgentServerStore>,
 215
 216    breakpoint_store: Entity<BreakpointStore>,
 217    collab_client: Arc<client::Client>,
 218    join_project_response_message_id: u32,
 219    task_store: Entity<TaskStore>,
 220    user_store: Entity<UserStore>,
 221    fs: Arc<dyn Fs>,
 222    remote_client: Option<Entity<RemoteClient>>,
 223    // todo lw explain the client_state x remote_client matrix, its super confusing
 224    client_state: ProjectClientState,
 225    git_store: Entity<GitStore>,
 226    collaborators: HashMap<proto::PeerId, Collaborator>,
 227    client_subscriptions: Vec<client::Subscription>,
 228    worktree_store: Entity<WorktreeStore>,
 229    buffer_store: Entity<BufferStore>,
 230    context_server_store: Entity<ContextServerStore>,
 231    image_store: Entity<ImageStore>,
 232    lsp_store: Entity<LspStore>,
 233    _subscriptions: Vec<gpui::Subscription>,
 234    buffers_needing_diff: HashSet<WeakEntity<Buffer>>,
 235    git_diff_debouncer: DebouncedDelay<Self>,
 236    remotely_created_models: Arc<Mutex<RemotelyCreatedModels>>,
 237    terminals: Terminals,
 238    node: Option<NodeRuntime>,
 239    search_history: SearchHistory,
 240    search_included_history: SearchHistory,
 241    search_excluded_history: SearchHistory,
 242    snippets: Entity<SnippetProvider>,
 243    environment: Entity<ProjectEnvironment>,
 244    settings_observer: Entity<SettingsObserver>,
 245    toolchain_store: Option<Entity<ToolchainStore>>,
 246    agent_location: Option<AgentLocation>,
 247    downloading_files: Arc<Mutex<HashMap<(WorktreeId, String), DownloadingFile>>>,
 248}
 249
 250struct DownloadingFile {
 251    destination_path: PathBuf,
 252    chunks: Vec<u8>,
 253    total_size: u64,
 254    file_id: Option<u64>, // Set when we receive the State message
 255}
 256
 257#[derive(Clone, Debug, PartialEq, Eq)]
 258pub struct AgentLocation {
 259    pub buffer: WeakEntity<Buffer>,
 260    pub position: Anchor,
 261}
 262
 263#[derive(Default)]
 264struct RemotelyCreatedModels {
 265    worktrees: Vec<Entity<Worktree>>,
 266    buffers: Vec<Entity<Buffer>>,
 267    retain_count: usize,
 268}
 269
 270struct RemotelyCreatedModelGuard {
 271    remote_models: std::sync::Weak<Mutex<RemotelyCreatedModels>>,
 272}
 273
 274impl Drop for RemotelyCreatedModelGuard {
 275    fn drop(&mut self) {
 276        if let Some(remote_models) = self.remote_models.upgrade() {
 277            let mut remote_models = remote_models.lock();
 278            assert!(
 279                remote_models.retain_count > 0,
 280                "RemotelyCreatedModelGuard dropped too many times"
 281            );
 282            remote_models.retain_count -= 1;
 283            if remote_models.retain_count == 0 {
 284                remote_models.buffers.clear();
 285                remote_models.worktrees.clear();
 286            }
 287        }
 288    }
 289}
 290/// Message ordered with respect to buffer operations
 291#[derive(Debug)]
 292enum BufferOrderedMessage {
 293    Operation {
 294        buffer_id: BufferId,
 295        operation: proto::Operation,
 296    },
 297    LanguageServerUpdate {
 298        language_server_id: LanguageServerId,
 299        message: proto::update_language_server::Variant,
 300        name: Option<LanguageServerName>,
 301    },
 302    Resync,
 303}
 304
 305#[derive(Debug)]
 306enum ProjectClientState {
 307    /// Single-player mode.
 308    Local,
 309    /// Multi-player mode but still a local project.
 310    Shared { remote_id: u64 },
 311    /// Multi-player mode but working on a remote project.
 312    Collab {
 313        sharing_has_stopped: bool,
 314        capability: Capability,
 315        remote_id: u64,
 316        replica_id: ReplicaId,
 317    },
 318}
 319
 320/// A link to display in a toast notification, useful to point to documentation.
 321#[derive(PartialEq, Debug, Clone)]
 322pub struct ToastLink {
 323    pub label: &'static str,
 324    pub url: &'static str,
 325}
 326
 327#[derive(Clone, Debug, PartialEq)]
 328pub enum Event {
 329    LanguageServerAdded(LanguageServerId, LanguageServerName, Option<WorktreeId>),
 330    LanguageServerRemoved(LanguageServerId),
 331    LanguageServerLog(LanguageServerId, LanguageServerLogType, String),
 332    // [`lsp::notification::DidOpenTextDocument`] was sent to this server using the buffer data.
 333    // Zed's buffer-related data is updated accordingly.
 334    LanguageServerBufferRegistered {
 335        server_id: LanguageServerId,
 336        buffer_id: BufferId,
 337        buffer_abs_path: PathBuf,
 338        name: Option<LanguageServerName>,
 339    },
 340    ToggleLspLogs {
 341        server_id: LanguageServerId,
 342        enabled: bool,
 343        toggled_log_kind: LogKind,
 344    },
 345    Toast {
 346        notification_id: SharedString,
 347        message: String,
 348        /// Optional link to display as a button in the toast.
 349        link: Option<ToastLink>,
 350    },
 351    HideToast {
 352        notification_id: SharedString,
 353    },
 354    LanguageServerPrompt(LanguageServerPromptRequest),
 355    LanguageNotFound(Entity<Buffer>),
 356    ActiveEntryChanged(Option<ProjectEntryId>),
 357    ActivateProjectPanel,
 358    WorktreeAdded(WorktreeId),
 359    WorktreeOrderChanged,
 360    WorktreeRemoved(WorktreeId),
 361    WorktreeUpdatedEntries(WorktreeId, UpdatedEntriesSet),
 362    DiskBasedDiagnosticsStarted {
 363        language_server_id: LanguageServerId,
 364    },
 365    DiskBasedDiagnosticsFinished {
 366        language_server_id: LanguageServerId,
 367    },
 368    DiagnosticsUpdated {
 369        paths: Vec<ProjectPath>,
 370        language_server_id: LanguageServerId,
 371    },
 372    RemoteIdChanged(Option<u64>),
 373    DisconnectedFromHost,
 374    DisconnectedFromRemote {
 375        server_not_running: bool,
 376    },
 377    Closed,
 378    DeletedEntry(WorktreeId, ProjectEntryId),
 379    CollaboratorUpdated {
 380        old_peer_id: proto::PeerId,
 381        new_peer_id: proto::PeerId,
 382    },
 383    CollaboratorJoined(proto::PeerId),
 384    CollaboratorLeft(proto::PeerId),
 385    HostReshared,
 386    Reshared,
 387    Rejoined,
 388    RefreshInlayHints {
 389        server_id: LanguageServerId,
 390        request_id: Option<usize>,
 391    },
 392    RefreshSemanticTokens {
 393        server_id: LanguageServerId,
 394        request_id: Option<usize>,
 395    },
 396    RefreshCodeLens,
 397    RevealInProjectPanel(ProjectEntryId),
 398    SnippetEdit(BufferId, Vec<(lsp::Range, Snippet)>),
 399    ExpandedAllForEntry(WorktreeId, ProjectEntryId),
 400    EntryRenamed(ProjectTransaction, ProjectPath, PathBuf),
 401    WorkspaceEditApplied(ProjectTransaction),
 402    AgentLocationChanged,
 403    BufferEdited,
 404}
 405
 406pub struct AgentLocationChanged;
 407
 408pub enum DebugAdapterClientState {
 409    Starting(Task<Option<Arc<DebugAdapterClient>>>),
 410    Running(Arc<DebugAdapterClient>),
 411}
 412
 413#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
 414pub struct ProjectPath {
 415    pub worktree_id: WorktreeId,
 416    pub path: Arc<RelPath>,
 417}
 418
 419impl ProjectPath {
 420    pub fn from_file(value: &dyn language::File, cx: &App) -> Self {
 421        ProjectPath {
 422            worktree_id: value.worktree_id(cx),
 423            path: value.path().clone(),
 424        }
 425    }
 426
 427    pub fn from_proto(p: proto::ProjectPath) -> Option<Self> {
 428        Some(Self {
 429            worktree_id: WorktreeId::from_proto(p.worktree_id),
 430            path: RelPath::from_proto(&p.path).log_err()?,
 431        })
 432    }
 433
 434    pub fn to_proto(&self) -> proto::ProjectPath {
 435        proto::ProjectPath {
 436            worktree_id: self.worktree_id.to_proto(),
 437            path: self.path.as_ref().to_proto(),
 438        }
 439    }
 440
 441    pub fn root_path(worktree_id: WorktreeId) -> Self {
 442        Self {
 443            worktree_id,
 444            path: RelPath::empty().into(),
 445        }
 446    }
 447
 448    pub fn starts_with(&self, other: &ProjectPath) -> bool {
 449        self.worktree_id == other.worktree_id && self.path.starts_with(&other.path)
 450    }
 451}
 452
 453#[derive(Debug, Default)]
 454pub enum PrepareRenameResponse {
 455    Success(Range<Anchor>),
 456    OnlyUnpreparedRenameSupported,
 457    #[default]
 458    InvalidPosition,
 459}
 460
 461#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 462pub enum InlayId {
 463    EditPrediction(usize),
 464    DebuggerValue(usize),
 465    // LSP
 466    Hint(usize),
 467    Color(usize),
 468    ReplResult(usize),
 469}
 470
 471impl InlayId {
 472    pub fn id(&self) -> usize {
 473        match self {
 474            Self::EditPrediction(id) => *id,
 475            Self::DebuggerValue(id) => *id,
 476            Self::Hint(id) => *id,
 477            Self::Color(id) => *id,
 478            Self::ReplResult(id) => *id,
 479        }
 480    }
 481}
 482
 483#[derive(Debug, Clone, PartialEq, Eq)]
 484pub struct InlayHint {
 485    pub position: language::Anchor,
 486    pub label: InlayHintLabel,
 487    pub kind: Option<InlayHintKind>,
 488    pub padding_left: bool,
 489    pub padding_right: bool,
 490    pub tooltip: Option<InlayHintTooltip>,
 491    pub resolve_state: ResolveState,
 492}
 493
 494/// The user's intent behind a given completion confirmation.
 495#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
 496pub enum CompletionIntent {
 497    /// The user intends to 'commit' this result, if possible.
 498    /// Completion confirmations should run side effects.
 499    ///
 500    /// For LSP completions, will respect the setting `completions.lsp_insert_mode`.
 501    Complete,
 502    /// Similar to [Self::Complete], but behaves like `lsp_insert_mode` is set to `insert`.
 503    CompleteWithInsert,
 504    /// Similar to [Self::Complete], but behaves like `lsp_insert_mode` is set to `replace`.
 505    CompleteWithReplace,
 506    /// The user intends to continue 'composing' this completion.
 507    /// Completion confirmations should not run side effects and
 508    /// let the user continue composing their action.
 509    Compose,
 510}
 511
 512impl CompletionIntent {
 513    pub fn is_complete(&self) -> bool {
 514        self == &Self::Complete
 515    }
 516
 517    pub fn is_compose(&self) -> bool {
 518        self == &Self::Compose
 519    }
 520}
 521
 522/// Similar to `CoreCompletion`, but with extra metadata attached.
 523#[derive(Clone)]
 524pub struct Completion {
 525    /// The range of text that will be replaced by this completion.
 526    pub replace_range: Range<Anchor>,
 527    /// The new text that will be inserted.
 528    pub new_text: String,
 529    /// A label for this completion that is shown in the menu.
 530    pub label: CodeLabel,
 531    /// The documentation for this completion.
 532    pub documentation: Option<CompletionDocumentation>,
 533    /// Completion data source which it was constructed from.
 534    pub source: CompletionSource,
 535    /// A path to an icon for this completion that is shown in the menu.
 536    pub icon_path: Option<SharedString>,
 537    /// Text starting here and ending at the cursor will be used as the query for filtering this completion.
 538    ///
 539    /// If None, the start of the surrounding word is used.
 540    pub match_start: Option<text::Anchor>,
 541    /// Key used for de-duplicating snippets. If None, always considered unique.
 542    pub snippet_deduplication_key: Option<(usize, usize)>,
 543    /// Whether to adjust indentation (the default) or not.
 544    pub insert_text_mode: Option<InsertTextMode>,
 545    /// An optional callback to invoke when this completion is confirmed.
 546    /// Returns whether new completions should be retriggered after the current one.
 547    /// If `true` is returned, the editor will show a new completion menu after this completion is confirmed.
 548    /// if no confirmation is provided or `false` is returned, the completion will be committed.
 549    pub confirm: Option<Arc<dyn Send + Sync + Fn(CompletionIntent, &mut Window, &mut App) -> bool>>,
 550}
 551
 552#[derive(Debug, Clone)]
 553pub enum CompletionSource {
 554    Lsp {
 555        /// The alternate `insert` range, if provided by the LSP server.
 556        insert_range: Option<Range<Anchor>>,
 557        /// The id of the language server that produced this completion.
 558        server_id: LanguageServerId,
 559        /// The raw completion provided by the language server.
 560        lsp_completion: Box<lsp::CompletionItem>,
 561        /// A set of defaults for this completion item.
 562        lsp_defaults: Option<Arc<lsp::CompletionListItemDefaults>>,
 563        /// Whether this completion has been resolved, to ensure it happens once per completion.
 564        resolved: bool,
 565    },
 566    Dap {
 567        /// The sort text for this completion.
 568        sort_text: String,
 569    },
 570    Custom,
 571    BufferWord {
 572        word_range: Range<Anchor>,
 573        resolved: bool,
 574    },
 575}
 576
 577impl CompletionSource {
 578    pub fn server_id(&self) -> Option<LanguageServerId> {
 579        if let CompletionSource::Lsp { server_id, .. } = self {
 580            Some(*server_id)
 581        } else {
 582            None
 583        }
 584    }
 585
 586    pub fn lsp_completion(&self, apply_defaults: bool) -> Option<Cow<'_, lsp::CompletionItem>> {
 587        if let Self::Lsp {
 588            lsp_completion,
 589            lsp_defaults,
 590            ..
 591        } = self
 592        {
 593            if apply_defaults && let Some(lsp_defaults) = lsp_defaults {
 594                let mut completion_with_defaults = *lsp_completion.clone();
 595                let default_commit_characters = lsp_defaults.commit_characters.as_ref();
 596                let default_edit_range = lsp_defaults.edit_range.as_ref();
 597                let default_insert_text_format = lsp_defaults.insert_text_format.as_ref();
 598                let default_insert_text_mode = lsp_defaults.insert_text_mode.as_ref();
 599
 600                if default_commit_characters.is_some()
 601                    || default_edit_range.is_some()
 602                    || default_insert_text_format.is_some()
 603                    || default_insert_text_mode.is_some()
 604                {
 605                    if completion_with_defaults.commit_characters.is_none()
 606                        && default_commit_characters.is_some()
 607                    {
 608                        completion_with_defaults.commit_characters =
 609                            default_commit_characters.cloned()
 610                    }
 611                    if completion_with_defaults.text_edit.is_none() {
 612                        match default_edit_range {
 613                            Some(lsp::CompletionListItemDefaultsEditRange::Range(range)) => {
 614                                completion_with_defaults.text_edit =
 615                                    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 616                                        range: *range,
 617                                        new_text: completion_with_defaults.label.clone(),
 618                                    }))
 619                            }
 620                            Some(lsp::CompletionListItemDefaultsEditRange::InsertAndReplace {
 621                                insert,
 622                                replace,
 623                            }) => {
 624                                completion_with_defaults.text_edit =
 625                                    Some(lsp::CompletionTextEdit::InsertAndReplace(
 626                                        lsp::InsertReplaceEdit {
 627                                            new_text: completion_with_defaults.label.clone(),
 628                                            insert: *insert,
 629                                            replace: *replace,
 630                                        },
 631                                    ))
 632                            }
 633                            None => {}
 634                        }
 635                    }
 636                    if completion_with_defaults.insert_text_format.is_none()
 637                        && default_insert_text_format.is_some()
 638                    {
 639                        completion_with_defaults.insert_text_format =
 640                            default_insert_text_format.cloned()
 641                    }
 642                    if completion_with_defaults.insert_text_mode.is_none()
 643                        && default_insert_text_mode.is_some()
 644                    {
 645                        completion_with_defaults.insert_text_mode =
 646                            default_insert_text_mode.cloned()
 647                    }
 648                }
 649                return Some(Cow::Owned(completion_with_defaults));
 650            }
 651            Some(Cow::Borrowed(lsp_completion))
 652        } else {
 653            None
 654        }
 655    }
 656}
 657
 658impl std::fmt::Debug for Completion {
 659    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 660        f.debug_struct("Completion")
 661            .field("replace_range", &self.replace_range)
 662            .field("new_text", &self.new_text)
 663            .field("label", &self.label)
 664            .field("documentation", &self.documentation)
 665            .field("source", &self.source)
 666            .finish()
 667    }
 668}
 669
 670/// Response from a source of completions.
 671pub struct CompletionResponse {
 672    pub completions: Vec<Completion>,
 673    pub display_options: CompletionDisplayOptions,
 674    /// When false, indicates that the list is complete and does not need to be re-queried if it
 675    /// can be filtered instead.
 676    pub is_incomplete: bool,
 677}
 678
 679#[derive(Default)]
 680pub struct CompletionDisplayOptions {
 681    pub dynamic_width: bool,
 682}
 683
 684impl CompletionDisplayOptions {
 685    pub fn merge(&mut self, other: &CompletionDisplayOptions) {
 686        self.dynamic_width = self.dynamic_width && other.dynamic_width;
 687    }
 688}
 689
 690/// Response from language server completion request.
 691#[derive(Clone, Debug, Default)]
 692pub(crate) struct CoreCompletionResponse {
 693    pub completions: Vec<CoreCompletion>,
 694    /// When false, indicates that the list is complete and does not need to be re-queried if it
 695    /// can be filtered instead.
 696    pub is_incomplete: bool,
 697}
 698
 699/// A generic completion that can come from different sources.
 700#[derive(Clone, Debug)]
 701pub(crate) struct CoreCompletion {
 702    replace_range: Range<Anchor>,
 703    new_text: String,
 704    source: CompletionSource,
 705}
 706
 707/// A code action provided by a language server.
 708#[derive(Clone, Debug, PartialEq)]
 709pub struct CodeAction {
 710    /// The id of the language server that produced this code action.
 711    pub server_id: LanguageServerId,
 712    /// The range of the buffer where this code action is applicable.
 713    pub range: Range<Anchor>,
 714    /// The raw code action provided by the language server.
 715    /// Can be either an action or a command.
 716    pub lsp_action: LspAction,
 717    /// Whether the action needs to be resolved using the language server.
 718    pub resolved: bool,
 719}
 720
 721/// An action sent back by a language server.
 722#[derive(Clone, Debug, PartialEq)]
 723pub enum LspAction {
 724    /// An action with the full data, may have a command or may not.
 725    /// May require resolving.
 726    Action(Box<lsp::CodeAction>),
 727    /// A command data to run as an action.
 728    Command(lsp::Command),
 729    /// A code lens data to run as an action.
 730    CodeLens(lsp::CodeLens),
 731}
 732
 733impl LspAction {
 734    pub fn title(&self) -> &str {
 735        match self {
 736            Self::Action(action) => &action.title,
 737            Self::Command(command) => &command.title,
 738            Self::CodeLens(lens) => lens
 739                .command
 740                .as_ref()
 741                .map(|command| command.title.as_str())
 742                .unwrap_or("Unknown command"),
 743        }
 744    }
 745
 746    pub fn action_kind(&self) -> Option<lsp::CodeActionKind> {
 747        match self {
 748            Self::Action(action) => action.kind.clone(),
 749            Self::Command(_) => Some(lsp::CodeActionKind::new("command")),
 750            Self::CodeLens(_) => Some(lsp::CodeActionKind::new("code lens")),
 751        }
 752    }
 753
 754    fn edit(&self) -> Option<&lsp::WorkspaceEdit> {
 755        match self {
 756            Self::Action(action) => action.edit.as_ref(),
 757            Self::Command(_) => None,
 758            Self::CodeLens(_) => None,
 759        }
 760    }
 761
 762    fn command(&self) -> Option<&lsp::Command> {
 763        match self {
 764            Self::Action(action) => action.command.as_ref(),
 765            Self::Command(command) => Some(command),
 766            Self::CodeLens(lens) => lens.command.as_ref(),
 767        }
 768    }
 769}
 770
 771#[derive(Debug, Clone, PartialEq, Eq)]
 772pub enum ResolveState {
 773    Resolved,
 774    CanResolve(LanguageServerId, Option<lsp::LSPAny>),
 775    Resolving,
 776}
 777impl InlayHint {
 778    pub fn text(&self) -> Rope {
 779        match &self.label {
 780            InlayHintLabel::String(s) => Rope::from(s),
 781            InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &*part.value).collect(),
 782        }
 783    }
 784}
 785
 786#[derive(Debug, Clone, PartialEq, Eq)]
 787pub enum InlayHintLabel {
 788    String(String),
 789    LabelParts(Vec<InlayHintLabelPart>),
 790}
 791
 792#[derive(Debug, Clone, PartialEq, Eq)]
 793pub struct InlayHintLabelPart {
 794    pub value: String,
 795    pub tooltip: Option<InlayHintLabelPartTooltip>,
 796    pub location: Option<(LanguageServerId, lsp::Location)>,
 797}
 798
 799#[derive(Debug, Clone, PartialEq, Eq)]
 800pub enum InlayHintTooltip {
 801    String(String),
 802    MarkupContent(MarkupContent),
 803}
 804
 805#[derive(Debug, Clone, PartialEq, Eq)]
 806pub enum InlayHintLabelPartTooltip {
 807    String(String),
 808    MarkupContent(MarkupContent),
 809}
 810
 811#[derive(Debug, Clone, PartialEq, Eq)]
 812pub struct MarkupContent {
 813    pub kind: HoverBlockKind,
 814    pub value: String,
 815}
 816
 817#[derive(Debug, Clone, PartialEq)]
 818pub struct LocationLink {
 819    pub origin: Option<Location>,
 820    pub target: Location,
 821}
 822
 823#[derive(Debug)]
 824pub struct DocumentHighlight {
 825    pub range: Range<language::Anchor>,
 826    pub kind: DocumentHighlightKind,
 827}
 828
 829#[derive(Clone, Debug)]
 830pub struct Symbol {
 831    pub language_server_name: LanguageServerName,
 832    pub source_worktree_id: WorktreeId,
 833    pub source_language_server_id: LanguageServerId,
 834    pub path: SymbolLocation,
 835    pub label: CodeLabel,
 836    pub name: String,
 837    pub kind: lsp::SymbolKind,
 838    pub range: Range<Unclipped<PointUtf16>>,
 839    pub container_name: Option<String>,
 840}
 841
 842#[derive(Clone, Debug)]
 843pub struct DocumentSymbol {
 844    pub name: String,
 845    pub kind: lsp::SymbolKind,
 846    pub range: Range<Unclipped<PointUtf16>>,
 847    pub selection_range: Range<Unclipped<PointUtf16>>,
 848    pub children: Vec<DocumentSymbol>,
 849}
 850
 851#[derive(Clone, Debug, PartialEq)]
 852pub struct HoverBlock {
 853    pub text: String,
 854    pub kind: HoverBlockKind,
 855}
 856
 857#[derive(Clone, Debug, PartialEq, Eq)]
 858pub enum HoverBlockKind {
 859    PlainText,
 860    Markdown,
 861    Code { language: String },
 862}
 863
 864#[derive(Debug, Clone)]
 865pub struct Hover {
 866    pub contents: Vec<HoverBlock>,
 867    pub range: Option<Range<language::Anchor>>,
 868    pub language: Option<Arc<Language>>,
 869}
 870
 871impl Hover {
 872    pub fn is_empty(&self) -> bool {
 873        self.contents.iter().all(|block| block.text.is_empty())
 874    }
 875}
 876
 877enum EntitySubscription {
 878    Project(PendingEntitySubscription<Project>),
 879    BufferStore(PendingEntitySubscription<BufferStore>),
 880    GitStore(PendingEntitySubscription<GitStore>),
 881    WorktreeStore(PendingEntitySubscription<WorktreeStore>),
 882    LspStore(PendingEntitySubscription<LspStore>),
 883    SettingsObserver(PendingEntitySubscription<SettingsObserver>),
 884    DapStore(PendingEntitySubscription<DapStore>),
 885    BreakpointStore(PendingEntitySubscription<BreakpointStore>),
 886}
 887
 888#[derive(Debug, Clone)]
 889pub struct DirectoryItem {
 890    pub path: PathBuf,
 891    pub is_dir: bool,
 892}
 893
 894#[derive(Clone, Debug, PartialEq)]
 895pub struct DocumentColor {
 896    pub lsp_range: lsp::Range,
 897    pub color: lsp::Color,
 898    pub resolved: bool,
 899    pub color_presentations: Vec<ColorPresentation>,
 900}
 901
 902impl Eq for DocumentColor {}
 903
 904impl std::hash::Hash for DocumentColor {
 905    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 906        self.lsp_range.hash(state);
 907        self.color.red.to_bits().hash(state);
 908        self.color.green.to_bits().hash(state);
 909        self.color.blue.to_bits().hash(state);
 910        self.color.alpha.to_bits().hash(state);
 911        self.resolved.hash(state);
 912        self.color_presentations.hash(state);
 913    }
 914}
 915
 916#[derive(Clone, Debug, PartialEq, Eq)]
 917pub struct ColorPresentation {
 918    pub label: SharedString,
 919    pub text_edit: Option<lsp::TextEdit>,
 920    pub additional_text_edits: Vec<lsp::TextEdit>,
 921}
 922
 923impl std::hash::Hash for ColorPresentation {
 924    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 925        self.label.hash(state);
 926        if let Some(ref edit) = self.text_edit {
 927            edit.range.hash(state);
 928            edit.new_text.hash(state);
 929        }
 930        self.additional_text_edits.len().hash(state);
 931        for edit in &self.additional_text_edits {
 932            edit.range.hash(state);
 933            edit.new_text.hash(state);
 934        }
 935    }
 936}
 937
 938#[derive(Clone)]
 939pub enum DirectoryLister {
 940    Project(Entity<Project>),
 941    Local(Entity<Project>, Arc<dyn Fs>),
 942}
 943
 944impl std::fmt::Debug for DirectoryLister {
 945    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 946        match self {
 947            DirectoryLister::Project(project) => {
 948                write!(f, "DirectoryLister::Project({project:?})")
 949            }
 950            DirectoryLister::Local(project, _) => {
 951                write!(f, "DirectoryLister::Local({project:?})")
 952            }
 953        }
 954    }
 955}
 956
 957impl DirectoryLister {
 958    pub fn is_local(&self, cx: &App) -> bool {
 959        match self {
 960            DirectoryLister::Local(..) => true,
 961            DirectoryLister::Project(project) => project.read(cx).is_local(),
 962        }
 963    }
 964
 965    pub fn resolve_tilde<'a>(&self, path: &'a String, cx: &App) -> Cow<'a, str> {
 966        if self.is_local(cx) {
 967            shellexpand::tilde(path)
 968        } else {
 969            Cow::from(path)
 970        }
 971    }
 972
 973    pub fn default_query(&self, cx: &mut App) -> String {
 974        let project = match self {
 975            DirectoryLister::Project(project) => project,
 976            DirectoryLister::Local(project, _) => project,
 977        }
 978        .read(cx);
 979        let path_style = project.path_style(cx);
 980        project
 981            .visible_worktrees(cx)
 982            .next()
 983            .map(|worktree| worktree.read(cx).abs_path().to_string_lossy().into_owned())
 984            .or_else(|| std::env::home_dir().map(|dir| dir.to_string_lossy().into_owned()))
 985            .map(|mut s| {
 986                s.push_str(path_style.primary_separator());
 987                s
 988            })
 989            .unwrap_or_else(|| {
 990                if path_style.is_windows() {
 991                    "C:\\"
 992                } else {
 993                    "~/"
 994                }
 995                .to_string()
 996            })
 997    }
 998
 999    pub fn list_directory(&self, path: String, cx: &mut App) -> Task<Result<Vec<DirectoryItem>>> {
1000        match self {
1001            DirectoryLister::Project(project) => {
1002                project.update(cx, |project, cx| project.list_directory(path, cx))
1003            }
1004            DirectoryLister::Local(_, fs) => {
1005                let fs = fs.clone();
1006                cx.background_spawn(async move {
1007                    let mut results = vec![];
1008                    let expanded = shellexpand::tilde(&path);
1009                    let query = Path::new(expanded.as_ref());
1010                    let mut response = fs.read_dir(query).await?;
1011                    while let Some(path) = response.next().await {
1012                        let path = path?;
1013                        if let Some(file_name) = path.file_name() {
1014                            results.push(DirectoryItem {
1015                                path: PathBuf::from(file_name.to_os_string()),
1016                                is_dir: fs.is_dir(&path).await,
1017                            });
1018                        }
1019                    }
1020                    Ok(results)
1021                })
1022            }
1023        }
1024    }
1025
1026    pub fn path_style(&self, cx: &App) -> PathStyle {
1027        match self {
1028            Self::Local(project, ..) | Self::Project(project, ..) => {
1029                project.read(cx).path_style(cx)
1030            }
1031        }
1032    }
1033}
1034
1035pub const CURRENT_PROJECT_FEATURES: &[&str] = &["new-style-anchors"];
1036
1037#[cfg(feature = "test-support")]
1038pub const DEFAULT_COMPLETION_CONTEXT: CompletionContext = CompletionContext {
1039    trigger_kind: lsp::CompletionTriggerKind::INVOKED,
1040    trigger_character: None,
1041};
1042
1043/// An LSP diagnostics associated with a certain language server.
1044#[derive(Clone, Debug, Default)]
1045pub enum LspPullDiagnostics {
1046    #[default]
1047    Default,
1048    Response {
1049        /// The id of the language server that produced diagnostics.
1050        server_id: LanguageServerId,
1051        /// URI of the resource,
1052        uri: lsp::Uri,
1053        /// The ID provided by the dynamic registration that produced diagnostics.
1054        registration_id: Option<SharedString>,
1055        /// The diagnostics produced by this language server.
1056        diagnostics: PulledDiagnostics,
1057    },
1058}
1059
1060#[derive(Clone, Debug)]
1061pub enum PulledDiagnostics {
1062    Unchanged {
1063        /// An ID the current pulled batch for this file.
1064        /// If given, can be used to query workspace diagnostics partially.
1065        result_id: SharedString,
1066    },
1067    Changed {
1068        result_id: Option<SharedString>,
1069        diagnostics: Vec<lsp::Diagnostic>,
1070    },
1071}
1072
1073/// Whether to disable all AI features in Zed.
1074///
1075/// Default: false
1076#[derive(Copy, Clone, Debug, RegisterSetting)]
1077pub struct DisableAiSettings {
1078    pub disable_ai: bool,
1079}
1080
1081impl settings::Settings for DisableAiSettings {
1082    fn from_settings(content: &settings::SettingsContent) -> Self {
1083        Self {
1084            disable_ai: content.project.disable_ai.unwrap().0,
1085        }
1086    }
1087}
1088
1089impl DisableAiSettings {
1090    /// Returns whether AI is disabled for the given file.
1091    ///
1092    /// This checks the project-level settings for the file's worktree,
1093    /// allowing `disable_ai` to be configured per-project in `.zed/settings.json`.
1094    pub fn is_ai_disabled_for_buffer(buffer: Option<&Entity<Buffer>>, cx: &App) -> bool {
1095        Self::is_ai_disabled_for_file(buffer.and_then(|buffer| buffer.read(cx).file()), cx)
1096    }
1097
1098    pub fn is_ai_disabled_for_file(file: Option<&Arc<dyn language::File>>, cx: &App) -> bool {
1099        let location = file.map(|f| settings::SettingsLocation {
1100            worktree_id: f.worktree_id(cx),
1101            path: f.path().as_ref(),
1102        });
1103        Self::get(location, cx).disable_ai
1104    }
1105}
1106
1107impl Project {
1108    pub fn init(client: &Arc<Client>, cx: &mut App) {
1109        connection_manager::init(client.clone(), cx);
1110
1111        let client: AnyProtoClient = client.clone().into();
1112        client.add_entity_message_handler(Self::handle_add_collaborator);
1113        client.add_entity_message_handler(Self::handle_update_project_collaborator);
1114        client.add_entity_message_handler(Self::handle_remove_collaborator);
1115        client.add_entity_message_handler(Self::handle_update_project);
1116        client.add_entity_message_handler(Self::handle_unshare_project);
1117        client.add_entity_request_handler(Self::handle_update_buffer);
1118        client.add_entity_message_handler(Self::handle_update_worktree);
1119        client.add_entity_request_handler(Self::handle_synchronize_buffers);
1120
1121        client.add_entity_request_handler(Self::handle_search_candidate_buffers);
1122        client.add_entity_request_handler(Self::handle_open_buffer_by_id);
1123        client.add_entity_request_handler(Self::handle_open_buffer_by_path);
1124        client.add_entity_request_handler(Self::handle_open_new_buffer);
1125        client.add_entity_message_handler(Self::handle_create_buffer_for_peer);
1126        client.add_entity_message_handler(Self::handle_toggle_lsp_logs);
1127        client.add_entity_message_handler(Self::handle_create_image_for_peer);
1128        client.add_entity_request_handler(Self::handle_find_search_candidates_chunk);
1129        client.add_entity_message_handler(Self::handle_find_search_candidates_cancel);
1130        client.add_entity_message_handler(Self::handle_create_file_for_peer);
1131
1132        WorktreeStore::init(&client);
1133        BufferStore::init(&client);
1134        LspStore::init(&client);
1135        GitStore::init(&client);
1136        SettingsObserver::init(&client);
1137        TaskStore::init(Some(&client));
1138        ToolchainStore::init(&client);
1139        DapStore::init(&client, cx);
1140        BreakpointStore::init(&client);
1141        context_server_store::init(cx);
1142    }
1143
1144    pub fn local(
1145        client: Arc<Client>,
1146        node: NodeRuntime,
1147        user_store: Entity<UserStore>,
1148        languages: Arc<LanguageRegistry>,
1149        fs: Arc<dyn Fs>,
1150        env: Option<HashMap<String, String>>,
1151        flags: LocalProjectFlags,
1152        cx: &mut App,
1153    ) -> Entity<Self> {
1154        cx.new(|cx: &mut Context<Self>| {
1155            let (tx, rx) = mpsc::unbounded();
1156            cx.spawn(async move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx).await)
1157                .detach();
1158            let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx);
1159            let worktree_store =
1160                cx.new(|cx| WorktreeStore::local(false, fs.clone(), WorktreeIdCounter::get(cx)));
1161            if flags.init_worktree_trust {
1162                trusted_worktrees::track_worktree_trust(
1163                    worktree_store.clone(),
1164                    None,
1165                    None,
1166                    None,
1167                    cx,
1168                );
1169            }
1170            cx.subscribe(&worktree_store, Self::on_worktree_store_event)
1171                .detach();
1172
1173            let weak_self = cx.weak_entity();
1174            let context_server_store = cx.new(|cx| {
1175                ContextServerStore::local(
1176                    worktree_store.clone(),
1177                    Some(weak_self.clone()),
1178                    false,
1179                    cx,
1180                )
1181            });
1182
1183            let environment = cx.new(|cx| {
1184                ProjectEnvironment::new(env, worktree_store.downgrade(), None, false, cx)
1185            });
1186            let manifest_tree = ManifestTree::new(worktree_store.clone(), cx);
1187            let toolchain_store = cx.new(|cx| {
1188                ToolchainStore::local(
1189                    languages.clone(),
1190                    worktree_store.clone(),
1191                    environment.clone(),
1192                    manifest_tree.clone(),
1193                    cx,
1194                )
1195            });
1196
1197            let buffer_store = cx.new(|cx| BufferStore::local(worktree_store.clone(), cx));
1198            cx.subscribe(&buffer_store, Self::on_buffer_store_event)
1199                .detach();
1200
1201            let breakpoint_store =
1202                cx.new(|_| BreakpointStore::local(worktree_store.clone(), buffer_store.clone()));
1203
1204            let dap_store = cx.new(|cx| {
1205                DapStore::new_local(
1206                    client.http_client(),
1207                    node.clone(),
1208                    fs.clone(),
1209                    environment.clone(),
1210                    toolchain_store.read(cx).as_language_toolchain_store(),
1211                    worktree_store.clone(),
1212                    breakpoint_store.clone(),
1213                    false,
1214                    cx,
1215                )
1216            });
1217            cx.subscribe(&dap_store, Self::on_dap_store_event).detach();
1218
1219            let image_store = cx.new(|cx| ImageStore::local(worktree_store.clone(), cx));
1220            cx.subscribe(&image_store, Self::on_image_store_event)
1221                .detach();
1222
1223            let prettier_store = cx.new(|cx| {
1224                PrettierStore::new(
1225                    node.clone(),
1226                    fs.clone(),
1227                    languages.clone(),
1228                    worktree_store.clone(),
1229                    cx,
1230                )
1231            });
1232
1233            let task_store = cx.new(|cx| {
1234                TaskStore::local(
1235                    buffer_store.downgrade(),
1236                    worktree_store.clone(),
1237                    toolchain_store.read(cx).as_language_toolchain_store(),
1238                    environment.clone(),
1239                    cx,
1240                )
1241            });
1242
1243            let settings_observer = cx.new(|cx| {
1244                SettingsObserver::new_local(
1245                    fs.clone(),
1246                    worktree_store.clone(),
1247                    task_store.clone(),
1248                    flags.watch_global_configs,
1249                    cx,
1250                )
1251            });
1252            cx.subscribe(&settings_observer, Self::on_settings_observer_event)
1253                .detach();
1254
1255            let lsp_store = cx.new(|cx| {
1256                LspStore::new_local(
1257                    buffer_store.clone(),
1258                    worktree_store.clone(),
1259                    prettier_store.clone(),
1260                    toolchain_store
1261                        .read(cx)
1262                        .as_local_store()
1263                        .expect("Toolchain store to be local")
1264                        .clone(),
1265                    environment.clone(),
1266                    manifest_tree,
1267                    languages.clone(),
1268                    client.http_client(),
1269                    fs.clone(),
1270                    cx,
1271                )
1272            });
1273
1274            let git_store = cx.new(|cx| {
1275                GitStore::local(
1276                    &worktree_store,
1277                    buffer_store.clone(),
1278                    environment.clone(),
1279                    fs.clone(),
1280                    cx,
1281                )
1282            });
1283
1284            let agent_server_store = cx.new(|cx| {
1285                AgentServerStore::local(
1286                    node.clone(),
1287                    fs.clone(),
1288                    environment.clone(),
1289                    client.http_client(),
1290                    cx,
1291                )
1292            });
1293
1294            cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
1295
1296            Self {
1297                buffer_ordered_messages_tx: tx,
1298                collaborators: Default::default(),
1299                worktree_store,
1300                buffer_store,
1301                image_store,
1302                lsp_store,
1303                context_server_store,
1304                join_project_response_message_id: 0,
1305                client_state: ProjectClientState::Local,
1306                git_store,
1307                client_subscriptions: Vec::new(),
1308                _subscriptions: vec![cx.on_release(Self::release)],
1309                active_entry: None,
1310                snippets,
1311                languages,
1312                collab_client: client,
1313                task_store,
1314                user_store,
1315                settings_observer,
1316                fs,
1317                remote_client: None,
1318                breakpoint_store,
1319                dap_store,
1320                agent_server_store,
1321
1322                buffers_needing_diff: Default::default(),
1323                git_diff_debouncer: DebouncedDelay::new(),
1324                terminals: Terminals {
1325                    local_handles: Vec::new(),
1326                },
1327                node: Some(node),
1328                search_history: Self::new_search_history(),
1329                environment,
1330                remotely_created_models: Default::default(),
1331
1332                search_included_history: Self::new_search_history(),
1333                search_excluded_history: Self::new_search_history(),
1334
1335                toolchain_store: Some(toolchain_store),
1336
1337                agent_location: None,
1338                downloading_files: Default::default(),
1339            }
1340        })
1341    }
1342
1343    pub fn remote(
1344        remote: Entity<RemoteClient>,
1345        client: Arc<Client>,
1346        node: NodeRuntime,
1347        user_store: Entity<UserStore>,
1348        languages: Arc<LanguageRegistry>,
1349        fs: Arc<dyn Fs>,
1350        init_worktree_trust: bool,
1351        cx: &mut App,
1352    ) -> Entity<Self> {
1353        cx.new(|cx: &mut Context<Self>| {
1354            let (tx, rx) = mpsc::unbounded();
1355            cx.spawn(async move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx).await)
1356                .detach();
1357            let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx);
1358
1359            let (remote_proto, path_style, connection_options) =
1360                remote.read_with(cx, |remote, _| {
1361                    (
1362                        remote.proto_client(),
1363                        remote.path_style(),
1364                        remote.connection_options(),
1365                    )
1366                });
1367            let worktree_store = cx.new(|cx| {
1368                WorktreeStore::remote(
1369                    false,
1370                    remote_proto.clone(),
1371                    REMOTE_SERVER_PROJECT_ID,
1372                    path_style,
1373                    WorktreeIdCounter::get(cx),
1374                )
1375            });
1376
1377            cx.subscribe(&worktree_store, Self::on_worktree_store_event)
1378                .detach();
1379            if init_worktree_trust {
1380                trusted_worktrees::track_worktree_trust(
1381                    worktree_store.clone(),
1382                    Some(RemoteHostLocation::from(connection_options)),
1383                    None,
1384                    Some((remote_proto.clone(), ProjectId(REMOTE_SERVER_PROJECT_ID))),
1385                    cx,
1386                );
1387            }
1388
1389            let weak_self = cx.weak_entity();
1390
1391            let buffer_store = cx.new(|cx| {
1392                BufferStore::remote(
1393                    worktree_store.clone(),
1394                    remote.read(cx).proto_client(),
1395                    REMOTE_SERVER_PROJECT_ID,
1396                    cx,
1397                )
1398            });
1399            let image_store = cx.new(|cx| {
1400                ImageStore::remote(
1401                    worktree_store.clone(),
1402                    remote.read(cx).proto_client(),
1403                    REMOTE_SERVER_PROJECT_ID,
1404                    cx,
1405                )
1406            });
1407            cx.subscribe(&buffer_store, Self::on_buffer_store_event)
1408                .detach();
1409            let toolchain_store = cx.new(|cx| {
1410                ToolchainStore::remote(
1411                    REMOTE_SERVER_PROJECT_ID,
1412                    worktree_store.clone(),
1413                    remote.read(cx).proto_client(),
1414                    cx,
1415                )
1416            });
1417
1418            let task_store = cx.new(|cx| {
1419                TaskStore::remote(
1420                    buffer_store.downgrade(),
1421                    worktree_store.clone(),
1422                    toolchain_store.read(cx).as_language_toolchain_store(),
1423                    remote.read(cx).proto_client(),
1424                    REMOTE_SERVER_PROJECT_ID,
1425                    cx,
1426                )
1427            });
1428
1429            let settings_observer = cx.new(|cx| {
1430                SettingsObserver::new_remote(
1431                    fs.clone(),
1432                    worktree_store.clone(),
1433                    task_store.clone(),
1434                    Some(remote_proto.clone()),
1435                    false,
1436                    cx,
1437                )
1438            });
1439            cx.subscribe(&settings_observer, Self::on_settings_observer_event)
1440                .detach();
1441
1442            let context_server_store = cx.new(|cx| {
1443                ContextServerStore::remote(
1444                    rpc::proto::REMOTE_SERVER_PROJECT_ID,
1445                    remote.clone(),
1446                    worktree_store.clone(),
1447                    Some(weak_self.clone()),
1448                    cx,
1449                )
1450            });
1451
1452            let environment = cx.new(|cx| {
1453                ProjectEnvironment::new(
1454                    None,
1455                    worktree_store.downgrade(),
1456                    Some(remote.downgrade()),
1457                    false,
1458                    cx,
1459                )
1460            });
1461
1462            let lsp_store = cx.new(|cx| {
1463                LspStore::new_remote(
1464                    buffer_store.clone(),
1465                    worktree_store.clone(),
1466                    languages.clone(),
1467                    remote_proto.clone(),
1468                    REMOTE_SERVER_PROJECT_ID,
1469                    cx,
1470                )
1471            });
1472            cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
1473
1474            let breakpoint_store = cx.new(|_| {
1475                BreakpointStore::remote(
1476                    REMOTE_SERVER_PROJECT_ID,
1477                    remote_proto.clone(),
1478                    buffer_store.clone(),
1479                    worktree_store.clone(),
1480                )
1481            });
1482
1483            let dap_store = cx.new(|cx| {
1484                DapStore::new_remote(
1485                    REMOTE_SERVER_PROJECT_ID,
1486                    remote.clone(),
1487                    breakpoint_store.clone(),
1488                    worktree_store.clone(),
1489                    node.clone(),
1490                    client.http_client(),
1491                    fs.clone(),
1492                    cx,
1493                )
1494            });
1495
1496            let git_store = cx.new(|cx| {
1497                GitStore::remote(
1498                    &worktree_store,
1499                    buffer_store.clone(),
1500                    remote_proto.clone(),
1501                    REMOTE_SERVER_PROJECT_ID,
1502                    cx,
1503                )
1504            });
1505
1506            let agent_server_store = cx.new(|_| {
1507                AgentServerStore::remote(
1508                    REMOTE_SERVER_PROJECT_ID,
1509                    remote.clone(),
1510                    worktree_store.clone(),
1511                )
1512            });
1513
1514            cx.subscribe(&remote, Self::on_remote_client_event).detach();
1515
1516            let this = Self {
1517                buffer_ordered_messages_tx: tx,
1518                collaborators: Default::default(),
1519                worktree_store,
1520                buffer_store,
1521                image_store,
1522                lsp_store,
1523                context_server_store,
1524                breakpoint_store,
1525                dap_store,
1526                join_project_response_message_id: 0,
1527                client_state: ProjectClientState::Local,
1528                git_store,
1529                agent_server_store,
1530                client_subscriptions: Vec::new(),
1531                _subscriptions: vec![
1532                    cx.on_release(Self::release),
1533                    cx.on_app_quit(|this, cx| {
1534                        let shutdown = this.remote_client.take().and_then(|client| {
1535                            client.update(cx, |client, cx| {
1536                                client.shutdown_processes(
1537                                    Some(proto::ShutdownRemoteServer {}),
1538                                    cx.background_executor().clone(),
1539                                )
1540                            })
1541                        });
1542
1543                        cx.background_executor().spawn(async move {
1544                            if let Some(shutdown) = shutdown {
1545                                shutdown.await;
1546                            }
1547                        })
1548                    }),
1549                ],
1550                active_entry: None,
1551                snippets,
1552                languages,
1553                collab_client: client,
1554                task_store,
1555                user_store,
1556                settings_observer,
1557                fs,
1558                remote_client: Some(remote.clone()),
1559                buffers_needing_diff: Default::default(),
1560                git_diff_debouncer: DebouncedDelay::new(),
1561                terminals: Terminals {
1562                    local_handles: Vec::new(),
1563                },
1564                node: Some(node),
1565                search_history: Self::new_search_history(),
1566                environment,
1567                remotely_created_models: Default::default(),
1568
1569                search_included_history: Self::new_search_history(),
1570                search_excluded_history: Self::new_search_history(),
1571
1572                toolchain_store: Some(toolchain_store),
1573                agent_location: None,
1574                downloading_files: Default::default(),
1575            };
1576
1577            // remote server -> local machine handlers
1578            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &cx.entity());
1579            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.buffer_store);
1580            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.worktree_store);
1581            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.lsp_store);
1582            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.dap_store);
1583            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.breakpoint_store);
1584            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.settings_observer);
1585            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.git_store);
1586            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.agent_server_store);
1587
1588            remote_proto.add_entity_message_handler(Self::handle_create_buffer_for_peer);
1589            remote_proto.add_entity_message_handler(Self::handle_create_image_for_peer);
1590            remote_proto.add_entity_message_handler(Self::handle_create_file_for_peer);
1591            remote_proto.add_entity_message_handler(Self::handle_update_worktree);
1592            remote_proto.add_entity_message_handler(Self::handle_update_project);
1593            remote_proto.add_entity_message_handler(Self::handle_toast);
1594            remote_proto.add_entity_request_handler(Self::handle_language_server_prompt_request);
1595            remote_proto.add_entity_message_handler(Self::handle_hide_toast);
1596            remote_proto.add_entity_request_handler(Self::handle_update_buffer_from_remote_server);
1597            remote_proto.add_entity_request_handler(Self::handle_trust_worktrees);
1598            remote_proto.add_entity_request_handler(Self::handle_restrict_worktrees);
1599            remote_proto.add_entity_request_handler(Self::handle_find_search_candidates_chunk);
1600
1601            remote_proto.add_entity_message_handler(Self::handle_find_search_candidates_cancel);
1602            BufferStore::init(&remote_proto);
1603            WorktreeStore::init_remote(&remote_proto);
1604            LspStore::init(&remote_proto);
1605            SettingsObserver::init(&remote_proto);
1606            TaskStore::init(Some(&remote_proto));
1607            ToolchainStore::init(&remote_proto);
1608            DapStore::init(&remote_proto, cx);
1609            BreakpointStore::init(&remote_proto);
1610            GitStore::init(&remote_proto);
1611            AgentServerStore::init_remote(&remote_proto);
1612
1613            this
1614        })
1615    }
1616
1617    pub async fn in_room(
1618        remote_id: u64,
1619        client: Arc<Client>,
1620        user_store: Entity<UserStore>,
1621        languages: Arc<LanguageRegistry>,
1622        fs: Arc<dyn Fs>,
1623        cx: AsyncApp,
1624    ) -> Result<Entity<Self>> {
1625        client.connect(true, &cx).await.into_response()?;
1626
1627        let subscriptions = [
1628            EntitySubscription::Project(client.subscribe_to_entity::<Self>(remote_id)?),
1629            EntitySubscription::BufferStore(client.subscribe_to_entity::<BufferStore>(remote_id)?),
1630            EntitySubscription::GitStore(client.subscribe_to_entity::<GitStore>(remote_id)?),
1631            EntitySubscription::WorktreeStore(
1632                client.subscribe_to_entity::<WorktreeStore>(remote_id)?,
1633            ),
1634            EntitySubscription::LspStore(client.subscribe_to_entity::<LspStore>(remote_id)?),
1635            EntitySubscription::SettingsObserver(
1636                client.subscribe_to_entity::<SettingsObserver>(remote_id)?,
1637            ),
1638            EntitySubscription::DapStore(client.subscribe_to_entity::<DapStore>(remote_id)?),
1639            EntitySubscription::BreakpointStore(
1640                client.subscribe_to_entity::<BreakpointStore>(remote_id)?,
1641            ),
1642        ];
1643        let committer = get_git_committer(&cx).await;
1644        let response = client
1645            .request_envelope(proto::JoinProject {
1646                project_id: remote_id,
1647                committer_email: committer.email,
1648                committer_name: committer.name,
1649                features: CURRENT_PROJECT_FEATURES
1650                    .iter()
1651                    .map(|s| s.to_string())
1652                    .collect(),
1653            })
1654            .await?;
1655        Self::from_join_project_response(
1656            response,
1657            subscriptions,
1658            client,
1659            false,
1660            user_store,
1661            languages,
1662            fs,
1663            cx,
1664        )
1665        .await
1666    }
1667
1668    async fn from_join_project_response(
1669        response: TypedEnvelope<proto::JoinProjectResponse>,
1670        subscriptions: [EntitySubscription; 8],
1671        client: Arc<Client>,
1672        run_tasks: bool,
1673        user_store: Entity<UserStore>,
1674        languages: Arc<LanguageRegistry>,
1675        fs: Arc<dyn Fs>,
1676        mut cx: AsyncApp,
1677    ) -> Result<Entity<Self>> {
1678        let remote_id = response.payload.project_id;
1679        let role = response.payload.role();
1680
1681        let path_style = if response.payload.windows_paths {
1682            PathStyle::Windows
1683        } else {
1684            PathStyle::Posix
1685        };
1686
1687        let worktree_store = cx.new(|cx| {
1688            WorktreeStore::remote(
1689                true,
1690                client.clone().into(),
1691                response.payload.project_id,
1692                path_style,
1693                WorktreeIdCounter::get(cx),
1694            )
1695        });
1696        let buffer_store = cx.new(|cx| {
1697            BufferStore::remote(worktree_store.clone(), client.clone().into(), remote_id, cx)
1698        });
1699        let image_store = cx.new(|cx| {
1700            ImageStore::remote(worktree_store.clone(), client.clone().into(), remote_id, cx)
1701        });
1702
1703        let environment =
1704            cx.new(|cx| ProjectEnvironment::new(None, worktree_store.downgrade(), None, true, cx));
1705        let breakpoint_store = cx.new(|_| {
1706            BreakpointStore::remote(
1707                remote_id,
1708                client.clone().into(),
1709                buffer_store.clone(),
1710                worktree_store.clone(),
1711            )
1712        });
1713        let dap_store = cx.new(|cx| {
1714            DapStore::new_collab(
1715                remote_id,
1716                client.clone().into(),
1717                breakpoint_store.clone(),
1718                worktree_store.clone(),
1719                fs.clone(),
1720                cx,
1721            )
1722        });
1723
1724        let lsp_store = cx.new(|cx| {
1725            LspStore::new_remote(
1726                buffer_store.clone(),
1727                worktree_store.clone(),
1728                languages.clone(),
1729                client.clone().into(),
1730                remote_id,
1731                cx,
1732            )
1733        });
1734
1735        let task_store = cx.new(|cx| {
1736            if run_tasks {
1737                TaskStore::remote(
1738                    buffer_store.downgrade(),
1739                    worktree_store.clone(),
1740                    Arc::new(EmptyToolchainStore),
1741                    client.clone().into(),
1742                    remote_id,
1743                    cx,
1744                )
1745            } else {
1746                TaskStore::Noop
1747            }
1748        });
1749
1750        let settings_observer = cx.new(|cx| {
1751            SettingsObserver::new_remote(
1752                fs.clone(),
1753                worktree_store.clone(),
1754                task_store.clone(),
1755                None,
1756                true,
1757                cx,
1758            )
1759        });
1760
1761        let git_store = cx.new(|cx| {
1762            GitStore::remote(
1763                // In this remote case we pass None for the environment
1764                &worktree_store,
1765                buffer_store.clone(),
1766                client.clone().into(),
1767                remote_id,
1768                cx,
1769            )
1770        });
1771
1772        let agent_server_store = cx.new(|_cx| AgentServerStore::collab());
1773        let replica_id = ReplicaId::new(response.payload.replica_id as u16);
1774
1775        let project = cx.new(|cx| {
1776            let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx);
1777
1778            let weak_self = cx.weak_entity();
1779            let context_server_store = cx.new(|cx| {
1780                ContextServerStore::local(worktree_store.clone(), Some(weak_self), false, cx)
1781            });
1782
1783            let mut worktrees = Vec::new();
1784            for worktree in response.payload.worktrees {
1785                let worktree = Worktree::remote(
1786                    remote_id,
1787                    replica_id,
1788                    worktree,
1789                    client.clone().into(),
1790                    path_style,
1791                    cx,
1792                );
1793                worktrees.push(worktree);
1794            }
1795
1796            let (tx, rx) = mpsc::unbounded();
1797            cx.spawn(async move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx).await)
1798                .detach();
1799
1800            cx.subscribe(&worktree_store, Self::on_worktree_store_event)
1801                .detach();
1802
1803            cx.subscribe(&buffer_store, Self::on_buffer_store_event)
1804                .detach();
1805            cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
1806            cx.subscribe(&settings_observer, Self::on_settings_observer_event)
1807                .detach();
1808
1809            cx.subscribe(&dap_store, Self::on_dap_store_event).detach();
1810
1811            let mut project = Self {
1812                buffer_ordered_messages_tx: tx,
1813                buffer_store: buffer_store.clone(),
1814                image_store,
1815                worktree_store: worktree_store.clone(),
1816                lsp_store: lsp_store.clone(),
1817                context_server_store,
1818                active_entry: None,
1819                collaborators: Default::default(),
1820                join_project_response_message_id: response.message_id,
1821                languages,
1822                user_store: user_store.clone(),
1823                task_store,
1824                snippets,
1825                fs,
1826                remote_client: None,
1827                settings_observer: settings_observer.clone(),
1828                client_subscriptions: Default::default(),
1829                _subscriptions: vec![cx.on_release(Self::release)],
1830                collab_client: client.clone(),
1831                client_state: ProjectClientState::Collab {
1832                    sharing_has_stopped: false,
1833                    capability: Capability::ReadWrite,
1834                    remote_id,
1835                    replica_id,
1836                },
1837                breakpoint_store: breakpoint_store.clone(),
1838                dap_store: dap_store.clone(),
1839                git_store: git_store.clone(),
1840                agent_server_store,
1841                buffers_needing_diff: Default::default(),
1842                git_diff_debouncer: DebouncedDelay::new(),
1843                terminals: Terminals {
1844                    local_handles: Vec::new(),
1845                },
1846                node: None,
1847                search_history: Self::new_search_history(),
1848                search_included_history: Self::new_search_history(),
1849                search_excluded_history: Self::new_search_history(),
1850                environment,
1851                remotely_created_models: Arc::new(Mutex::new(RemotelyCreatedModels::default())),
1852                toolchain_store: None,
1853                agent_location: None,
1854                downloading_files: Default::default(),
1855            };
1856            project.set_role(role, cx);
1857            for worktree in worktrees {
1858                project.add_worktree(&worktree, cx);
1859            }
1860            project
1861        });
1862
1863        let weak_project = project.downgrade();
1864        lsp_store.update(&mut cx, |lsp_store, cx| {
1865            lsp_store.set_language_server_statuses_from_proto(
1866                weak_project,
1867                response.payload.language_servers,
1868                response.payload.language_server_capabilities,
1869                cx,
1870            );
1871        });
1872
1873        let subscriptions = subscriptions
1874            .into_iter()
1875            .map(|s| match s {
1876                EntitySubscription::BufferStore(subscription) => {
1877                    subscription.set_entity(&buffer_store, &cx)
1878                }
1879                EntitySubscription::WorktreeStore(subscription) => {
1880                    subscription.set_entity(&worktree_store, &cx)
1881                }
1882                EntitySubscription::GitStore(subscription) => {
1883                    subscription.set_entity(&git_store, &cx)
1884                }
1885                EntitySubscription::SettingsObserver(subscription) => {
1886                    subscription.set_entity(&settings_observer, &cx)
1887                }
1888                EntitySubscription::Project(subscription) => subscription.set_entity(&project, &cx),
1889                EntitySubscription::LspStore(subscription) => {
1890                    subscription.set_entity(&lsp_store, &cx)
1891                }
1892                EntitySubscription::DapStore(subscription) => {
1893                    subscription.set_entity(&dap_store, &cx)
1894                }
1895                EntitySubscription::BreakpointStore(subscription) => {
1896                    subscription.set_entity(&breakpoint_store, &cx)
1897                }
1898            })
1899            .collect::<Vec<_>>();
1900
1901        let user_ids = response
1902            .payload
1903            .collaborators
1904            .iter()
1905            .map(|peer| peer.user_id)
1906            .collect();
1907        user_store
1908            .update(&mut cx, |user_store, cx| user_store.get_users(user_ids, cx))
1909            .await?;
1910
1911        project.update(&mut cx, |this, cx| {
1912            this.set_collaborators_from_proto(response.payload.collaborators, cx)?;
1913            this.client_subscriptions.extend(subscriptions);
1914            anyhow::Ok(())
1915        })?;
1916
1917        Ok(project)
1918    }
1919
1920    fn new_search_history() -> SearchHistory {
1921        SearchHistory::new(
1922            Some(MAX_PROJECT_SEARCH_HISTORY_SIZE),
1923            search_history::QueryInsertionBehavior::AlwaysInsert,
1924        )
1925    }
1926
1927    fn release(&mut self, cx: &mut App) {
1928        if let Some(client) = self.remote_client.take() {
1929            let shutdown = client.update(cx, |client, cx| {
1930                client.shutdown_processes(
1931                    Some(proto::ShutdownRemoteServer {}),
1932                    cx.background_executor().clone(),
1933                )
1934            });
1935
1936            cx.background_spawn(async move {
1937                if let Some(shutdown) = shutdown {
1938                    shutdown.await;
1939                }
1940            })
1941            .detach()
1942        }
1943
1944        match &self.client_state {
1945            ProjectClientState::Local => {}
1946            ProjectClientState::Shared { .. } => {
1947                let _ = self.unshare_internal(cx);
1948            }
1949            ProjectClientState::Collab { remote_id, .. } => {
1950                let _ = self.collab_client.send(proto::LeaveProject {
1951                    project_id: *remote_id,
1952                });
1953                self.disconnected_from_host_internal(cx);
1954            }
1955        }
1956    }
1957
1958    #[cfg(feature = "test-support")]
1959    pub fn client_subscriptions(&self) -> &Vec<client::Subscription> {
1960        &self.client_subscriptions
1961    }
1962
1963    #[cfg(feature = "test-support")]
1964    pub async fn example(
1965        root_paths: impl IntoIterator<Item = &Path>,
1966        cx: &mut AsyncApp,
1967    ) -> Entity<Project> {
1968        use clock::FakeSystemClock;
1969
1970        let fs = Arc::new(RealFs::new(None, cx.background_executor().clone()));
1971        let languages = LanguageRegistry::test(cx.background_executor().clone());
1972        let clock = Arc::new(FakeSystemClock::new());
1973        let http_client = http_client::FakeHttpClient::with_404_response();
1974        let client = cx.update(|cx| client::Client::new(clock, http_client.clone(), cx));
1975        let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
1976        let project = cx.update(|cx| {
1977            Project::local(
1978                client,
1979                node_runtime::NodeRuntime::unavailable(),
1980                user_store,
1981                Arc::new(languages),
1982                fs,
1983                None,
1984                LocalProjectFlags {
1985                    init_worktree_trust: false,
1986                    ..Default::default()
1987                },
1988                cx,
1989            )
1990        });
1991        for path in root_paths {
1992            let (tree, _): (Entity<Worktree>, _) = project
1993                .update(cx, |project, cx| {
1994                    project.find_or_create_worktree(path, true, cx)
1995                })
1996                .await
1997                .unwrap();
1998            tree.read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
1999                .await;
2000        }
2001        project
2002    }
2003
2004    #[cfg(feature = "test-support")]
2005    pub async fn test(
2006        fs: Arc<dyn Fs>,
2007        root_paths: impl IntoIterator<Item = &Path>,
2008        cx: &mut gpui::TestAppContext,
2009    ) -> Entity<Project> {
2010        Self::test_project(fs, root_paths, false, cx).await
2011    }
2012
2013    #[cfg(feature = "test-support")]
2014    pub async fn test_with_worktree_trust(
2015        fs: Arc<dyn Fs>,
2016        root_paths: impl IntoIterator<Item = &Path>,
2017        cx: &mut gpui::TestAppContext,
2018    ) -> Entity<Project> {
2019        Self::test_project(fs, root_paths, true, cx).await
2020    }
2021
2022    #[cfg(feature = "test-support")]
2023    async fn test_project(
2024        fs: Arc<dyn Fs>,
2025        root_paths: impl IntoIterator<Item = &Path>,
2026        init_worktree_trust: bool,
2027        cx: &mut gpui::TestAppContext,
2028    ) -> Entity<Project> {
2029        use clock::FakeSystemClock;
2030
2031        let languages = LanguageRegistry::test(cx.executor());
2032        let clock = Arc::new(FakeSystemClock::new());
2033        let http_client = http_client::FakeHttpClient::with_404_response();
2034        let client = cx.update(|cx| client::Client::new(clock, http_client.clone(), cx));
2035        let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
2036        let project = cx.update(|cx| {
2037            Project::local(
2038                client,
2039                node_runtime::NodeRuntime::unavailable(),
2040                user_store,
2041                Arc::new(languages),
2042                fs,
2043                None,
2044                LocalProjectFlags {
2045                    init_worktree_trust,
2046                    ..Default::default()
2047                },
2048                cx,
2049            )
2050        });
2051        for path in root_paths {
2052            let (tree, _) = project
2053                .update(cx, |project, cx| {
2054                    project.find_or_create_worktree(path, true, cx)
2055                })
2056                .await
2057                .unwrap();
2058
2059            tree.read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
2060                .await;
2061        }
2062        project
2063    }
2064
2065    #[inline]
2066    pub fn dap_store(&self) -> Entity<DapStore> {
2067        self.dap_store.clone()
2068    }
2069
2070    #[inline]
2071    pub fn breakpoint_store(&self) -> Entity<BreakpointStore> {
2072        self.breakpoint_store.clone()
2073    }
2074
2075    pub fn active_debug_session(&self, cx: &App) -> Option<(Entity<Session>, ActiveStackFrame)> {
2076        let active_position = self.breakpoint_store.read(cx).active_position()?;
2077        let session = self
2078            .dap_store
2079            .read(cx)
2080            .session_by_id(active_position.session_id)?;
2081        Some((session, active_position.clone()))
2082    }
2083
2084    #[inline]
2085    pub fn lsp_store(&self) -> Entity<LspStore> {
2086        self.lsp_store.clone()
2087    }
2088
2089    #[inline]
2090    pub fn worktree_store(&self) -> Entity<WorktreeStore> {
2091        self.worktree_store.clone()
2092    }
2093
2094    /// Returns a future that resolves when all visible worktrees have completed
2095    /// their initial scan.
2096    pub fn wait_for_initial_scan(&self, cx: &App) -> impl Future<Output = ()> + use<> {
2097        self.worktree_store.read(cx).wait_for_initial_scan()
2098    }
2099
2100    #[inline]
2101    pub fn context_server_store(&self) -> Entity<ContextServerStore> {
2102        self.context_server_store.clone()
2103    }
2104
2105    #[inline]
2106    pub fn buffer_for_id(&self, remote_id: BufferId, cx: &App) -> Option<Entity<Buffer>> {
2107        self.buffer_store.read(cx).get(remote_id)
2108    }
2109
2110    #[inline]
2111    pub fn languages(&self) -> &Arc<LanguageRegistry> {
2112        &self.languages
2113    }
2114
2115    #[inline]
2116    pub fn client(&self) -> Arc<Client> {
2117        self.collab_client.clone()
2118    }
2119
2120    #[inline]
2121    pub fn remote_client(&self) -> Option<Entity<RemoteClient>> {
2122        self.remote_client.clone()
2123    }
2124
2125    #[inline]
2126    pub fn user_store(&self) -> Entity<UserStore> {
2127        self.user_store.clone()
2128    }
2129
2130    #[inline]
2131    pub fn node_runtime(&self) -> Option<&NodeRuntime> {
2132        self.node.as_ref()
2133    }
2134
2135    #[inline]
2136    pub fn opened_buffers(&self, cx: &App) -> Vec<Entity<Buffer>> {
2137        self.buffer_store.read(cx).buffers().collect()
2138    }
2139
2140    #[inline]
2141    pub fn environment(&self) -> &Entity<ProjectEnvironment> {
2142        &self.environment
2143    }
2144
2145    #[inline]
2146    pub fn cli_environment(&self, cx: &App) -> Option<HashMap<String, String>> {
2147        self.environment.read(cx).get_cli_environment()
2148    }
2149
2150    #[inline]
2151    pub fn peek_environment_error<'a>(&'a self, cx: &'a App) -> Option<&'a String> {
2152        self.environment.read(cx).peek_environment_error()
2153    }
2154
2155    #[inline]
2156    pub fn pop_environment_error(&mut self, cx: &mut Context<Self>) {
2157        self.environment.update(cx, |environment, _| {
2158            environment.pop_environment_error();
2159        });
2160    }
2161
2162    #[cfg(feature = "test-support")]
2163    #[inline]
2164    pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &App) -> bool {
2165        self.buffer_store
2166            .read(cx)
2167            .get_by_path(&path.into())
2168            .is_some()
2169    }
2170
2171    #[inline]
2172    pub fn fs(&self) -> &Arc<dyn Fs> {
2173        &self.fs
2174    }
2175
2176    #[inline]
2177    pub fn remote_id(&self) -> Option<u64> {
2178        match self.client_state {
2179            ProjectClientState::Local => None,
2180            ProjectClientState::Shared { remote_id, .. }
2181            | ProjectClientState::Collab { remote_id, .. } => Some(remote_id),
2182        }
2183    }
2184
2185    #[inline]
2186    pub fn supports_terminal(&self, _cx: &App) -> bool {
2187        if self.is_local() {
2188            return true;
2189        }
2190        if self.is_via_remote_server() {
2191            return true;
2192        }
2193
2194        false
2195    }
2196
2197    #[inline]
2198    pub fn remote_connection_state(&self, cx: &App) -> Option<remote::ConnectionState> {
2199        self.remote_client
2200            .as_ref()
2201            .map(|remote| remote.read(cx).connection_state())
2202    }
2203
2204    #[inline]
2205    pub fn remote_connection_options(&self, cx: &App) -> Option<RemoteConnectionOptions> {
2206        self.remote_client
2207            .as_ref()
2208            .map(|remote| remote.read(cx).connection_options())
2209    }
2210
2211    /// Reveals the given path in the system file manager.
2212    ///
2213    /// On Windows with a WSL remote connection, this converts the POSIX path
2214    /// to a Windows UNC path before revealing.
2215    pub fn reveal_path(&self, path: &Path, cx: &mut Context<Self>) {
2216        #[cfg(target_os = "windows")]
2217        if let Some(RemoteConnectionOptions::Wsl(wsl_options)) = self.remote_connection_options(cx)
2218        {
2219            let path = path.to_path_buf();
2220            cx.spawn(async move |_, cx| {
2221                wsl_path_to_windows_path(&wsl_options, &path)
2222                    .await
2223                    .map(|windows_path| cx.update(|cx| cx.reveal_path(&windows_path)))
2224            })
2225            .detach_and_log_err(cx);
2226            return;
2227        }
2228
2229        cx.reveal_path(path);
2230    }
2231
2232    #[inline]
2233    pub fn replica_id(&self) -> ReplicaId {
2234        match self.client_state {
2235            ProjectClientState::Collab { replica_id, .. } => replica_id,
2236            _ => {
2237                if self.remote_client.is_some() {
2238                    ReplicaId::REMOTE_SERVER
2239                } else {
2240                    ReplicaId::LOCAL
2241                }
2242            }
2243        }
2244    }
2245
2246    #[inline]
2247    pub fn task_store(&self) -> &Entity<TaskStore> {
2248        &self.task_store
2249    }
2250
2251    #[inline]
2252    pub fn snippets(&self) -> &Entity<SnippetProvider> {
2253        &self.snippets
2254    }
2255
2256    #[inline]
2257    pub fn search_history(&self, kind: SearchInputKind) -> &SearchHistory {
2258        match kind {
2259            SearchInputKind::Query => &self.search_history,
2260            SearchInputKind::Include => &self.search_included_history,
2261            SearchInputKind::Exclude => &self.search_excluded_history,
2262        }
2263    }
2264
2265    #[inline]
2266    pub fn search_history_mut(&mut self, kind: SearchInputKind) -> &mut SearchHistory {
2267        match kind {
2268            SearchInputKind::Query => &mut self.search_history,
2269            SearchInputKind::Include => &mut self.search_included_history,
2270            SearchInputKind::Exclude => &mut self.search_excluded_history,
2271        }
2272    }
2273
2274    #[inline]
2275    pub fn collaborators(&self) -> &HashMap<proto::PeerId, Collaborator> {
2276        &self.collaborators
2277    }
2278
2279    #[inline]
2280    pub fn host(&self) -> Option<&Collaborator> {
2281        self.collaborators.values().find(|c| c.is_host)
2282    }
2283
2284    #[inline]
2285    pub fn set_worktrees_reordered(&mut self, worktrees_reordered: bool, cx: &mut App) {
2286        self.worktree_store.update(cx, |store, _| {
2287            store.set_worktrees_reordered(worktrees_reordered);
2288        });
2289    }
2290
2291    /// Collect all worktrees, including ones that don't appear in the project panel
2292    #[inline]
2293    pub fn worktrees<'a>(
2294        &self,
2295        cx: &'a App,
2296    ) -> impl 'a + DoubleEndedIterator<Item = Entity<Worktree>> {
2297        self.worktree_store.read(cx).worktrees()
2298    }
2299
2300    /// Collect all user-visible worktrees, the ones that appear in the project panel.
2301    #[inline]
2302    pub fn visible_worktrees<'a>(
2303        &'a self,
2304        cx: &'a App,
2305    ) -> impl 'a + DoubleEndedIterator<Item = Entity<Worktree>> {
2306        self.worktree_store.read(cx).visible_worktrees(cx)
2307    }
2308
2309    pub(crate) fn default_visible_worktree_paths(
2310        worktree_store: &WorktreeStore,
2311        cx: &App,
2312    ) -> Vec<PathBuf> {
2313        worktree_store
2314            .visible_worktrees(cx)
2315            .sorted_by(|left, right| {
2316                left.read(cx)
2317                    .is_single_file()
2318                    .cmp(&right.read(cx).is_single_file())
2319            })
2320            .filter_map(|worktree| {
2321                let worktree = worktree.read(cx);
2322                let path = worktree.abs_path();
2323                if worktree.is_single_file() {
2324                    Some(path.parent()?.to_path_buf())
2325                } else {
2326                    Some(path.to_path_buf())
2327                }
2328            })
2329            .collect()
2330    }
2331
2332    pub fn default_path_list(&self, cx: &App) -> PathList {
2333        let worktree_roots =
2334            Self::default_visible_worktree_paths(&self.worktree_store.read(cx), cx);
2335
2336        if worktree_roots.is_empty() {
2337            PathList::new(&[paths::home_dir().as_path()])
2338        } else {
2339            PathList::new(&worktree_roots)
2340        }
2341    }
2342
2343    #[inline]
2344    pub fn worktree_for_root_name(&self, root_name: &str, cx: &App) -> Option<Entity<Worktree>> {
2345        self.visible_worktrees(cx)
2346            .find(|tree| tree.read(cx).root_name() == root_name)
2347    }
2348
2349    #[inline]
2350    pub fn worktree_root_names<'a>(&'a self, cx: &'a App) -> impl Iterator<Item = &'a str> {
2351        self.visible_worktrees(cx)
2352            .map(|tree| tree.read(cx).root_name().as_unix_str())
2353    }
2354
2355    #[inline]
2356    pub fn worktree_for_id(&self, id: WorktreeId, cx: &App) -> Option<Entity<Worktree>> {
2357        self.worktree_store.read(cx).worktree_for_id(id, cx)
2358    }
2359
2360    pub fn worktree_for_entry(
2361        &self,
2362        entry_id: ProjectEntryId,
2363        cx: &App,
2364    ) -> Option<Entity<Worktree>> {
2365        self.worktree_store
2366            .read(cx)
2367            .worktree_for_entry(entry_id, cx)
2368    }
2369
2370    #[inline]
2371    pub fn worktree_id_for_entry(&self, entry_id: ProjectEntryId, cx: &App) -> Option<WorktreeId> {
2372        self.worktree_for_entry(entry_id, cx)
2373            .map(|worktree| worktree.read(cx).id())
2374    }
2375
2376    /// Checks if the entry is the root of a worktree.
2377    #[inline]
2378    pub fn entry_is_worktree_root(&self, entry_id: ProjectEntryId, cx: &App) -> bool {
2379        self.worktree_for_entry(entry_id, cx)
2380            .map(|worktree| {
2381                worktree
2382                    .read(cx)
2383                    .root_entry()
2384                    .is_some_and(|e| e.id == entry_id)
2385            })
2386            .unwrap_or(false)
2387    }
2388
2389    #[inline]
2390    pub fn project_path_git_status(
2391        &self,
2392        project_path: &ProjectPath,
2393        cx: &App,
2394    ) -> Option<FileStatus> {
2395        self.git_store
2396            .read(cx)
2397            .project_path_git_status(project_path, cx)
2398    }
2399
2400    #[inline]
2401    pub fn visibility_for_paths(
2402        &self,
2403        paths: &[PathBuf],
2404        exclude_sub_dirs: bool,
2405        cx: &App,
2406    ) -> Option<bool> {
2407        paths
2408            .iter()
2409            .map(|path| self.visibility_for_path(path, exclude_sub_dirs, cx))
2410            .max()
2411            .flatten()
2412    }
2413
2414    pub fn visibility_for_path(
2415        &self,
2416        path: &Path,
2417        exclude_sub_dirs: bool,
2418        cx: &App,
2419    ) -> Option<bool> {
2420        let path = SanitizedPath::new(path).as_path();
2421        let path_style = self.path_style(cx);
2422        self.worktrees(cx)
2423            .filter_map(|worktree| {
2424                let worktree = worktree.read(cx);
2425                let abs_path = worktree.abs_path();
2426                let relative_path = path_style.strip_prefix(path, abs_path.as_ref());
2427                let is_dir = relative_path
2428                    .as_ref()
2429                    .and_then(|p| worktree.entry_for_path(p))
2430                    .is_some_and(|e| e.is_dir());
2431                // Don't exclude the worktree root itself, only actual subdirectories
2432                let is_subdir = relative_path
2433                    .as_ref()
2434                    .is_some_and(|p| !p.as_ref().as_unix_str().is_empty());
2435                let contains =
2436                    relative_path.is_some() && (!exclude_sub_dirs || !is_dir || !is_subdir);
2437                contains.then(|| worktree.is_visible())
2438            })
2439            .max()
2440    }
2441
2442    pub fn create_entry(
2443        &mut self,
2444        project_path: impl Into<ProjectPath>,
2445        is_directory: bool,
2446        cx: &mut Context<Self>,
2447    ) -> Task<Result<CreatedEntry>> {
2448        let project_path = project_path.into();
2449        let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) else {
2450            return Task::ready(Err(anyhow!(format!(
2451                "No worktree for path {project_path:?}"
2452            ))));
2453        };
2454        worktree.update(cx, |worktree, cx| {
2455            worktree.create_entry(project_path.path, is_directory, None, cx)
2456        })
2457    }
2458
2459    #[inline]
2460    pub fn copy_entry(
2461        &mut self,
2462        entry_id: ProjectEntryId,
2463        new_project_path: ProjectPath,
2464        cx: &mut Context<Self>,
2465    ) -> Task<Result<Option<Entry>>> {
2466        self.worktree_store.update(cx, |worktree_store, cx| {
2467            worktree_store.copy_entry(entry_id, new_project_path, cx)
2468        })
2469    }
2470
2471    /// Renames the project entry with given `entry_id`.
2472    ///
2473    /// `new_path` is a relative path to worktree root.
2474    /// If root entry is renamed then its new root name is used instead.
2475    pub fn rename_entry(
2476        &mut self,
2477        entry_id: ProjectEntryId,
2478        new_path: ProjectPath,
2479        cx: &mut Context<Self>,
2480    ) -> Task<Result<CreatedEntry>> {
2481        let worktree_store = self.worktree_store.clone();
2482        let Some((worktree, old_path, is_dir)) = worktree_store
2483            .read(cx)
2484            .worktree_and_entry_for_id(entry_id, cx)
2485            .map(|(worktree, entry)| (worktree, entry.path.clone(), entry.is_dir()))
2486        else {
2487            return Task::ready(Err(anyhow!(format!("No worktree for entry {entry_id:?}"))));
2488        };
2489
2490        let worktree_id = worktree.read(cx).id();
2491        let is_root_entry = self.entry_is_worktree_root(entry_id, cx);
2492
2493        let lsp_store = self.lsp_store().downgrade();
2494        cx.spawn(async move |project, cx| {
2495            let (old_abs_path, new_abs_path) = {
2496                let root_path = worktree.read_with(cx, |this, _| this.abs_path());
2497                let new_abs_path = if is_root_entry {
2498                    root_path
2499                        .parent()
2500                        .unwrap()
2501                        .join(new_path.path.as_std_path())
2502                } else {
2503                    root_path.join(&new_path.path.as_std_path())
2504                };
2505                (root_path.join(old_path.as_std_path()), new_abs_path)
2506            };
2507            let transaction = LspStore::will_rename_entry(
2508                lsp_store.clone(),
2509                worktree_id,
2510                &old_abs_path,
2511                &new_abs_path,
2512                is_dir,
2513                cx.clone(),
2514            )
2515            .await;
2516
2517            let entry = worktree_store
2518                .update(cx, |worktree_store, cx| {
2519                    worktree_store.rename_entry(entry_id, new_path.clone(), cx)
2520                })
2521                .await?;
2522
2523            project
2524                .update(cx, |_, cx| {
2525                    cx.emit(Event::EntryRenamed(
2526                        transaction,
2527                        new_path.clone(),
2528                        new_abs_path.clone(),
2529                    ));
2530                })
2531                .ok();
2532
2533            lsp_store
2534                .read_with(cx, |this, _| {
2535                    this.did_rename_entry(worktree_id, &old_abs_path, &new_abs_path, is_dir);
2536                })
2537                .ok();
2538            Ok(entry)
2539        })
2540    }
2541
2542    #[inline]
2543    pub fn delete_file(
2544        &mut self,
2545        path: ProjectPath,
2546        trash: bool,
2547        cx: &mut Context<Self>,
2548    ) -> Option<Task<Result<()>>> {
2549        let entry = self.entry_for_path(&path, cx)?;
2550        self.delete_entry(entry.id, trash, cx)
2551    }
2552
2553    #[inline]
2554    pub fn delete_entry(
2555        &mut self,
2556        entry_id: ProjectEntryId,
2557        trash: bool,
2558        cx: &mut Context<Self>,
2559    ) -> Option<Task<Result<()>>> {
2560        let worktree = self.worktree_for_entry(entry_id, cx)?;
2561        cx.emit(Event::DeletedEntry(worktree.read(cx).id(), entry_id));
2562        worktree.update(cx, |worktree, cx| {
2563            worktree.delete_entry(entry_id, trash, cx)
2564        })
2565    }
2566
2567    #[inline]
2568    pub fn expand_entry(
2569        &mut self,
2570        worktree_id: WorktreeId,
2571        entry_id: ProjectEntryId,
2572        cx: &mut Context<Self>,
2573    ) -> Option<Task<Result<()>>> {
2574        let worktree = self.worktree_for_id(worktree_id, cx)?;
2575        worktree.update(cx, |worktree, cx| worktree.expand_entry(entry_id, cx))
2576    }
2577
2578    pub fn expand_all_for_entry(
2579        &mut self,
2580        worktree_id: WorktreeId,
2581        entry_id: ProjectEntryId,
2582        cx: &mut Context<Self>,
2583    ) -> Option<Task<Result<()>>> {
2584        let worktree = self.worktree_for_id(worktree_id, cx)?;
2585        let task = worktree.update(cx, |worktree, cx| {
2586            worktree.expand_all_for_entry(entry_id, cx)
2587        });
2588        Some(cx.spawn(async move |this, cx| {
2589            task.context("no task")?.await?;
2590            this.update(cx, |_, cx| {
2591                cx.emit(Event::ExpandedAllForEntry(worktree_id, entry_id));
2592            })?;
2593            Ok(())
2594        }))
2595    }
2596
2597    pub fn shared(&mut self, project_id: u64, cx: &mut Context<Self>) -> Result<()> {
2598        anyhow::ensure!(
2599            matches!(self.client_state, ProjectClientState::Local),
2600            "project was already shared"
2601        );
2602
2603        self.client_subscriptions.extend([
2604            self.collab_client
2605                .subscribe_to_entity(project_id)?
2606                .set_entity(&cx.entity(), &cx.to_async()),
2607            self.collab_client
2608                .subscribe_to_entity(project_id)?
2609                .set_entity(&self.worktree_store, &cx.to_async()),
2610            self.collab_client
2611                .subscribe_to_entity(project_id)?
2612                .set_entity(&self.buffer_store, &cx.to_async()),
2613            self.collab_client
2614                .subscribe_to_entity(project_id)?
2615                .set_entity(&self.lsp_store, &cx.to_async()),
2616            self.collab_client
2617                .subscribe_to_entity(project_id)?
2618                .set_entity(&self.settings_observer, &cx.to_async()),
2619            self.collab_client
2620                .subscribe_to_entity(project_id)?
2621                .set_entity(&self.dap_store, &cx.to_async()),
2622            self.collab_client
2623                .subscribe_to_entity(project_id)?
2624                .set_entity(&self.breakpoint_store, &cx.to_async()),
2625            self.collab_client
2626                .subscribe_to_entity(project_id)?
2627                .set_entity(&self.git_store, &cx.to_async()),
2628        ]);
2629
2630        self.buffer_store.update(cx, |buffer_store, cx| {
2631            buffer_store.shared(project_id, self.collab_client.clone().into(), cx)
2632        });
2633        self.worktree_store.update(cx, |worktree_store, cx| {
2634            worktree_store.shared(project_id, self.collab_client.clone().into(), cx);
2635        });
2636        self.lsp_store.update(cx, |lsp_store, cx| {
2637            lsp_store.shared(project_id, self.collab_client.clone().into(), cx)
2638        });
2639        self.breakpoint_store.update(cx, |breakpoint_store, _| {
2640            breakpoint_store.shared(project_id, self.collab_client.clone().into())
2641        });
2642        self.dap_store.update(cx, |dap_store, cx| {
2643            dap_store.shared(project_id, self.collab_client.clone().into(), cx);
2644        });
2645        self.task_store.update(cx, |task_store, cx| {
2646            task_store.shared(project_id, self.collab_client.clone().into(), cx);
2647        });
2648        self.settings_observer.update(cx, |settings_observer, cx| {
2649            settings_observer.shared(project_id, self.collab_client.clone().into(), cx)
2650        });
2651        self.git_store.update(cx, |git_store, cx| {
2652            git_store.shared(project_id, self.collab_client.clone().into(), cx)
2653        });
2654
2655        self.client_state = ProjectClientState::Shared {
2656            remote_id: project_id,
2657        };
2658
2659        cx.emit(Event::RemoteIdChanged(Some(project_id)));
2660        Ok(())
2661    }
2662
2663    pub fn reshared(
2664        &mut self,
2665        message: proto::ResharedProject,
2666        cx: &mut Context<Self>,
2667    ) -> Result<()> {
2668        self.buffer_store
2669            .update(cx, |buffer_store, _| buffer_store.forget_shared_buffers());
2670        self.set_collaborators_from_proto(message.collaborators, cx)?;
2671
2672        self.worktree_store.update(cx, |worktree_store, cx| {
2673            worktree_store.send_project_updates(cx);
2674        });
2675        if let Some(remote_id) = self.remote_id() {
2676            self.git_store.update(cx, |git_store, cx| {
2677                git_store.shared(remote_id, self.collab_client.clone().into(), cx)
2678            });
2679        }
2680        cx.emit(Event::Reshared);
2681        Ok(())
2682    }
2683
2684    pub fn rejoined(
2685        &mut self,
2686        message: proto::RejoinedProject,
2687        message_id: u32,
2688        cx: &mut Context<Self>,
2689    ) -> Result<()> {
2690        cx.update_global::<SettingsStore, _>(|store, cx| {
2691            for worktree_metadata in &message.worktrees {
2692                store
2693                    .clear_local_settings(WorktreeId::from_proto(worktree_metadata.id), cx)
2694                    .log_err();
2695            }
2696        });
2697
2698        self.join_project_response_message_id = message_id;
2699        self.set_worktrees_from_proto(message.worktrees, cx)?;
2700        self.set_collaborators_from_proto(message.collaborators, cx)?;
2701
2702        let project = cx.weak_entity();
2703        self.lsp_store.update(cx, |lsp_store, cx| {
2704            lsp_store.set_language_server_statuses_from_proto(
2705                project,
2706                message.language_servers,
2707                message.language_server_capabilities,
2708                cx,
2709            )
2710        });
2711        self.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
2712            .unwrap();
2713        cx.emit(Event::Rejoined);
2714        Ok(())
2715    }
2716
2717    #[inline]
2718    pub fn unshare(&mut self, cx: &mut Context<Self>) -> Result<()> {
2719        self.unshare_internal(cx)?;
2720        cx.emit(Event::RemoteIdChanged(None));
2721        Ok(())
2722    }
2723
2724    fn unshare_internal(&mut self, cx: &mut App) -> Result<()> {
2725        anyhow::ensure!(
2726            !self.is_via_collab(),
2727            "attempted to unshare a remote project"
2728        );
2729
2730        if let ProjectClientState::Shared { remote_id, .. } = self.client_state {
2731            self.client_state = ProjectClientState::Local;
2732            self.collaborators.clear();
2733            self.client_subscriptions.clear();
2734            self.worktree_store.update(cx, |store, cx| {
2735                store.unshared(cx);
2736            });
2737            self.buffer_store.update(cx, |buffer_store, cx| {
2738                buffer_store.forget_shared_buffers();
2739                buffer_store.unshared(cx)
2740            });
2741            self.task_store.update(cx, |task_store, cx| {
2742                task_store.unshared(cx);
2743            });
2744            self.breakpoint_store.update(cx, |breakpoint_store, cx| {
2745                breakpoint_store.unshared(cx);
2746            });
2747            self.dap_store.update(cx, |dap_store, cx| {
2748                dap_store.unshared(cx);
2749            });
2750            self.settings_observer.update(cx, |settings_observer, cx| {
2751                settings_observer.unshared(cx);
2752            });
2753            self.git_store.update(cx, |git_store, cx| {
2754                git_store.unshared(cx);
2755            });
2756
2757            self.collab_client
2758                .send(proto::UnshareProject {
2759                    project_id: remote_id,
2760                })
2761                .ok();
2762            Ok(())
2763        } else {
2764            anyhow::bail!("attempted to unshare an unshared project");
2765        }
2766    }
2767
2768    pub fn disconnected_from_host(&mut self, cx: &mut Context<Self>) {
2769        if self.is_disconnected(cx) {
2770            return;
2771        }
2772        self.disconnected_from_host_internal(cx);
2773        cx.emit(Event::DisconnectedFromHost);
2774    }
2775
2776    pub fn set_role(&mut self, role: proto::ChannelRole, cx: &mut Context<Self>) {
2777        let new_capability =
2778            if role == proto::ChannelRole::Member || role == proto::ChannelRole::Admin {
2779                Capability::ReadWrite
2780            } else {
2781                Capability::ReadOnly
2782            };
2783        if let ProjectClientState::Collab { capability, .. } = &mut self.client_state {
2784            if *capability == new_capability {
2785                return;
2786            }
2787
2788            *capability = new_capability;
2789            for buffer in self.opened_buffers(cx) {
2790                buffer.update(cx, |buffer, cx| buffer.set_capability(new_capability, cx));
2791            }
2792        }
2793    }
2794
2795    fn disconnected_from_host_internal(&mut self, cx: &mut App) {
2796        if let ProjectClientState::Collab {
2797            sharing_has_stopped,
2798            ..
2799        } = &mut self.client_state
2800        {
2801            *sharing_has_stopped = true;
2802            self.client_subscriptions.clear();
2803            self.collaborators.clear();
2804            self.worktree_store.update(cx, |store, cx| {
2805                store.disconnected_from_host(cx);
2806            });
2807            self.buffer_store.update(cx, |buffer_store, cx| {
2808                buffer_store.disconnected_from_host(cx)
2809            });
2810            self.lsp_store
2811                .update(cx, |lsp_store, _cx| lsp_store.disconnected_from_host());
2812        }
2813    }
2814
2815    #[inline]
2816    pub fn close(&mut self, cx: &mut Context<Self>) {
2817        cx.emit(Event::Closed);
2818    }
2819
2820    #[inline]
2821    pub fn is_disconnected(&self, cx: &App) -> bool {
2822        match &self.client_state {
2823            ProjectClientState::Collab {
2824                sharing_has_stopped,
2825                ..
2826            } => *sharing_has_stopped,
2827            ProjectClientState::Local if self.is_via_remote_server() => {
2828                self.remote_client_is_disconnected(cx)
2829            }
2830            _ => false,
2831        }
2832    }
2833
2834    #[inline]
2835    fn remote_client_is_disconnected(&self, cx: &App) -> bool {
2836        self.remote_client
2837            .as_ref()
2838            .map(|remote| remote.read(cx).is_disconnected())
2839            .unwrap_or(false)
2840    }
2841
2842    #[inline]
2843    pub fn capability(&self) -> Capability {
2844        match &self.client_state {
2845            ProjectClientState::Collab { capability, .. } => *capability,
2846            ProjectClientState::Shared { .. } | ProjectClientState::Local => Capability::ReadWrite,
2847        }
2848    }
2849
2850    #[inline]
2851    pub fn is_read_only(&self, cx: &App) -> bool {
2852        self.is_disconnected(cx) || !self.capability().editable()
2853    }
2854
2855    #[inline]
2856    pub fn is_local(&self) -> bool {
2857        match &self.client_state {
2858            ProjectClientState::Local | ProjectClientState::Shared { .. } => {
2859                self.remote_client.is_none()
2860            }
2861            ProjectClientState::Collab { .. } => false,
2862        }
2863    }
2864
2865    /// Whether this project is a remote server (not counting collab).
2866    #[inline]
2867    pub fn is_via_remote_server(&self) -> bool {
2868        match &self.client_state {
2869            ProjectClientState::Local | ProjectClientState::Shared { .. } => {
2870                self.remote_client.is_some()
2871            }
2872            ProjectClientState::Collab { .. } => false,
2873        }
2874    }
2875
2876    /// Whether this project is from collab (not counting remote servers).
2877    #[inline]
2878    pub fn is_via_collab(&self) -> bool {
2879        match &self.client_state {
2880            ProjectClientState::Local | ProjectClientState::Shared { .. } => false,
2881            ProjectClientState::Collab { .. } => true,
2882        }
2883    }
2884
2885    /// `!self.is_local()`
2886    #[inline]
2887    pub fn is_remote(&self) -> bool {
2888        debug_assert_eq!(
2889            !self.is_local(),
2890            self.is_via_collab() || self.is_via_remote_server()
2891        );
2892        !self.is_local()
2893    }
2894
2895    #[inline]
2896    pub fn is_via_wsl_with_host_interop(&self, cx: &App) -> bool {
2897        match &self.client_state {
2898            ProjectClientState::Local | ProjectClientState::Shared { .. } => {
2899                matches!(
2900                    &self.remote_client, Some(remote_client)
2901                    if remote_client.read(cx).has_wsl_interop()
2902                )
2903            }
2904            _ => false,
2905        }
2906    }
2907
2908    pub fn disable_worktree_scanner(&mut self, cx: &mut Context<Self>) {
2909        self.worktree_store.update(cx, |worktree_store, _cx| {
2910            worktree_store.disable_scanner();
2911        });
2912    }
2913
2914    #[inline]
2915    pub fn create_buffer(
2916        &mut self,
2917        language: Option<Arc<Language>>,
2918        project_searchable: bool,
2919        cx: &mut Context<Self>,
2920    ) -> Task<Result<Entity<Buffer>>> {
2921        self.buffer_store.update(cx, |buffer_store, cx| {
2922            buffer_store.create_buffer(language, project_searchable, cx)
2923        })
2924    }
2925
2926    #[inline]
2927    pub fn create_local_buffer(
2928        &mut self,
2929        text: &str,
2930        language: Option<Arc<Language>>,
2931        project_searchable: bool,
2932        cx: &mut Context<Self>,
2933    ) -> Entity<Buffer> {
2934        if self.is_remote() {
2935            panic!("called create_local_buffer on a remote project")
2936        }
2937        self.buffer_store.update(cx, |buffer_store, cx| {
2938            buffer_store.create_local_buffer(text, language, project_searchable, cx)
2939        })
2940    }
2941
2942    pub fn open_path(
2943        &mut self,
2944        path: ProjectPath,
2945        cx: &mut Context<Self>,
2946    ) -> Task<Result<(Option<ProjectEntryId>, Entity<Buffer>)>> {
2947        let task = self.open_buffer(path, cx);
2948        cx.spawn(async move |_project, cx| {
2949            let buffer = task.await?;
2950            let project_entry_id = buffer.read_with(cx, |buffer, _cx| {
2951                File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id())
2952            });
2953
2954            Ok((project_entry_id, buffer))
2955        })
2956    }
2957
2958    pub fn open_local_buffer(
2959        &mut self,
2960        abs_path: impl AsRef<Path>,
2961        cx: &mut Context<Self>,
2962    ) -> Task<Result<Entity<Buffer>>> {
2963        let worktree_task = self.find_or_create_worktree(abs_path.as_ref(), false, cx);
2964        cx.spawn(async move |this, cx| {
2965            let (worktree, relative_path) = worktree_task.await?;
2966            this.update(cx, |this, cx| {
2967                this.open_buffer((worktree.read(cx).id(), relative_path), cx)
2968            })?
2969            .await
2970        })
2971    }
2972
2973    #[cfg(feature = "test-support")]
2974    pub fn open_local_buffer_with_lsp(
2975        &mut self,
2976        abs_path: impl AsRef<Path>,
2977        cx: &mut Context<Self>,
2978    ) -> Task<Result<(Entity<Buffer>, lsp_store::OpenLspBufferHandle)>> {
2979        if let Some((worktree, relative_path)) = self.find_worktree(abs_path.as_ref(), cx) {
2980            self.open_buffer_with_lsp((worktree.read(cx).id(), relative_path), cx)
2981        } else {
2982            Task::ready(Err(anyhow!("no such path")))
2983        }
2984    }
2985
2986    pub fn download_file(
2987        &mut self,
2988        worktree_id: WorktreeId,
2989        path: Arc<RelPath>,
2990        destination_path: PathBuf,
2991        cx: &mut Context<Self>,
2992    ) -> Task<Result<()>> {
2993        log::debug!(
2994            "download_file called: worktree_id={:?}, path={:?}, destination={:?}",
2995            worktree_id,
2996            path,
2997            destination_path
2998        );
2999
3000        let Some(remote_client) = &self.remote_client else {
3001            log::error!("download_file: not a remote project");
3002            return Task::ready(Err(anyhow!("not a remote project")));
3003        };
3004
3005        let proto_client = remote_client.read(cx).proto_client();
3006        // For SSH remote projects, use REMOTE_SERVER_PROJECT_ID instead of remote_id()
3007        // because SSH projects have client_state: Local but still need to communicate with remote server
3008        let project_id = self.remote_id().unwrap_or(REMOTE_SERVER_PROJECT_ID);
3009        let downloading_files = self.downloading_files.clone();
3010        let path_str = path.to_proto();
3011
3012        static NEXT_FILE_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
3013        let file_id = NEXT_FILE_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
3014
3015        // Register BEFORE sending request to avoid race condition
3016        let key = (worktree_id, path_str.clone());
3017        log::debug!(
3018            "download_file: pre-registering download with key={:?}, file_id={}",
3019            key,
3020            file_id
3021        );
3022        downloading_files.lock().insert(
3023            key,
3024            DownloadingFile {
3025                destination_path: destination_path,
3026                chunks: Vec::new(),
3027                total_size: 0,
3028                file_id: Some(file_id),
3029            },
3030        );
3031        log::debug!(
3032            "download_file: sending DownloadFileByPath request, path_str={}",
3033            path_str
3034        );
3035
3036        cx.spawn(async move |_this, _cx| {
3037            log::debug!("download_file: sending request with file_id={}...", file_id);
3038            let response = proto_client
3039                .request(proto::DownloadFileByPath {
3040                    project_id,
3041                    worktree_id: worktree_id.to_proto(),
3042                    path: path_str.clone(),
3043                    file_id,
3044                })
3045                .await?;
3046
3047            log::debug!("download_file: got response, file_id={}", response.file_id);
3048            // The file_id is set from the State message, we just confirm the request succeeded
3049            Ok(())
3050        })
3051    }
3052
3053    #[ztracing::instrument(skip_all)]
3054    pub fn open_buffer(
3055        &mut self,
3056        path: impl Into<ProjectPath>,
3057        cx: &mut App,
3058    ) -> Task<Result<Entity<Buffer>>> {
3059        if self.is_disconnected(cx) {
3060            return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
3061        }
3062
3063        self.buffer_store.update(cx, |buffer_store, cx| {
3064            buffer_store.open_buffer(path.into(), cx)
3065        })
3066    }
3067
3068    #[cfg(feature = "test-support")]
3069    pub fn open_buffer_with_lsp(
3070        &mut self,
3071        path: impl Into<ProjectPath>,
3072        cx: &mut Context<Self>,
3073    ) -> Task<Result<(Entity<Buffer>, lsp_store::OpenLspBufferHandle)>> {
3074        let buffer = self.open_buffer(path, cx);
3075        cx.spawn(async move |this, cx| {
3076            let buffer = buffer.await?;
3077            let handle = this.update(cx, |project, cx| {
3078                project.register_buffer_with_language_servers(&buffer, cx)
3079            })?;
3080            Ok((buffer, handle))
3081        })
3082    }
3083
3084    pub fn register_buffer_with_language_servers(
3085        &self,
3086        buffer: &Entity<Buffer>,
3087        cx: &mut App,
3088    ) -> OpenLspBufferHandle {
3089        self.lsp_store.update(cx, |lsp_store, cx| {
3090            lsp_store.register_buffer_with_language_servers(buffer, HashSet::default(), false, cx)
3091        })
3092    }
3093
3094    pub fn open_unstaged_diff(
3095        &mut self,
3096        buffer: Entity<Buffer>,
3097        cx: &mut Context<Self>,
3098    ) -> Task<Result<Entity<BufferDiff>>> {
3099        if self.is_disconnected(cx) {
3100            return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
3101        }
3102        self.git_store
3103            .update(cx, |git_store, cx| git_store.open_unstaged_diff(buffer, cx))
3104    }
3105
3106    #[ztracing::instrument(skip_all)]
3107    pub fn open_uncommitted_diff(
3108        &mut self,
3109        buffer: Entity<Buffer>,
3110        cx: &mut Context<Self>,
3111    ) -> Task<Result<Entity<BufferDiff>>> {
3112        if self.is_disconnected(cx) {
3113            return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
3114        }
3115        self.git_store.update(cx, |git_store, cx| {
3116            git_store.open_uncommitted_diff(buffer, cx)
3117        })
3118    }
3119
3120    pub fn open_buffer_by_id(
3121        &mut self,
3122        id: BufferId,
3123        cx: &mut Context<Self>,
3124    ) -> Task<Result<Entity<Buffer>>> {
3125        if let Some(buffer) = self.buffer_for_id(id, cx) {
3126            Task::ready(Ok(buffer))
3127        } else if self.is_local() || self.is_via_remote_server() {
3128            Task::ready(Err(anyhow!("buffer {id} does not exist")))
3129        } else if let Some(project_id) = self.remote_id() {
3130            let request = self.collab_client.request(proto::OpenBufferById {
3131                project_id,
3132                id: id.into(),
3133            });
3134            cx.spawn(async move |project, cx| {
3135                let buffer_id = BufferId::new(request.await?.buffer_id)?;
3136                project
3137                    .update(cx, |project, cx| {
3138                        project.buffer_store.update(cx, |buffer_store, cx| {
3139                            buffer_store.wait_for_remote_buffer(buffer_id, cx)
3140                        })
3141                    })?
3142                    .await
3143            })
3144        } else {
3145            Task::ready(Err(anyhow!("cannot open buffer while disconnected")))
3146        }
3147    }
3148
3149    pub fn save_buffers(
3150        &self,
3151        buffers: HashSet<Entity<Buffer>>,
3152        cx: &mut Context<Self>,
3153    ) -> Task<Result<()>> {
3154        cx.spawn(async move |this, cx| {
3155            let save_tasks = buffers.into_iter().filter_map(|buffer| {
3156                this.update(cx, |this, cx| this.save_buffer(buffer, cx))
3157                    .ok()
3158            });
3159            try_join_all(save_tasks).await?;
3160            Ok(())
3161        })
3162    }
3163
3164    pub fn save_buffer(&self, buffer: Entity<Buffer>, cx: &mut Context<Self>) -> Task<Result<()>> {
3165        self.buffer_store
3166            .update(cx, |buffer_store, cx| buffer_store.save_buffer(buffer, cx))
3167    }
3168
3169    pub fn save_buffer_as(
3170        &mut self,
3171        buffer: Entity<Buffer>,
3172        path: ProjectPath,
3173        cx: &mut Context<Self>,
3174    ) -> Task<Result<()>> {
3175        self.buffer_store.update(cx, |buffer_store, cx| {
3176            buffer_store.save_buffer_as(buffer.clone(), path, cx)
3177        })
3178    }
3179
3180    pub fn get_open_buffer(&self, path: &ProjectPath, cx: &App) -> Option<Entity<Buffer>> {
3181        self.buffer_store.read(cx).get_by_path(path)
3182    }
3183
3184    fn register_buffer(&mut self, buffer: &Entity<Buffer>, cx: &mut Context<Self>) -> Result<()> {
3185        {
3186            let mut remotely_created_models = self.remotely_created_models.lock();
3187            if remotely_created_models.retain_count > 0 {
3188                remotely_created_models.buffers.push(buffer.clone())
3189            }
3190        }
3191
3192        self.request_buffer_diff_recalculation(buffer, cx);
3193
3194        cx.subscribe(buffer, |this, buffer, event, cx| {
3195            this.on_buffer_event(buffer, event, cx);
3196        })
3197        .detach();
3198
3199        Ok(())
3200    }
3201
3202    pub fn open_image(
3203        &mut self,
3204        path: impl Into<ProjectPath>,
3205        cx: &mut Context<Self>,
3206    ) -> Task<Result<Entity<ImageItem>>> {
3207        if self.is_disconnected(cx) {
3208            return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
3209        }
3210
3211        let open_image_task = self.image_store.update(cx, |image_store, cx| {
3212            image_store.open_image(path.into(), cx)
3213        });
3214
3215        let weak_project = cx.entity().downgrade();
3216        cx.spawn(async move |_, cx| {
3217            let image_item = open_image_task.await?;
3218
3219            // Check if metadata already exists (e.g., for remote images)
3220            let needs_metadata =
3221                cx.read_entity(&image_item, |item, _| item.image_metadata.is_none());
3222
3223            if needs_metadata {
3224                let project = weak_project.upgrade().context("Project dropped")?;
3225                let metadata =
3226                    ImageItem::load_image_metadata(image_item.clone(), project, cx).await?;
3227                image_item.update(cx, |image_item, cx| {
3228                    image_item.image_metadata = Some(metadata);
3229                    cx.emit(ImageItemEvent::MetadataUpdated);
3230                });
3231            }
3232
3233            Ok(image_item)
3234        })
3235    }
3236
3237    async fn send_buffer_ordered_messages(
3238        project: WeakEntity<Self>,
3239        rx: UnboundedReceiver<BufferOrderedMessage>,
3240        cx: &mut AsyncApp,
3241    ) -> Result<()> {
3242        const MAX_BATCH_SIZE: usize = 128;
3243
3244        let mut operations_by_buffer_id = HashMap::default();
3245        async fn flush_operations(
3246            this: &WeakEntity<Project>,
3247            operations_by_buffer_id: &mut HashMap<BufferId, Vec<proto::Operation>>,
3248            needs_resync_with_host: &mut bool,
3249            is_local: bool,
3250            cx: &mut AsyncApp,
3251        ) -> Result<()> {
3252            for (buffer_id, operations) in operations_by_buffer_id.drain() {
3253                let request = this.read_with(cx, |this, _| {
3254                    let project_id = this.remote_id()?;
3255                    Some(this.collab_client.request(proto::UpdateBuffer {
3256                        buffer_id: buffer_id.into(),
3257                        project_id,
3258                        operations,
3259                    }))
3260                })?;
3261                if let Some(request) = request
3262                    && request.await.is_err()
3263                    && !is_local
3264                {
3265                    *needs_resync_with_host = true;
3266                    break;
3267                }
3268            }
3269            Ok(())
3270        }
3271
3272        let mut needs_resync_with_host = false;
3273        let mut changes = rx.ready_chunks(MAX_BATCH_SIZE);
3274
3275        while let Some(changes) = changes.next().await {
3276            let is_local = project.read_with(cx, |this, _| this.is_local())?;
3277
3278            for change in changes {
3279                match change {
3280                    BufferOrderedMessage::Operation {
3281                        buffer_id,
3282                        operation,
3283                    } => {
3284                        if needs_resync_with_host {
3285                            continue;
3286                        }
3287
3288                        operations_by_buffer_id
3289                            .entry(buffer_id)
3290                            .or_insert(Vec::new())
3291                            .push(operation);
3292                    }
3293
3294                    BufferOrderedMessage::Resync => {
3295                        operations_by_buffer_id.clear();
3296                        if project
3297                            .update(cx, |this, cx| this.synchronize_remote_buffers(cx))?
3298                            .await
3299                            .is_ok()
3300                        {
3301                            needs_resync_with_host = false;
3302                        }
3303                    }
3304
3305                    BufferOrderedMessage::LanguageServerUpdate {
3306                        language_server_id,
3307                        message,
3308                        name,
3309                    } => {
3310                        flush_operations(
3311                            &project,
3312                            &mut operations_by_buffer_id,
3313                            &mut needs_resync_with_host,
3314                            is_local,
3315                            cx,
3316                        )
3317                        .await?;
3318
3319                        project.read_with(cx, |project, _| {
3320                            if let Some(project_id) = project.remote_id() {
3321                                project
3322                                    .collab_client
3323                                    .send(proto::UpdateLanguageServer {
3324                                        project_id,
3325                                        server_name: name.map(|name| String::from(name.0)),
3326                                        language_server_id: language_server_id.to_proto(),
3327                                        variant: Some(message),
3328                                    })
3329                                    .log_err();
3330                            }
3331                        })?;
3332                    }
3333                }
3334            }
3335
3336            flush_operations(
3337                &project,
3338                &mut operations_by_buffer_id,
3339                &mut needs_resync_with_host,
3340                is_local,
3341                cx,
3342            )
3343            .await?;
3344        }
3345
3346        Ok(())
3347    }
3348
3349    fn on_buffer_store_event(
3350        &mut self,
3351        _: Entity<BufferStore>,
3352        event: &BufferStoreEvent,
3353        cx: &mut Context<Self>,
3354    ) {
3355        match event {
3356            BufferStoreEvent::BufferAdded(buffer) => {
3357                self.register_buffer(buffer, cx).log_err();
3358            }
3359            BufferStoreEvent::BufferDropped(buffer_id) => {
3360                if let Some(ref remote_client) = self.remote_client {
3361                    remote_client
3362                        .read(cx)
3363                        .proto_client()
3364                        .send(proto::CloseBuffer {
3365                            project_id: 0,
3366                            buffer_id: buffer_id.to_proto(),
3367                        })
3368                        .log_err();
3369                }
3370            }
3371            _ => {}
3372        }
3373    }
3374
3375    fn on_image_store_event(
3376        &mut self,
3377        _: Entity<ImageStore>,
3378        event: &ImageStoreEvent,
3379        cx: &mut Context<Self>,
3380    ) {
3381        match event {
3382            ImageStoreEvent::ImageAdded(image) => {
3383                cx.subscribe(image, |this, image, event, cx| {
3384                    this.on_image_event(image, event, cx);
3385                })
3386                .detach();
3387            }
3388        }
3389    }
3390
3391    fn on_dap_store_event(
3392        &mut self,
3393        _: Entity<DapStore>,
3394        event: &DapStoreEvent,
3395        cx: &mut Context<Self>,
3396    ) {
3397        if let DapStoreEvent::Notification(message) = event {
3398            cx.emit(Event::Toast {
3399                notification_id: "dap".into(),
3400                message: message.clone(),
3401                link: None,
3402            });
3403        }
3404    }
3405
3406    fn on_lsp_store_event(
3407        &mut self,
3408        _: Entity<LspStore>,
3409        event: &LspStoreEvent,
3410        cx: &mut Context<Self>,
3411    ) {
3412        match event {
3413            LspStoreEvent::DiagnosticsUpdated { server_id, paths } => {
3414                cx.emit(Event::DiagnosticsUpdated {
3415                    paths: paths.clone(),
3416                    language_server_id: *server_id,
3417                })
3418            }
3419            LspStoreEvent::LanguageServerAdded(server_id, name, worktree_id) => cx.emit(
3420                Event::LanguageServerAdded(*server_id, name.clone(), *worktree_id),
3421            ),
3422            LspStoreEvent::LanguageServerRemoved(server_id) => {
3423                cx.emit(Event::LanguageServerRemoved(*server_id))
3424            }
3425            LspStoreEvent::LanguageServerLog(server_id, log_type, string) => cx.emit(
3426                Event::LanguageServerLog(*server_id, log_type.clone(), string.clone()),
3427            ),
3428            LspStoreEvent::LanguageDetected {
3429                buffer,
3430                new_language,
3431            } => {
3432                let Some(_) = new_language else {
3433                    cx.emit(Event::LanguageNotFound(buffer.clone()));
3434                    return;
3435                };
3436            }
3437            LspStoreEvent::RefreshInlayHints {
3438                server_id,
3439                request_id,
3440            } => cx.emit(Event::RefreshInlayHints {
3441                server_id: *server_id,
3442                request_id: *request_id,
3443            }),
3444            LspStoreEvent::RefreshSemanticTokens {
3445                server_id,
3446                request_id,
3447            } => cx.emit(Event::RefreshSemanticTokens {
3448                server_id: *server_id,
3449                request_id: *request_id,
3450            }),
3451            LspStoreEvent::RefreshCodeLens => cx.emit(Event::RefreshCodeLens),
3452            LspStoreEvent::LanguageServerPrompt(prompt) => {
3453                cx.emit(Event::LanguageServerPrompt(prompt.clone()))
3454            }
3455            LspStoreEvent::DiskBasedDiagnosticsStarted { language_server_id } => {
3456                cx.emit(Event::DiskBasedDiagnosticsStarted {
3457                    language_server_id: *language_server_id,
3458                });
3459            }
3460            LspStoreEvent::DiskBasedDiagnosticsFinished { language_server_id } => {
3461                cx.emit(Event::DiskBasedDiagnosticsFinished {
3462                    language_server_id: *language_server_id,
3463                });
3464            }
3465            LspStoreEvent::LanguageServerUpdate {
3466                language_server_id,
3467                name,
3468                message,
3469            } => {
3470                if self.is_local() {
3471                    self.enqueue_buffer_ordered_message(
3472                        BufferOrderedMessage::LanguageServerUpdate {
3473                            language_server_id: *language_server_id,
3474                            message: message.clone(),
3475                            name: name.clone(),
3476                        },
3477                    )
3478                    .ok();
3479                }
3480
3481                match message {
3482                    proto::update_language_server::Variant::MetadataUpdated(update) => {
3483                        self.lsp_store.update(cx, |lsp_store, _| {
3484                            if let Some(capabilities) = update
3485                                .capabilities
3486                                .as_ref()
3487                                .and_then(|capabilities| serde_json::from_str(capabilities).ok())
3488                            {
3489                                lsp_store
3490                                    .lsp_server_capabilities
3491                                    .insert(*language_server_id, capabilities);
3492                            }
3493
3494                            if let Some(language_server_status) = lsp_store
3495                                .language_server_statuses
3496                                .get_mut(language_server_id)
3497                            {
3498                                if let Some(binary) = &update.binary {
3499                                    language_server_status.binary = Some(LanguageServerBinary {
3500                                        path: PathBuf::from(&binary.path),
3501                                        arguments: binary
3502                                            .arguments
3503                                            .iter()
3504                                            .map(OsString::from)
3505                                            .collect(),
3506                                        env: None,
3507                                    });
3508                                }
3509
3510                                language_server_status.configuration = update
3511                                    .configuration
3512                                    .as_ref()
3513                                    .and_then(|config_str| serde_json::from_str(config_str).ok());
3514
3515                                language_server_status.workspace_folders = update
3516                                    .workspace_folders
3517                                    .iter()
3518                                    .filter_map(|uri_str| lsp::Uri::from_str(uri_str).ok())
3519                                    .collect();
3520                            }
3521                        });
3522                    }
3523                    proto::update_language_server::Variant::RegisteredForBuffer(update) => {
3524                        if let Some(buffer_id) = BufferId::new(update.buffer_id).ok() {
3525                            cx.emit(Event::LanguageServerBufferRegistered {
3526                                buffer_id,
3527                                server_id: *language_server_id,
3528                                buffer_abs_path: PathBuf::from(&update.buffer_abs_path),
3529                                name: name.clone(),
3530                            });
3531                        }
3532                    }
3533                    _ => (),
3534                }
3535            }
3536            LspStoreEvent::Notification(message) => cx.emit(Event::Toast {
3537                notification_id: "lsp".into(),
3538                message: message.clone(),
3539                link: None,
3540            }),
3541            LspStoreEvent::SnippetEdit {
3542                buffer_id,
3543                edits,
3544                most_recent_edit,
3545            } => {
3546                if most_recent_edit.replica_id == self.replica_id() {
3547                    cx.emit(Event::SnippetEdit(*buffer_id, edits.clone()))
3548                }
3549            }
3550            LspStoreEvent::WorkspaceEditApplied(transaction) => {
3551                cx.emit(Event::WorkspaceEditApplied(transaction.clone()))
3552            }
3553        }
3554    }
3555
3556    fn on_remote_client_event(
3557        &mut self,
3558        _: Entity<RemoteClient>,
3559        event: &remote::RemoteClientEvent,
3560        cx: &mut Context<Self>,
3561    ) {
3562        match event {
3563            &remote::RemoteClientEvent::Disconnected { server_not_running } => {
3564                self.worktree_store.update(cx, |store, cx| {
3565                    store.disconnected_from_host(cx);
3566                });
3567                self.buffer_store.update(cx, |buffer_store, cx| {
3568                    buffer_store.disconnected_from_host(cx)
3569                });
3570                self.lsp_store.update(cx, |lsp_store, _cx| {
3571                    lsp_store.disconnected_from_ssh_remote()
3572                });
3573                cx.emit(Event::DisconnectedFromRemote { server_not_running });
3574            }
3575        }
3576    }
3577
3578    fn on_settings_observer_event(
3579        &mut self,
3580        _: Entity<SettingsObserver>,
3581        event: &SettingsObserverEvent,
3582        cx: &mut Context<Self>,
3583    ) {
3584        match event {
3585            SettingsObserverEvent::LocalSettingsUpdated(result) => match result {
3586                Err(InvalidSettingsError::LocalSettings { message, path }) => {
3587                    let message = format!("Failed to set local settings in {path:?}:\n{message}");
3588                    cx.emit(Event::Toast {
3589                        notification_id: format!("local-settings-{path:?}").into(),
3590                        link: None,
3591                        message,
3592                    });
3593                }
3594                Ok(path) => cx.emit(Event::HideToast {
3595                    notification_id: format!("local-settings-{path:?}").into(),
3596                }),
3597                Err(_) => {}
3598            },
3599            SettingsObserverEvent::LocalTasksUpdated(result) => match result {
3600                Err(InvalidSettingsError::Tasks { message, path }) => {
3601                    let message = format!("Failed to set local tasks in {path:?}:\n{message}");
3602                    cx.emit(Event::Toast {
3603                        notification_id: format!("local-tasks-{path:?}").into(),
3604                        link: Some(ToastLink {
3605                            label: "Open Tasks Documentation",
3606                            url: "https://zed.dev/docs/tasks",
3607                        }),
3608                        message,
3609                    });
3610                }
3611                Ok(path) => cx.emit(Event::HideToast {
3612                    notification_id: format!("local-tasks-{path:?}").into(),
3613                }),
3614                Err(_) => {}
3615            },
3616            SettingsObserverEvent::LocalDebugScenariosUpdated(result) => match result {
3617                Err(InvalidSettingsError::Debug { message, path }) => {
3618                    let message =
3619                        format!("Failed to set local debug scenarios in {path:?}:\n{message}");
3620                    cx.emit(Event::Toast {
3621                        notification_id: format!("local-debug-scenarios-{path:?}").into(),
3622                        link: None,
3623                        message,
3624                    });
3625                }
3626                Ok(path) => cx.emit(Event::HideToast {
3627                    notification_id: format!("local-debug-scenarios-{path:?}").into(),
3628                }),
3629                Err(_) => {}
3630            },
3631        }
3632    }
3633
3634    fn on_worktree_store_event(
3635        &mut self,
3636        _: Entity<WorktreeStore>,
3637        event: &WorktreeStoreEvent,
3638        cx: &mut Context<Self>,
3639    ) {
3640        match event {
3641            WorktreeStoreEvent::WorktreeAdded(worktree) => {
3642                self.on_worktree_added(worktree, cx);
3643                cx.emit(Event::WorktreeAdded(worktree.read(cx).id()));
3644            }
3645            WorktreeStoreEvent::WorktreeRemoved(_, id) => {
3646                cx.emit(Event::WorktreeRemoved(*id));
3647            }
3648            WorktreeStoreEvent::WorktreeReleased(_, id) => {
3649                self.on_worktree_released(*id, cx);
3650            }
3651            WorktreeStoreEvent::WorktreeOrderChanged => cx.emit(Event::WorktreeOrderChanged),
3652            WorktreeStoreEvent::WorktreeUpdateSent(_) => {}
3653            WorktreeStoreEvent::WorktreeUpdatedEntries(worktree_id, changes) => {
3654                self.client()
3655                    .telemetry()
3656                    .report_discovered_project_type_events(*worktree_id, changes);
3657                cx.emit(Event::WorktreeUpdatedEntries(*worktree_id, changes.clone()))
3658            }
3659            WorktreeStoreEvent::WorktreeDeletedEntry(worktree_id, id) => {
3660                cx.emit(Event::DeletedEntry(*worktree_id, *id))
3661            }
3662            // Listen to the GitStore instead.
3663            WorktreeStoreEvent::WorktreeUpdatedGitRepositories(_, _) => {}
3664        }
3665    }
3666
3667    fn on_worktree_added(&mut self, worktree: &Entity<Worktree>, _: &mut Context<Self>) {
3668        let mut remotely_created_models = self.remotely_created_models.lock();
3669        if remotely_created_models.retain_count > 0 {
3670            remotely_created_models.worktrees.push(worktree.clone())
3671        }
3672    }
3673
3674    fn on_worktree_released(&mut self, id_to_remove: WorktreeId, cx: &mut Context<Self>) {
3675        if let Some(remote) = &self.remote_client {
3676            remote
3677                .read(cx)
3678                .proto_client()
3679                .send(proto::RemoveWorktree {
3680                    worktree_id: id_to_remove.to_proto(),
3681                })
3682                .log_err();
3683        }
3684    }
3685
3686    fn on_buffer_event(
3687        &mut self,
3688        buffer: Entity<Buffer>,
3689        event: &BufferEvent,
3690        cx: &mut Context<Self>,
3691    ) -> Option<()> {
3692        if matches!(event, BufferEvent::Edited { .. } | BufferEvent::Reloaded) {
3693            self.request_buffer_diff_recalculation(&buffer, cx);
3694        }
3695
3696        if matches!(event, BufferEvent::Edited { .. }) {
3697            cx.emit(Event::BufferEdited);
3698        }
3699
3700        let buffer_id = buffer.read(cx).remote_id();
3701        match event {
3702            BufferEvent::ReloadNeeded => {
3703                if !self.is_via_collab() {
3704                    self.reload_buffers([buffer.clone()].into_iter().collect(), true, cx)
3705                        .detach_and_log_err(cx);
3706                }
3707            }
3708            BufferEvent::Operation {
3709                operation,
3710                is_local: true,
3711            } => {
3712                let operation = language::proto::serialize_operation(operation);
3713
3714                if let Some(remote) = &self.remote_client {
3715                    remote
3716                        .read(cx)
3717                        .proto_client()
3718                        .send(proto::UpdateBuffer {
3719                            project_id: 0,
3720                            buffer_id: buffer_id.to_proto(),
3721                            operations: vec![operation.clone()],
3722                        })
3723                        .ok();
3724                }
3725
3726                self.enqueue_buffer_ordered_message(BufferOrderedMessage::Operation {
3727                    buffer_id,
3728                    operation,
3729                })
3730                .ok();
3731            }
3732
3733            _ => {}
3734        }
3735
3736        None
3737    }
3738
3739    fn on_image_event(
3740        &mut self,
3741        image: Entity<ImageItem>,
3742        event: &ImageItemEvent,
3743        cx: &mut Context<Self>,
3744    ) -> Option<()> {
3745        // TODO: handle image events from remote
3746        if let ImageItemEvent::ReloadNeeded = event
3747            && !self.is_via_collab()
3748        {
3749            self.reload_images([image].into_iter().collect(), cx)
3750                .detach_and_log_err(cx);
3751        }
3752
3753        None
3754    }
3755
3756    fn request_buffer_diff_recalculation(
3757        &mut self,
3758        buffer: &Entity<Buffer>,
3759        cx: &mut Context<Self>,
3760    ) {
3761        self.buffers_needing_diff.insert(buffer.downgrade());
3762        let first_insertion = self.buffers_needing_diff.len() == 1;
3763        let settings = ProjectSettings::get_global(cx);
3764        let delay = settings.git.gutter_debounce;
3765
3766        if delay == 0 {
3767            if first_insertion {
3768                let this = cx.weak_entity();
3769                cx.defer(move |cx| {
3770                    if let Some(this) = this.upgrade() {
3771                        this.update(cx, |this, cx| {
3772                            this.recalculate_buffer_diffs(cx).detach();
3773                        });
3774                    }
3775                });
3776            }
3777            return;
3778        }
3779
3780        const MIN_DELAY: u64 = 50;
3781        let delay = delay.max(MIN_DELAY);
3782        let duration = Duration::from_millis(delay);
3783
3784        self.git_diff_debouncer
3785            .fire_new(duration, cx, move |this, cx| {
3786                this.recalculate_buffer_diffs(cx)
3787            });
3788    }
3789
3790    fn recalculate_buffer_diffs(&mut self, cx: &mut Context<Self>) -> Task<()> {
3791        cx.spawn(async move |this, cx| {
3792            loop {
3793                let task = this
3794                    .update(cx, |this, cx| {
3795                        let buffers = this
3796                            .buffers_needing_diff
3797                            .drain()
3798                            .filter_map(|buffer| buffer.upgrade())
3799                            .collect::<Vec<_>>();
3800                        if buffers.is_empty() {
3801                            None
3802                        } else {
3803                            Some(this.git_store.update(cx, |git_store, cx| {
3804                                git_store.recalculate_buffer_diffs(buffers, cx)
3805                            }))
3806                        }
3807                    })
3808                    .ok()
3809                    .flatten();
3810
3811                if let Some(task) = task {
3812                    task.await;
3813                } else {
3814                    break;
3815                }
3816            }
3817        })
3818    }
3819
3820    pub fn set_language_for_buffer(
3821        &mut self,
3822        buffer: &Entity<Buffer>,
3823        new_language: Arc<Language>,
3824        cx: &mut Context<Self>,
3825    ) {
3826        self.lsp_store.update(cx, |lsp_store, cx| {
3827            lsp_store.set_language_for_buffer(buffer, new_language, cx)
3828        })
3829    }
3830
3831    pub fn restart_language_servers_for_buffers(
3832        &mut self,
3833        buffers: Vec<Entity<Buffer>>,
3834        only_restart_servers: HashSet<LanguageServerSelector>,
3835        cx: &mut Context<Self>,
3836    ) {
3837        self.lsp_store.update(cx, |lsp_store, cx| {
3838            lsp_store.restart_language_servers_for_buffers(buffers, only_restart_servers, cx)
3839        })
3840    }
3841
3842    pub fn stop_language_servers_for_buffers(
3843        &mut self,
3844        buffers: Vec<Entity<Buffer>>,
3845        also_restart_servers: HashSet<LanguageServerSelector>,
3846        cx: &mut Context<Self>,
3847    ) {
3848        self.lsp_store
3849            .update(cx, |lsp_store, cx| {
3850                lsp_store.stop_language_servers_for_buffers(buffers, also_restart_servers, cx)
3851            })
3852            .detach_and_log_err(cx);
3853    }
3854
3855    pub fn cancel_language_server_work_for_buffers(
3856        &mut self,
3857        buffers: impl IntoIterator<Item = Entity<Buffer>>,
3858        cx: &mut Context<Self>,
3859    ) {
3860        self.lsp_store.update(cx, |lsp_store, cx| {
3861            lsp_store.cancel_language_server_work_for_buffers(buffers, cx)
3862        })
3863    }
3864
3865    pub fn cancel_language_server_work(
3866        &mut self,
3867        server_id: LanguageServerId,
3868        token_to_cancel: Option<ProgressToken>,
3869        cx: &mut Context<Self>,
3870    ) {
3871        self.lsp_store.update(cx, |lsp_store, cx| {
3872            lsp_store.cancel_language_server_work(server_id, token_to_cancel, cx)
3873        })
3874    }
3875
3876    fn enqueue_buffer_ordered_message(&mut self, message: BufferOrderedMessage) -> Result<()> {
3877        self.buffer_ordered_messages_tx
3878            .unbounded_send(message)
3879            .map_err(|e| anyhow!(e))
3880    }
3881
3882    pub fn available_toolchains(
3883        &self,
3884        path: ProjectPath,
3885        language_name: LanguageName,
3886        cx: &App,
3887    ) -> Task<Option<Toolchains>> {
3888        if let Some(toolchain_store) = self.toolchain_store.as_ref().map(Entity::downgrade) {
3889            cx.spawn(async move |cx| {
3890                toolchain_store
3891                    .update(cx, |this, cx| this.list_toolchains(path, language_name, cx))
3892                    .ok()?
3893                    .await
3894            })
3895        } else {
3896            Task::ready(None)
3897        }
3898    }
3899
3900    pub async fn toolchain_metadata(
3901        languages: Arc<LanguageRegistry>,
3902        language_name: LanguageName,
3903    ) -> Option<ToolchainMetadata> {
3904        languages
3905            .language_for_name(language_name.as_ref())
3906            .await
3907            .ok()?
3908            .toolchain_lister()
3909            .map(|lister| lister.meta())
3910    }
3911
3912    pub fn add_toolchain(
3913        &self,
3914        toolchain: Toolchain,
3915        scope: ToolchainScope,
3916        cx: &mut Context<Self>,
3917    ) {
3918        maybe!({
3919            self.toolchain_store.as_ref()?.update(cx, |this, cx| {
3920                this.add_toolchain(toolchain, scope, cx);
3921            });
3922            Some(())
3923        });
3924    }
3925
3926    pub fn remove_toolchain(
3927        &self,
3928        toolchain: Toolchain,
3929        scope: ToolchainScope,
3930        cx: &mut Context<Self>,
3931    ) {
3932        maybe!({
3933            self.toolchain_store.as_ref()?.update(cx, |this, cx| {
3934                this.remove_toolchain(toolchain, scope, cx);
3935            });
3936            Some(())
3937        });
3938    }
3939
3940    pub fn user_toolchains(
3941        &self,
3942        cx: &App,
3943    ) -> Option<BTreeMap<ToolchainScope, IndexSet<Toolchain>>> {
3944        Some(self.toolchain_store.as_ref()?.read(cx).user_toolchains())
3945    }
3946
3947    pub fn resolve_toolchain(
3948        &self,
3949        path: PathBuf,
3950        language_name: LanguageName,
3951        cx: &App,
3952    ) -> Task<Result<Toolchain>> {
3953        if let Some(toolchain_store) = self.toolchain_store.as_ref().map(Entity::downgrade) {
3954            cx.spawn(async move |cx| {
3955                toolchain_store
3956                    .update(cx, |this, cx| {
3957                        this.resolve_toolchain(path, language_name, cx)
3958                    })?
3959                    .await
3960            })
3961        } else {
3962            Task::ready(Err(anyhow!("This project does not support toolchains")))
3963        }
3964    }
3965
3966    pub fn toolchain_store(&self) -> Option<Entity<ToolchainStore>> {
3967        self.toolchain_store.clone()
3968    }
3969    pub fn activate_toolchain(
3970        &self,
3971        path: ProjectPath,
3972        toolchain: Toolchain,
3973        cx: &mut App,
3974    ) -> Task<Option<()>> {
3975        let Some(toolchain_store) = self.toolchain_store.clone() else {
3976            return Task::ready(None);
3977        };
3978        toolchain_store.update(cx, |this, cx| this.activate_toolchain(path, toolchain, cx))
3979    }
3980    pub fn active_toolchain(
3981        &self,
3982        path: ProjectPath,
3983        language_name: LanguageName,
3984        cx: &App,
3985    ) -> Task<Option<Toolchain>> {
3986        let Some(toolchain_store) = self.toolchain_store.clone() else {
3987            return Task::ready(None);
3988        };
3989        toolchain_store
3990            .read(cx)
3991            .active_toolchain(path, language_name, cx)
3992    }
3993    pub fn language_server_statuses<'a>(
3994        &'a self,
3995        cx: &'a App,
3996    ) -> impl DoubleEndedIterator<Item = (LanguageServerId, &'a LanguageServerStatus)> {
3997        self.lsp_store.read(cx).language_server_statuses()
3998    }
3999
4000    pub fn last_formatting_failure<'a>(&self, cx: &'a App) -> Option<&'a str> {
4001        self.lsp_store.read(cx).last_formatting_failure()
4002    }
4003
4004    pub fn reset_last_formatting_failure(&self, cx: &mut App) {
4005        self.lsp_store
4006            .update(cx, |store, _| store.reset_last_formatting_failure());
4007    }
4008
4009    pub fn reload_buffers(
4010        &self,
4011        buffers: HashSet<Entity<Buffer>>,
4012        push_to_history: bool,
4013        cx: &mut Context<Self>,
4014    ) -> Task<Result<ProjectTransaction>> {
4015        self.buffer_store.update(cx, |buffer_store, cx| {
4016            buffer_store.reload_buffers(buffers, push_to_history, cx)
4017        })
4018    }
4019
4020    pub fn reload_images(
4021        &self,
4022        images: HashSet<Entity<ImageItem>>,
4023        cx: &mut Context<Self>,
4024    ) -> Task<Result<()>> {
4025        self.image_store
4026            .update(cx, |image_store, cx| image_store.reload_images(images, cx))
4027    }
4028
4029    pub fn format(
4030        &mut self,
4031        buffers: HashSet<Entity<Buffer>>,
4032        target: LspFormatTarget,
4033        push_to_history: bool,
4034        trigger: lsp_store::FormatTrigger,
4035        cx: &mut Context<Project>,
4036    ) -> Task<anyhow::Result<ProjectTransaction>> {
4037        self.lsp_store.update(cx, |lsp_store, cx| {
4038            lsp_store.format(buffers, target, push_to_history, trigger, cx)
4039        })
4040    }
4041
4042    pub fn definitions<T: ToPointUtf16>(
4043        &mut self,
4044        buffer: &Entity<Buffer>,
4045        position: T,
4046        cx: &mut Context<Self>,
4047    ) -> Task<Result<Option<Vec<LocationLink>>>> {
4048        let position = position.to_point_utf16(buffer.read(cx));
4049        let guard = self.retain_remotely_created_models(cx);
4050        let task = self.lsp_store.update(cx, |lsp_store, cx| {
4051            lsp_store.definitions(buffer, position, cx)
4052        });
4053        cx.background_spawn(async move {
4054            let result = task.await;
4055            drop(guard);
4056            result
4057        })
4058    }
4059
4060    pub fn declarations<T: ToPointUtf16>(
4061        &mut self,
4062        buffer: &Entity<Buffer>,
4063        position: T,
4064        cx: &mut Context<Self>,
4065    ) -> Task<Result<Option<Vec<LocationLink>>>> {
4066        let position = position.to_point_utf16(buffer.read(cx));
4067        let guard = self.retain_remotely_created_models(cx);
4068        let task = self.lsp_store.update(cx, |lsp_store, cx| {
4069            lsp_store.declarations(buffer, position, cx)
4070        });
4071        cx.background_spawn(async move {
4072            let result = task.await;
4073            drop(guard);
4074            result
4075        })
4076    }
4077
4078    pub fn type_definitions<T: ToPointUtf16>(
4079        &mut self,
4080        buffer: &Entity<Buffer>,
4081        position: T,
4082        cx: &mut Context<Self>,
4083    ) -> Task<Result<Option<Vec<LocationLink>>>> {
4084        let position = position.to_point_utf16(buffer.read(cx));
4085        let guard = self.retain_remotely_created_models(cx);
4086        let task = self.lsp_store.update(cx, |lsp_store, cx| {
4087            lsp_store.type_definitions(buffer, position, cx)
4088        });
4089        cx.background_spawn(async move {
4090            let result = task.await;
4091            drop(guard);
4092            result
4093        })
4094    }
4095
4096    pub fn implementations<T: ToPointUtf16>(
4097        &mut self,
4098        buffer: &Entity<Buffer>,
4099        position: T,
4100        cx: &mut Context<Self>,
4101    ) -> Task<Result<Option<Vec<LocationLink>>>> {
4102        let position = position.to_point_utf16(buffer.read(cx));
4103        let guard = self.retain_remotely_created_models(cx);
4104        let task = self.lsp_store.update(cx, |lsp_store, cx| {
4105            lsp_store.implementations(buffer, position, cx)
4106        });
4107        cx.background_spawn(async move {
4108            let result = task.await;
4109            drop(guard);
4110            result
4111        })
4112    }
4113
4114    pub fn references<T: ToPointUtf16>(
4115        &mut self,
4116        buffer: &Entity<Buffer>,
4117        position: T,
4118        cx: &mut Context<Self>,
4119    ) -> Task<Result<Option<Vec<Location>>>> {
4120        let position = position.to_point_utf16(buffer.read(cx));
4121        let guard = self.retain_remotely_created_models(cx);
4122        let task = self.lsp_store.update(cx, |lsp_store, cx| {
4123            lsp_store.references(buffer, position, cx)
4124        });
4125        cx.background_spawn(async move {
4126            let result = task.await;
4127            drop(guard);
4128            result
4129        })
4130    }
4131
4132    pub fn document_highlights<T: ToPointUtf16>(
4133        &mut self,
4134        buffer: &Entity<Buffer>,
4135        position: T,
4136        cx: &mut Context<Self>,
4137    ) -> Task<Result<Vec<DocumentHighlight>>> {
4138        let position = position.to_point_utf16(buffer.read(cx));
4139        self.request_lsp(
4140            buffer.clone(),
4141            LanguageServerToQuery::FirstCapable,
4142            GetDocumentHighlights { position },
4143            cx,
4144        )
4145    }
4146
4147    pub fn document_symbols(
4148        &mut self,
4149        buffer: &Entity<Buffer>,
4150        cx: &mut Context<Self>,
4151    ) -> Task<Result<Vec<DocumentSymbol>>> {
4152        self.request_lsp(
4153            buffer.clone(),
4154            LanguageServerToQuery::FirstCapable,
4155            GetDocumentSymbols,
4156            cx,
4157        )
4158    }
4159
4160    pub fn symbols(&self, query: &str, cx: &mut Context<Self>) -> Task<Result<Vec<Symbol>>> {
4161        self.lsp_store
4162            .update(cx, |lsp_store, cx| lsp_store.symbols(query, cx))
4163    }
4164
4165    pub fn open_buffer_for_symbol(
4166        &mut self,
4167        symbol: &Symbol,
4168        cx: &mut Context<Self>,
4169    ) -> Task<Result<Entity<Buffer>>> {
4170        self.lsp_store.update(cx, |lsp_store, cx| {
4171            lsp_store.open_buffer_for_symbol(symbol, cx)
4172        })
4173    }
4174
4175    pub fn open_server_settings(&mut self, cx: &mut Context<Self>) -> Task<Result<Entity<Buffer>>> {
4176        let guard = self.retain_remotely_created_models(cx);
4177        let Some(remote) = self.remote_client.as_ref() else {
4178            return Task::ready(Err(anyhow!("not an ssh project")));
4179        };
4180
4181        let proto_client = remote.read(cx).proto_client();
4182
4183        cx.spawn(async move |project, cx| {
4184            let buffer = proto_client
4185                .request(proto::OpenServerSettings {
4186                    project_id: REMOTE_SERVER_PROJECT_ID,
4187                })
4188                .await?;
4189
4190            let buffer = project
4191                .update(cx, |project, cx| {
4192                    project.buffer_store.update(cx, |buffer_store, cx| {
4193                        anyhow::Ok(
4194                            buffer_store
4195                                .wait_for_remote_buffer(BufferId::new(buffer.buffer_id)?, cx),
4196                        )
4197                    })
4198                })??
4199                .await;
4200
4201            drop(guard);
4202            buffer
4203        })
4204    }
4205
4206    pub fn open_local_buffer_via_lsp(
4207        &mut self,
4208        abs_path: lsp::Uri,
4209        language_server_id: LanguageServerId,
4210        cx: &mut Context<Self>,
4211    ) -> Task<Result<Entity<Buffer>>> {
4212        self.lsp_store.update(cx, |lsp_store, cx| {
4213            lsp_store.open_local_buffer_via_lsp(abs_path, language_server_id, cx)
4214        })
4215    }
4216
4217    pub fn hover<T: ToPointUtf16>(
4218        &self,
4219        buffer: &Entity<Buffer>,
4220        position: T,
4221        cx: &mut Context<Self>,
4222    ) -> Task<Option<Vec<Hover>>> {
4223        let position = position.to_point_utf16(buffer.read(cx));
4224        self.lsp_store
4225            .update(cx, |lsp_store, cx| lsp_store.hover(buffer, position, cx))
4226    }
4227
4228    pub fn linked_edits(
4229        &self,
4230        buffer: &Entity<Buffer>,
4231        position: Anchor,
4232        cx: &mut Context<Self>,
4233    ) -> Task<Result<Vec<Range<Anchor>>>> {
4234        self.lsp_store.update(cx, |lsp_store, cx| {
4235            lsp_store.linked_edits(buffer, position, cx)
4236        })
4237    }
4238
4239    pub fn completions<T: ToOffset + ToPointUtf16>(
4240        &self,
4241        buffer: &Entity<Buffer>,
4242        position: T,
4243        context: CompletionContext,
4244        cx: &mut Context<Self>,
4245    ) -> Task<Result<Vec<CompletionResponse>>> {
4246        let position = position.to_point_utf16(buffer.read(cx));
4247        self.lsp_store.update(cx, |lsp_store, cx| {
4248            lsp_store.completions(buffer, position, context, cx)
4249        })
4250    }
4251
4252    pub fn code_actions<T: Clone + ToOffset>(
4253        &mut self,
4254        buffer_handle: &Entity<Buffer>,
4255        range: Range<T>,
4256        kinds: Option<Vec<CodeActionKind>>,
4257        cx: &mut Context<Self>,
4258    ) -> Task<Result<Option<Vec<CodeAction>>>> {
4259        let buffer = buffer_handle.read(cx);
4260        let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
4261        self.lsp_store.update(cx, |lsp_store, cx| {
4262            lsp_store.code_actions(buffer_handle, range, kinds, cx)
4263        })
4264    }
4265
4266    pub fn code_lens_actions<T: Clone + ToOffset>(
4267        &mut self,
4268        buffer: &Entity<Buffer>,
4269        range: Range<T>,
4270        cx: &mut Context<Self>,
4271    ) -> Task<Result<Option<Vec<CodeAction>>>> {
4272        let snapshot = buffer.read(cx).snapshot();
4273        let range = range.to_point(&snapshot);
4274        let range_start = snapshot.anchor_before(range.start);
4275        let range_end = if range.start == range.end {
4276            range_start
4277        } else {
4278            snapshot.anchor_after(range.end)
4279        };
4280        let range = range_start..range_end;
4281        let code_lens_actions = self
4282            .lsp_store
4283            .update(cx, |lsp_store, cx| lsp_store.code_lens_actions(buffer, cx));
4284
4285        cx.background_spawn(async move {
4286            let mut code_lens_actions = code_lens_actions
4287                .await
4288                .map_err(|e| anyhow!("code lens fetch failed: {e:#}"))?;
4289            if let Some(code_lens_actions) = &mut code_lens_actions {
4290                code_lens_actions.retain(|code_lens_action| {
4291                    range
4292                        .start
4293                        .cmp(&code_lens_action.range.start, &snapshot)
4294                        .is_ge()
4295                        && range
4296                            .end
4297                            .cmp(&code_lens_action.range.end, &snapshot)
4298                            .is_le()
4299                });
4300            }
4301            Ok(code_lens_actions)
4302        })
4303    }
4304
4305    pub fn apply_code_action(
4306        &self,
4307        buffer_handle: Entity<Buffer>,
4308        action: CodeAction,
4309        push_to_history: bool,
4310        cx: &mut Context<Self>,
4311    ) -> Task<Result<ProjectTransaction>> {
4312        self.lsp_store.update(cx, |lsp_store, cx| {
4313            lsp_store.apply_code_action(buffer_handle, action, push_to_history, cx)
4314        })
4315    }
4316
4317    pub fn apply_code_action_kind(
4318        &self,
4319        buffers: HashSet<Entity<Buffer>>,
4320        kind: CodeActionKind,
4321        push_to_history: bool,
4322        cx: &mut Context<Self>,
4323    ) -> Task<Result<ProjectTransaction>> {
4324        self.lsp_store.update(cx, |lsp_store, cx| {
4325            lsp_store.apply_code_action_kind(buffers, kind, push_to_history, cx)
4326        })
4327    }
4328
4329    pub fn prepare_rename<T: ToPointUtf16>(
4330        &mut self,
4331        buffer: Entity<Buffer>,
4332        position: T,
4333        cx: &mut Context<Self>,
4334    ) -> Task<Result<PrepareRenameResponse>> {
4335        let position = position.to_point_utf16(buffer.read(cx));
4336        self.request_lsp(
4337            buffer,
4338            LanguageServerToQuery::FirstCapable,
4339            PrepareRename { position },
4340            cx,
4341        )
4342    }
4343
4344    pub fn perform_rename<T: ToPointUtf16>(
4345        &mut self,
4346        buffer: Entity<Buffer>,
4347        position: T,
4348        new_name: String,
4349        cx: &mut Context<Self>,
4350    ) -> Task<Result<ProjectTransaction>> {
4351        let push_to_history = true;
4352        let position = position.to_point_utf16(buffer.read(cx));
4353        self.request_lsp(
4354            buffer,
4355            LanguageServerToQuery::FirstCapable,
4356            PerformRename {
4357                position,
4358                new_name,
4359                push_to_history,
4360            },
4361            cx,
4362        )
4363    }
4364
4365    pub fn on_type_format<T: ToPointUtf16>(
4366        &mut self,
4367        buffer: Entity<Buffer>,
4368        position: T,
4369        trigger: String,
4370        push_to_history: bool,
4371        cx: &mut Context<Self>,
4372    ) -> Task<Result<Option<Transaction>>> {
4373        self.lsp_store.update(cx, |lsp_store, cx| {
4374            lsp_store.on_type_format(buffer, position, trigger, push_to_history, cx)
4375        })
4376    }
4377
4378    pub fn inline_values(
4379        &mut self,
4380        session: Entity<Session>,
4381        active_stack_frame: ActiveStackFrame,
4382        buffer_handle: Entity<Buffer>,
4383        range: Range<text::Anchor>,
4384        cx: &mut Context<Self>,
4385    ) -> Task<anyhow::Result<Vec<InlayHint>>> {
4386        let snapshot = buffer_handle.read(cx).snapshot();
4387
4388        let captures =
4389            snapshot.debug_variables_query(Anchor::min_for_buffer(snapshot.remote_id())..range.end);
4390
4391        let row = snapshot
4392            .summary_for_anchor::<text::PointUtf16>(&range.end)
4393            .row as usize;
4394
4395        let inline_value_locations = provide_inline_values(captures, &snapshot, row);
4396
4397        let stack_frame_id = active_stack_frame.stack_frame_id;
4398        cx.spawn(async move |this, cx| {
4399            this.update(cx, |project, cx| {
4400                project.dap_store().update(cx, |dap_store, cx| {
4401                    dap_store.resolve_inline_value_locations(
4402                        session,
4403                        stack_frame_id,
4404                        buffer_handle,
4405                        inline_value_locations,
4406                        cx,
4407                    )
4408                })
4409            })?
4410            .await
4411        })
4412    }
4413
4414    fn search_impl(&mut self, query: SearchQuery, cx: &mut Context<Self>) -> SearchResultsHandle {
4415        let client: Option<(AnyProtoClient, _)> = if let Some(ssh_client) = &self.remote_client {
4416            Some((ssh_client.read(cx).proto_client(), 0))
4417        } else if let Some(remote_id) = self.remote_id() {
4418            self.is_local()
4419                .not()
4420                .then(|| (self.collab_client.clone().into(), remote_id))
4421        } else {
4422            None
4423        };
4424        let searcher = if query.is_opened_only() {
4425            project_search::Search::open_buffers_only(
4426                self.buffer_store.clone(),
4427                self.worktree_store.clone(),
4428                project_search::Search::MAX_SEARCH_RESULT_FILES + 1,
4429            )
4430        } else {
4431            match client {
4432                Some((client, remote_id)) => project_search::Search::remote(
4433                    self.buffer_store.clone(),
4434                    self.worktree_store.clone(),
4435                    project_search::Search::MAX_SEARCH_RESULT_FILES + 1,
4436                    (client, remote_id, self.remotely_created_models.clone()),
4437                ),
4438                None => project_search::Search::local(
4439                    self.fs.clone(),
4440                    self.buffer_store.clone(),
4441                    self.worktree_store.clone(),
4442                    project_search::Search::MAX_SEARCH_RESULT_FILES + 1,
4443                    cx,
4444                ),
4445            }
4446        };
4447        searcher.into_handle(query, cx)
4448    }
4449
4450    pub fn search(
4451        &mut self,
4452        query: SearchQuery,
4453        cx: &mut Context<Self>,
4454    ) -> SearchResults<SearchResult> {
4455        self.search_impl(query, cx).results(cx)
4456    }
4457
4458    pub fn request_lsp<R: LspCommand>(
4459        &mut self,
4460        buffer_handle: Entity<Buffer>,
4461        server: LanguageServerToQuery,
4462        request: R,
4463        cx: &mut Context<Self>,
4464    ) -> Task<Result<R::Response>>
4465    where
4466        <R::LspRequest as lsp::request::Request>::Result: Send,
4467        <R::LspRequest as lsp::request::Request>::Params: Send,
4468    {
4469        let guard = self.retain_remotely_created_models(cx);
4470        let task = self.lsp_store.update(cx, |lsp_store, cx| {
4471            lsp_store.request_lsp(buffer_handle, server, request, cx)
4472        });
4473        cx.background_spawn(async move {
4474            let result = task.await;
4475            drop(guard);
4476            result
4477        })
4478    }
4479
4480    /// Move a worktree to a new position in the worktree order.
4481    ///
4482    /// The worktree will moved to the opposite side of the destination worktree.
4483    ///
4484    /// # Example
4485    ///
4486    /// Given the worktree order `[11, 22, 33]` and a call to move worktree `22` to `33`,
4487    /// worktree_order will be updated to produce the indexes `[11, 33, 22]`.
4488    ///
4489    /// Given the worktree order `[11, 22, 33]` and a call to move worktree `22` to `11`,
4490    /// worktree_order will be updated to produce the indexes `[22, 11, 33]`.
4491    ///
4492    /// # Errors
4493    ///
4494    /// An error will be returned if the worktree or destination worktree are not found.
4495    pub fn move_worktree(
4496        &mut self,
4497        source: WorktreeId,
4498        destination: WorktreeId,
4499        cx: &mut Context<Self>,
4500    ) -> Result<()> {
4501        self.worktree_store.update(cx, |worktree_store, cx| {
4502            worktree_store.move_worktree(source, destination, cx)
4503        })
4504    }
4505
4506    /// Attempts to convert the input path to a WSL path if this is a wsl remote project and the input path is a host windows path.
4507    pub fn try_windows_path_to_wsl(
4508        &self,
4509        abs_path: &Path,
4510        cx: &App,
4511    ) -> impl Future<Output = Result<PathBuf>> + use<> {
4512        let fut = if cfg!(windows)
4513            && let (
4514                ProjectClientState::Local | ProjectClientState::Shared { .. },
4515                Some(remote_client),
4516            ) = (&self.client_state, &self.remote_client)
4517            && let RemoteConnectionOptions::Wsl(wsl) = remote_client.read(cx).connection_options()
4518        {
4519            Either::Left(wsl.abs_windows_path_to_wsl_path(abs_path))
4520        } else {
4521            Either::Right(abs_path.to_owned())
4522        };
4523        async move {
4524            match fut {
4525                Either::Left(fut) => fut.await.map(Into::into),
4526                Either::Right(path) => Ok(path),
4527            }
4528        }
4529    }
4530
4531    pub fn find_or_create_worktree(
4532        &mut self,
4533        abs_path: impl AsRef<Path>,
4534        visible: bool,
4535        cx: &mut Context<Self>,
4536    ) -> Task<Result<(Entity<Worktree>, Arc<RelPath>)>> {
4537        self.worktree_store.update(cx, |worktree_store, cx| {
4538            worktree_store.find_or_create_worktree(abs_path, visible, cx)
4539        })
4540    }
4541
4542    pub fn find_worktree(
4543        &self,
4544        abs_path: &Path,
4545        cx: &App,
4546    ) -> Option<(Entity<Worktree>, Arc<RelPath>)> {
4547        self.worktree_store.read(cx).find_worktree(abs_path, cx)
4548    }
4549
4550    pub fn is_shared(&self) -> bool {
4551        match &self.client_state {
4552            ProjectClientState::Shared { .. } => true,
4553            ProjectClientState::Local => false,
4554            ProjectClientState::Collab { .. } => true,
4555        }
4556    }
4557
4558    /// Returns the resolved version of `path`, that was found in `buffer`, if it exists.
4559    pub fn resolve_path_in_buffer(
4560        &self,
4561        path: &str,
4562        buffer: &Entity<Buffer>,
4563        cx: &mut Context<Self>,
4564    ) -> Task<Option<ResolvedPath>> {
4565        if util::paths::is_absolute(path, self.path_style(cx)) || path.starts_with("~") {
4566            self.resolve_abs_path(path, cx)
4567        } else {
4568            self.resolve_path_in_worktrees(path, buffer, cx)
4569        }
4570    }
4571
4572    pub fn resolve_abs_file_path(
4573        &self,
4574        path: &str,
4575        cx: &mut Context<Self>,
4576    ) -> Task<Option<ResolvedPath>> {
4577        let resolve_task = self.resolve_abs_path(path, cx);
4578        cx.background_spawn(async move {
4579            let resolved_path = resolve_task.await;
4580            resolved_path.filter(|path| path.is_file())
4581        })
4582    }
4583
4584    pub fn resolve_abs_path(&self, path: &str, cx: &App) -> Task<Option<ResolvedPath>> {
4585        if self.is_local() {
4586            let expanded = PathBuf::from(shellexpand::tilde(&path).into_owned());
4587            let fs = self.fs.clone();
4588            cx.background_spawn(async move {
4589                let metadata = fs.metadata(&expanded).await.ok().flatten();
4590
4591                metadata.map(|metadata| ResolvedPath::AbsPath {
4592                    path: expanded.to_string_lossy().into_owned(),
4593                    is_dir: metadata.is_dir,
4594                })
4595            })
4596        } else if let Some(ssh_client) = self.remote_client.as_ref() {
4597            let request = ssh_client
4598                .read(cx)
4599                .proto_client()
4600                .request(proto::GetPathMetadata {
4601                    project_id: REMOTE_SERVER_PROJECT_ID,
4602                    path: path.into(),
4603                });
4604            cx.background_spawn(async move {
4605                let response = request.await.log_err()?;
4606                if response.exists {
4607                    Some(ResolvedPath::AbsPath {
4608                        path: response.path,
4609                        is_dir: response.is_dir,
4610                    })
4611                } else {
4612                    None
4613                }
4614            })
4615        } else {
4616            Task::ready(None)
4617        }
4618    }
4619
4620    fn resolve_path_in_worktrees(
4621        &self,
4622        path: &str,
4623        buffer: &Entity<Buffer>,
4624        cx: &mut Context<Self>,
4625    ) -> Task<Option<ResolvedPath>> {
4626        let mut candidates = vec![];
4627        let path_style = self.path_style(cx);
4628        if let Ok(path) = RelPath::new(path.as_ref(), path_style) {
4629            candidates.push(path.into_arc());
4630        }
4631
4632        if let Some(file) = buffer.read(cx).file()
4633            && let Some(dir) = file.path().parent()
4634        {
4635            if let Some(joined) = path_style.join(&*dir.display(path_style), path)
4636                && let Some(joined) = RelPath::new(joined.as_ref(), path_style).ok()
4637            {
4638                candidates.push(joined.into_arc());
4639            }
4640        }
4641
4642        let buffer_worktree_id = buffer.read(cx).file().map(|file| file.worktree_id(cx));
4643        let worktrees_with_ids: Vec<_> = self
4644            .worktrees(cx)
4645            .map(|worktree| {
4646                let id = worktree.read(cx).id();
4647                (worktree, id)
4648            })
4649            .collect();
4650
4651        cx.spawn(async move |_, cx| {
4652            if let Some(buffer_worktree_id) = buffer_worktree_id
4653                && let Some((worktree, _)) = worktrees_with_ids
4654                    .iter()
4655                    .find(|(_, id)| *id == buffer_worktree_id)
4656            {
4657                for candidate in candidates.iter() {
4658                    if let Some(path) = Self::resolve_path_in_worktree(worktree, candidate, cx) {
4659                        return Some(path);
4660                    }
4661                }
4662            }
4663            for (worktree, id) in worktrees_with_ids {
4664                if Some(id) == buffer_worktree_id {
4665                    continue;
4666                }
4667                for candidate in candidates.iter() {
4668                    if let Some(path) = Self::resolve_path_in_worktree(&worktree, candidate, cx) {
4669                        return Some(path);
4670                    }
4671                }
4672            }
4673            None
4674        })
4675    }
4676
4677    fn resolve_path_in_worktree(
4678        worktree: &Entity<Worktree>,
4679        path: &RelPath,
4680        cx: &mut AsyncApp,
4681    ) -> Option<ResolvedPath> {
4682        worktree.read_with(cx, |worktree, _| {
4683            worktree.entry_for_path(path).map(|entry| {
4684                let project_path = ProjectPath {
4685                    worktree_id: worktree.id(),
4686                    path: entry.path.clone(),
4687                };
4688                ResolvedPath::ProjectPath {
4689                    project_path,
4690                    is_dir: entry.is_dir(),
4691                }
4692            })
4693        })
4694    }
4695
4696    pub fn list_directory(
4697        &self,
4698        query: String,
4699        cx: &mut Context<Self>,
4700    ) -> Task<Result<Vec<DirectoryItem>>> {
4701        if self.is_local() {
4702            DirectoryLister::Local(cx.entity(), self.fs.clone()).list_directory(query, cx)
4703        } else if let Some(session) = self.remote_client.as_ref() {
4704            let request = proto::ListRemoteDirectory {
4705                dev_server_id: REMOTE_SERVER_PROJECT_ID,
4706                path: query,
4707                config: Some(proto::ListRemoteDirectoryConfig { is_dir: true }),
4708            };
4709
4710            let response = session.read(cx).proto_client().request(request);
4711            cx.background_spawn(async move {
4712                let proto::ListRemoteDirectoryResponse {
4713                    entries,
4714                    entry_info,
4715                } = response.await?;
4716                Ok(entries
4717                    .into_iter()
4718                    .zip(entry_info)
4719                    .map(|(entry, info)| DirectoryItem {
4720                        path: PathBuf::from(entry),
4721                        is_dir: info.is_dir,
4722                    })
4723                    .collect())
4724            })
4725        } else {
4726            Task::ready(Err(anyhow!("cannot list directory in remote project")))
4727        }
4728    }
4729
4730    pub fn create_worktree(
4731        &mut self,
4732        abs_path: impl AsRef<Path>,
4733        visible: bool,
4734        cx: &mut Context<Self>,
4735    ) -> Task<Result<Entity<Worktree>>> {
4736        self.worktree_store.update(cx, |worktree_store, cx| {
4737            worktree_store.create_worktree(abs_path, visible, cx)
4738        })
4739    }
4740
4741    pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut Context<Self>) {
4742        self.worktree_store.update(cx, |worktree_store, cx| {
4743            worktree_store.remove_worktree(id_to_remove, cx);
4744        });
4745    }
4746
4747    fn add_worktree(&mut self, worktree: &Entity<Worktree>, cx: &mut Context<Self>) {
4748        self.worktree_store.update(cx, |worktree_store, cx| {
4749            worktree_store.add(worktree, cx);
4750        });
4751    }
4752
4753    pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut Context<Self>) {
4754        let new_active_entry = entry.and_then(|project_path| {
4755            let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
4756            let entry = worktree.read(cx).entry_for_path(&project_path.path)?;
4757            Some(entry.id)
4758        });
4759        if new_active_entry != self.active_entry {
4760            self.active_entry = new_active_entry;
4761            self.lsp_store.update(cx, |lsp_store, _| {
4762                lsp_store.set_active_entry(new_active_entry);
4763            });
4764            cx.emit(Event::ActiveEntryChanged(new_active_entry));
4765        }
4766    }
4767
4768    pub fn language_servers_running_disk_based_diagnostics<'a>(
4769        &'a self,
4770        cx: &'a App,
4771    ) -> impl Iterator<Item = LanguageServerId> + 'a {
4772        self.lsp_store
4773            .read(cx)
4774            .language_servers_running_disk_based_diagnostics()
4775    }
4776
4777    pub fn diagnostic_summary(&self, include_ignored: bool, cx: &App) -> DiagnosticSummary {
4778        self.lsp_store
4779            .read(cx)
4780            .diagnostic_summary(include_ignored, cx)
4781    }
4782
4783    /// Returns a summary of the diagnostics for the provided project path only.
4784    pub fn diagnostic_summary_for_path(&self, path: &ProjectPath, cx: &App) -> DiagnosticSummary {
4785        self.lsp_store
4786            .read(cx)
4787            .diagnostic_summary_for_path(path, cx)
4788    }
4789
4790    pub fn diagnostic_summaries<'a>(
4791        &'a self,
4792        include_ignored: bool,
4793        cx: &'a App,
4794    ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
4795        self.lsp_store
4796            .read(cx)
4797            .diagnostic_summaries(include_ignored, cx)
4798    }
4799
4800    pub fn active_entry(&self) -> Option<ProjectEntryId> {
4801        self.active_entry
4802    }
4803
4804    pub fn entry_for_path<'a>(&'a self, path: &ProjectPath, cx: &'a App) -> Option<&'a Entry> {
4805        self.worktree_store.read(cx).entry_for_path(path, cx)
4806    }
4807
4808    pub fn path_for_entry(&self, entry_id: ProjectEntryId, cx: &App) -> Option<ProjectPath> {
4809        let worktree = self.worktree_for_entry(entry_id, cx)?;
4810        let worktree = worktree.read(cx);
4811        let worktree_id = worktree.id();
4812        let path = worktree.entry_for_id(entry_id)?.path.clone();
4813        Some(ProjectPath { worktree_id, path })
4814    }
4815
4816    pub fn absolute_path(&self, project_path: &ProjectPath, cx: &App) -> Option<PathBuf> {
4817        Some(
4818            self.worktree_for_id(project_path.worktree_id, cx)?
4819                .read(cx)
4820                .absolutize(&project_path.path),
4821        )
4822    }
4823
4824    /// Attempts to find a `ProjectPath` corresponding to the given path. If the path
4825    /// is a *full path*, meaning it starts with the root name of a worktree, we'll locate
4826    /// it in that worktree. Otherwise, we'll attempt to find it as a relative path in
4827    /// the first visible worktree that has an entry for that relative path.
4828    ///
4829    /// We use this to resolve edit steps, when there's a chance an LLM may omit the workree
4830    /// root name from paths.
4831    ///
4832    /// # Arguments
4833    ///
4834    /// * `path` - An absolute path, or a full path that starts with a worktree root name, or a
4835    ///   relative path within a visible worktree.
4836    /// * `cx` - A reference to the `AppContext`.
4837    ///
4838    /// # Returns
4839    ///
4840    /// Returns `Some(ProjectPath)` if a matching worktree is found, otherwise `None`.
4841    pub fn find_project_path(&self, path: impl AsRef<Path>, cx: &App) -> Option<ProjectPath> {
4842        let path_style = self.path_style(cx);
4843        let path = path.as_ref();
4844        let worktree_store = self.worktree_store.read(cx);
4845
4846        if is_absolute(&path.to_string_lossy(), path_style) {
4847            for worktree in worktree_store.visible_worktrees(cx) {
4848                let worktree_abs_path = worktree.read(cx).abs_path();
4849
4850                if let Ok(relative_path) = path.strip_prefix(worktree_abs_path)
4851                    && let Ok(path) = RelPath::new(relative_path, path_style)
4852                {
4853                    return Some(ProjectPath {
4854                        worktree_id: worktree.read(cx).id(),
4855                        path: path.into_arc(),
4856                    });
4857                }
4858            }
4859        } else {
4860            for worktree in worktree_store.visible_worktrees(cx) {
4861                let worktree = worktree.read(cx);
4862                if let Ok(rel_path) = RelPath::new(path, path_style) {
4863                    if let Some(entry) = worktree.entry_for_path(&rel_path) {
4864                        return Some(ProjectPath {
4865                            worktree_id: worktree.id(),
4866                            path: entry.path.clone(),
4867                        });
4868                    }
4869                }
4870            }
4871
4872            for worktree in worktree_store.visible_worktrees(cx) {
4873                let worktree_root_name = worktree.read(cx).root_name();
4874                if let Ok(relative_path) = path.strip_prefix(worktree_root_name.as_std_path())
4875                    && let Ok(path) = RelPath::new(relative_path, path_style)
4876                {
4877                    return Some(ProjectPath {
4878                        worktree_id: worktree.read(cx).id(),
4879                        path: path.into_arc(),
4880                    });
4881                }
4882            }
4883        }
4884
4885        None
4886    }
4887
4888    /// If there's only one visible worktree, returns the given worktree-relative path with no prefix.
4889    ///
4890    /// Otherwise, returns the full path for the project path (obtained by prefixing the worktree-relative path with the name of the worktree).
4891    pub fn short_full_path_for_project_path(
4892        &self,
4893        project_path: &ProjectPath,
4894        cx: &App,
4895    ) -> Option<String> {
4896        let path_style = self.path_style(cx);
4897        if self.visible_worktrees(cx).take(2).count() < 2 {
4898            return Some(project_path.path.display(path_style).to_string());
4899        }
4900        self.worktree_for_id(project_path.worktree_id, cx)
4901            .map(|worktree| {
4902                let worktree_name = worktree.read(cx).root_name();
4903                worktree_name
4904                    .join(&project_path.path)
4905                    .display(path_style)
4906                    .to_string()
4907            })
4908    }
4909
4910    pub fn project_path_for_absolute_path(&self, abs_path: &Path, cx: &App) -> Option<ProjectPath> {
4911        self.worktree_store
4912            .read(cx)
4913            .project_path_for_absolute_path(abs_path, cx)
4914    }
4915
4916    pub fn get_workspace_root(&self, project_path: &ProjectPath, cx: &App) -> Option<PathBuf> {
4917        Some(
4918            self.worktree_for_id(project_path.worktree_id, cx)?
4919                .read(cx)
4920                .abs_path()
4921                .to_path_buf(),
4922        )
4923    }
4924
4925    pub fn blame_buffer(
4926        &self,
4927        buffer: &Entity<Buffer>,
4928        version: Option<clock::Global>,
4929        cx: &mut App,
4930    ) -> Task<Result<Option<Blame>>> {
4931        self.git_store.update(cx, |git_store, cx| {
4932            git_store.blame_buffer(buffer, version, cx)
4933        })
4934    }
4935
4936    pub fn get_permalink_to_line(
4937        &self,
4938        buffer: &Entity<Buffer>,
4939        selection: Range<u32>,
4940        cx: &mut App,
4941    ) -> Task<Result<url::Url>> {
4942        self.git_store.update(cx, |git_store, cx| {
4943            git_store.get_permalink_to_line(buffer, selection, cx)
4944        })
4945    }
4946
4947    // RPC message handlers
4948
4949    async fn handle_unshare_project(
4950        this: Entity<Self>,
4951        _: TypedEnvelope<proto::UnshareProject>,
4952        mut cx: AsyncApp,
4953    ) -> Result<()> {
4954        this.update(&mut cx, |this, cx| {
4955            if this.is_local() || this.is_via_remote_server() {
4956                this.unshare(cx)?;
4957            } else {
4958                this.disconnected_from_host(cx);
4959            }
4960            Ok(())
4961        })
4962    }
4963
4964    async fn handle_add_collaborator(
4965        this: Entity<Self>,
4966        mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
4967        mut cx: AsyncApp,
4968    ) -> Result<()> {
4969        let collaborator = envelope
4970            .payload
4971            .collaborator
4972            .take()
4973            .context("empty collaborator")?;
4974
4975        let collaborator = Collaborator::from_proto(collaborator)?;
4976        this.update(&mut cx, |this, cx| {
4977            this.buffer_store.update(cx, |buffer_store, _| {
4978                buffer_store.forget_shared_buffers_for(&collaborator.peer_id);
4979            });
4980            this.breakpoint_store.read(cx).broadcast();
4981            cx.emit(Event::CollaboratorJoined(collaborator.peer_id));
4982            this.collaborators
4983                .insert(collaborator.peer_id, collaborator);
4984        });
4985
4986        Ok(())
4987    }
4988
4989    async fn handle_update_project_collaborator(
4990        this: Entity<Self>,
4991        envelope: TypedEnvelope<proto::UpdateProjectCollaborator>,
4992        mut cx: AsyncApp,
4993    ) -> Result<()> {
4994        let old_peer_id = envelope
4995            .payload
4996            .old_peer_id
4997            .context("missing old peer id")?;
4998        let new_peer_id = envelope
4999            .payload
5000            .new_peer_id
5001            .context("missing new peer id")?;
5002        this.update(&mut cx, |this, cx| {
5003            let collaborator = this
5004                .collaborators
5005                .remove(&old_peer_id)
5006                .context("received UpdateProjectCollaborator for unknown peer")?;
5007            let is_host = collaborator.is_host;
5008            this.collaborators.insert(new_peer_id, collaborator);
5009
5010            log::info!("peer {} became {}", old_peer_id, new_peer_id,);
5011            this.buffer_store.update(cx, |buffer_store, _| {
5012                buffer_store.update_peer_id(&old_peer_id, new_peer_id)
5013            });
5014
5015            if is_host {
5016                this.buffer_store
5017                    .update(cx, |buffer_store, _| buffer_store.discard_incomplete());
5018                this.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
5019                    .unwrap();
5020                cx.emit(Event::HostReshared);
5021            }
5022
5023            cx.emit(Event::CollaboratorUpdated {
5024                old_peer_id,
5025                new_peer_id,
5026            });
5027            Ok(())
5028        })
5029    }
5030
5031    async fn handle_remove_collaborator(
5032        this: Entity<Self>,
5033        envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
5034        mut cx: AsyncApp,
5035    ) -> Result<()> {
5036        this.update(&mut cx, |this, cx| {
5037            let peer_id = envelope.payload.peer_id.context("invalid peer id")?;
5038            let replica_id = this
5039                .collaborators
5040                .remove(&peer_id)
5041                .with_context(|| format!("unknown peer {peer_id:?}"))?
5042                .replica_id;
5043            this.buffer_store.update(cx, |buffer_store, cx| {
5044                buffer_store.forget_shared_buffers_for(&peer_id);
5045                for buffer in buffer_store.buffers() {
5046                    buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
5047                }
5048            });
5049            this.git_store.update(cx, |git_store, _| {
5050                git_store.forget_shared_diffs_for(&peer_id);
5051            });
5052
5053            cx.emit(Event::CollaboratorLeft(peer_id));
5054            Ok(())
5055        })
5056    }
5057
5058    async fn handle_update_project(
5059        this: Entity<Self>,
5060        envelope: TypedEnvelope<proto::UpdateProject>,
5061        mut cx: AsyncApp,
5062    ) -> Result<()> {
5063        this.update(&mut cx, |this, cx| {
5064            // Don't handle messages that were sent before the response to us joining the project
5065            if envelope.message_id > this.join_project_response_message_id {
5066                cx.update_global::<SettingsStore, _>(|store, cx| {
5067                    for worktree_metadata in &envelope.payload.worktrees {
5068                        store
5069                            .clear_local_settings(WorktreeId::from_proto(worktree_metadata.id), cx)
5070                            .log_err();
5071                    }
5072                });
5073
5074                this.set_worktrees_from_proto(envelope.payload.worktrees, cx)?;
5075            }
5076            Ok(())
5077        })
5078    }
5079
5080    async fn handle_toast(
5081        this: Entity<Self>,
5082        envelope: TypedEnvelope<proto::Toast>,
5083        mut cx: AsyncApp,
5084    ) -> Result<()> {
5085        this.update(&mut cx, |_, cx| {
5086            cx.emit(Event::Toast {
5087                notification_id: envelope.payload.notification_id.into(),
5088                message: envelope.payload.message,
5089                link: None,
5090            });
5091            Ok(())
5092        })
5093    }
5094
5095    async fn handle_language_server_prompt_request(
5096        this: Entity<Self>,
5097        envelope: TypedEnvelope<proto::LanguageServerPromptRequest>,
5098        mut cx: AsyncApp,
5099    ) -> Result<proto::LanguageServerPromptResponse> {
5100        let (tx, rx) = smol::channel::bounded(1);
5101        let actions: Vec<_> = envelope
5102            .payload
5103            .actions
5104            .into_iter()
5105            .map(|action| MessageActionItem {
5106                title: action,
5107                properties: Default::default(),
5108            })
5109            .collect();
5110        this.update(&mut cx, |_, cx| {
5111            cx.emit(Event::LanguageServerPrompt(
5112                LanguageServerPromptRequest::new(
5113                    proto_to_prompt(envelope.payload.level.context("Invalid prompt level")?),
5114                    envelope.payload.message,
5115                    actions.clone(),
5116                    envelope.payload.lsp_name,
5117                    tx,
5118                ),
5119            ));
5120
5121            anyhow::Ok(())
5122        })?;
5123
5124        // We drop `this` to avoid holding a reference in this future for too
5125        // long.
5126        // If we keep the reference, we might not drop the `Project` early
5127        // enough when closing a window and it will only get releases on the
5128        // next `flush_effects()` call.
5129        drop(this);
5130
5131        let mut rx = pin!(rx);
5132        let answer = rx.next().await;
5133
5134        Ok(LanguageServerPromptResponse {
5135            action_response: answer.and_then(|answer| {
5136                actions
5137                    .iter()
5138                    .position(|action| *action == answer)
5139                    .map(|index| index as u64)
5140            }),
5141        })
5142    }
5143
5144    async fn handle_hide_toast(
5145        this: Entity<Self>,
5146        envelope: TypedEnvelope<proto::HideToast>,
5147        mut cx: AsyncApp,
5148    ) -> Result<()> {
5149        this.update(&mut cx, |_, cx| {
5150            cx.emit(Event::HideToast {
5151                notification_id: envelope.payload.notification_id.into(),
5152            });
5153            Ok(())
5154        })
5155    }
5156
5157    // Collab sends UpdateWorktree protos as messages
5158    async fn handle_update_worktree(
5159        this: Entity<Self>,
5160        envelope: TypedEnvelope<proto::UpdateWorktree>,
5161        mut cx: AsyncApp,
5162    ) -> Result<()> {
5163        this.update(&mut cx, |project, cx| {
5164            let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
5165            if let Some(worktree) = project.worktree_for_id(worktree_id, cx) {
5166                worktree.update(cx, |worktree, _| {
5167                    let worktree = worktree.as_remote_mut().unwrap();
5168                    worktree.update_from_remote(envelope.payload);
5169                });
5170            }
5171            Ok(())
5172        })
5173    }
5174
5175    async fn handle_update_buffer_from_remote_server(
5176        this: Entity<Self>,
5177        envelope: TypedEnvelope<proto::UpdateBuffer>,
5178        cx: AsyncApp,
5179    ) -> Result<proto::Ack> {
5180        let buffer_store = this.read_with(&cx, |this, cx| {
5181            if let Some(remote_id) = this.remote_id() {
5182                let mut payload = envelope.payload.clone();
5183                payload.project_id = remote_id;
5184                cx.background_spawn(this.collab_client.request(payload))
5185                    .detach_and_log_err(cx);
5186            }
5187            this.buffer_store.clone()
5188        });
5189        BufferStore::handle_update_buffer(buffer_store, envelope, cx).await
5190    }
5191
5192    async fn handle_trust_worktrees(
5193        this: Entity<Self>,
5194        envelope: TypedEnvelope<proto::TrustWorktrees>,
5195        mut cx: AsyncApp,
5196    ) -> Result<proto::Ack> {
5197        if this.read_with(&cx, |project, _| project.is_via_collab()) {
5198            return Ok(proto::Ack {});
5199        }
5200
5201        let trusted_worktrees = cx
5202            .update(|cx| TrustedWorktrees::try_get_global(cx))
5203            .context("missing trusted worktrees")?;
5204        trusted_worktrees.update(&mut cx, |trusted_worktrees, cx| {
5205            trusted_worktrees.trust(
5206                &this.read(cx).worktree_store(),
5207                envelope
5208                    .payload
5209                    .trusted_paths
5210                    .into_iter()
5211                    .filter_map(|proto_path| PathTrust::from_proto(proto_path))
5212                    .collect(),
5213                cx,
5214            );
5215        });
5216        Ok(proto::Ack {})
5217    }
5218
5219    async fn handle_restrict_worktrees(
5220        this: Entity<Self>,
5221        envelope: TypedEnvelope<proto::RestrictWorktrees>,
5222        mut cx: AsyncApp,
5223    ) -> Result<proto::Ack> {
5224        if this.read_with(&cx, |project, _| project.is_via_collab()) {
5225            return Ok(proto::Ack {});
5226        }
5227
5228        let trusted_worktrees = cx
5229            .update(|cx| TrustedWorktrees::try_get_global(cx))
5230            .context("missing trusted worktrees")?;
5231        trusted_worktrees.update(&mut cx, |trusted_worktrees, cx| {
5232            let worktree_store = this.read(cx).worktree_store().downgrade();
5233            let restricted_paths = envelope
5234                .payload
5235                .worktree_ids
5236                .into_iter()
5237                .map(WorktreeId::from_proto)
5238                .map(PathTrust::Worktree)
5239                .collect::<HashSet<_>>();
5240            trusted_worktrees.restrict(worktree_store, restricted_paths, cx);
5241        });
5242        Ok(proto::Ack {})
5243    }
5244
5245    // Goes from host to client.
5246    async fn handle_find_search_candidates_chunk(
5247        this: Entity<Self>,
5248        envelope: TypedEnvelope<proto::FindSearchCandidatesChunk>,
5249        mut cx: AsyncApp,
5250    ) -> Result<proto::Ack> {
5251        let buffer_store = this.read_with(&mut cx, |this, _| this.buffer_store.clone());
5252        BufferStore::handle_find_search_candidates_chunk(buffer_store, envelope, cx).await
5253    }
5254
5255    // Goes from client to host.
5256    async fn handle_find_search_candidates_cancel(
5257        this: Entity<Self>,
5258        envelope: TypedEnvelope<proto::FindSearchCandidatesCancelled>,
5259        mut cx: AsyncApp,
5260    ) -> Result<()> {
5261        let buffer_store = this.read_with(&mut cx, |this, _| this.buffer_store.clone());
5262        BufferStore::handle_find_search_candidates_cancel(buffer_store, envelope, cx).await
5263    }
5264
5265    async fn handle_update_buffer(
5266        this: Entity<Self>,
5267        envelope: TypedEnvelope<proto::UpdateBuffer>,
5268        cx: AsyncApp,
5269    ) -> Result<proto::Ack> {
5270        let buffer_store = this.read_with(&cx, |this, cx| {
5271            if let Some(ssh) = &this.remote_client {
5272                let mut payload = envelope.payload.clone();
5273                payload.project_id = REMOTE_SERVER_PROJECT_ID;
5274                cx.background_spawn(ssh.read(cx).proto_client().request(payload))
5275                    .detach_and_log_err(cx);
5276            }
5277            this.buffer_store.clone()
5278        });
5279        BufferStore::handle_update_buffer(buffer_store, envelope, cx).await
5280    }
5281
5282    fn retain_remotely_created_models(
5283        &mut self,
5284        cx: &mut Context<Self>,
5285    ) -> RemotelyCreatedModelGuard {
5286        Self::retain_remotely_created_models_impl(
5287            &self.remotely_created_models,
5288            &self.buffer_store,
5289            &self.worktree_store,
5290            cx,
5291        )
5292    }
5293
5294    fn retain_remotely_created_models_impl(
5295        models: &Arc<Mutex<RemotelyCreatedModels>>,
5296        buffer_store: &Entity<BufferStore>,
5297        worktree_store: &Entity<WorktreeStore>,
5298        cx: &mut App,
5299    ) -> RemotelyCreatedModelGuard {
5300        {
5301            let mut remotely_create_models = models.lock();
5302            if remotely_create_models.retain_count == 0 {
5303                remotely_create_models.buffers = buffer_store.read(cx).buffers().collect();
5304                remotely_create_models.worktrees = worktree_store.read(cx).worktrees().collect();
5305            }
5306            remotely_create_models.retain_count += 1;
5307        }
5308        RemotelyCreatedModelGuard {
5309            remote_models: Arc::downgrade(&models),
5310        }
5311    }
5312
5313    async fn handle_create_buffer_for_peer(
5314        this: Entity<Self>,
5315        envelope: TypedEnvelope<proto::CreateBufferForPeer>,
5316        mut cx: AsyncApp,
5317    ) -> Result<()> {
5318        this.update(&mut cx, |this, cx| {
5319            this.buffer_store.update(cx, |buffer_store, cx| {
5320                buffer_store.handle_create_buffer_for_peer(
5321                    envelope,
5322                    this.replica_id(),
5323                    this.capability(),
5324                    cx,
5325                )
5326            })
5327        })
5328    }
5329
5330    async fn handle_toggle_lsp_logs(
5331        project: Entity<Self>,
5332        envelope: TypedEnvelope<proto::ToggleLspLogs>,
5333        mut cx: AsyncApp,
5334    ) -> Result<()> {
5335        let toggled_log_kind =
5336            match proto::toggle_lsp_logs::LogType::from_i32(envelope.payload.log_type)
5337                .context("invalid log type")?
5338            {
5339                proto::toggle_lsp_logs::LogType::Log => LogKind::Logs,
5340                proto::toggle_lsp_logs::LogType::Trace => LogKind::Trace,
5341                proto::toggle_lsp_logs::LogType::Rpc => LogKind::Rpc,
5342            };
5343        project.update(&mut cx, |_, cx| {
5344            cx.emit(Event::ToggleLspLogs {
5345                server_id: LanguageServerId::from_proto(envelope.payload.server_id),
5346                enabled: envelope.payload.enabled,
5347                toggled_log_kind,
5348            })
5349        });
5350        Ok(())
5351    }
5352
5353    async fn handle_synchronize_buffers(
5354        this: Entity<Self>,
5355        envelope: TypedEnvelope<proto::SynchronizeBuffers>,
5356        mut cx: AsyncApp,
5357    ) -> Result<proto::SynchronizeBuffersResponse> {
5358        let response = this.update(&mut cx, |this, cx| {
5359            let client = this.collab_client.clone();
5360            this.buffer_store.update(cx, |this, cx| {
5361                this.handle_synchronize_buffers(envelope, cx, client)
5362            })
5363        })?;
5364
5365        Ok(response)
5366    }
5367
5368    // Goes from client to host.
5369    async fn handle_search_candidate_buffers(
5370        this: Entity<Self>,
5371        envelope: TypedEnvelope<proto::FindSearchCandidates>,
5372        mut cx: AsyncApp,
5373    ) -> Result<proto::Ack> {
5374        let peer_id = envelope.original_sender_id.unwrap_or(envelope.sender_id);
5375        let message = envelope.payload;
5376        let project_id = message.project_id;
5377        let path_style = this.read_with(&cx, |this, cx| this.path_style(cx));
5378        let query =
5379            SearchQuery::from_proto(message.query.context("missing query field")?, path_style)?;
5380
5381        let handle = message.handle;
5382        let buffer_store = this.read_with(&cx, |this, _| this.buffer_store().clone());
5383        let client = this.read_with(&cx, |this, _| this.client());
5384        let task = cx.spawn(async move |cx| {
5385            let results = this.update(cx, |this, cx| {
5386                this.search_impl(query, cx).matching_buffers(cx)
5387            });
5388            let (batcher, batches) = project_search::AdaptiveBatcher::new(cx.background_executor());
5389            let mut new_matches = Box::pin(results.rx);
5390
5391            let sender_task = cx.background_executor().spawn({
5392                let client = client.clone();
5393                async move {
5394                    let mut batches = std::pin::pin!(batches);
5395                    while let Some(buffer_ids) = batches.next().await {
5396                        client
5397                            .request(proto::FindSearchCandidatesChunk {
5398                                handle,
5399                                peer_id: Some(peer_id),
5400                                project_id,
5401                                variant: Some(
5402                                    proto::find_search_candidates_chunk::Variant::Matches(
5403                                        proto::FindSearchCandidatesMatches { buffer_ids },
5404                                    ),
5405                                ),
5406                            })
5407                            .await?;
5408                    }
5409                    anyhow::Ok(())
5410                }
5411            });
5412
5413            while let Some(buffer) = new_matches.next().await {
5414                let buffer_id = this.update(cx, |this, cx| {
5415                    this.create_buffer_for_peer(&buffer, peer_id, cx).to_proto()
5416                });
5417                batcher.push(buffer_id).await;
5418            }
5419            batcher.flush().await;
5420
5421            sender_task.await?;
5422
5423            let _ = client
5424                .request(proto::FindSearchCandidatesChunk {
5425                    handle,
5426                    peer_id: Some(peer_id),
5427                    project_id,
5428                    variant: Some(proto::find_search_candidates_chunk::Variant::Done(
5429                        proto::FindSearchCandidatesDone {},
5430                    )),
5431                })
5432                .await?;
5433            anyhow::Ok(())
5434        });
5435        buffer_store.update(&mut cx, |this, _| {
5436            this.register_ongoing_project_search((peer_id, handle), task);
5437        });
5438
5439        Ok(proto::Ack {})
5440    }
5441
5442    async fn handle_open_buffer_by_id(
5443        this: Entity<Self>,
5444        envelope: TypedEnvelope<proto::OpenBufferById>,
5445        mut cx: AsyncApp,
5446    ) -> Result<proto::OpenBufferResponse> {
5447        let peer_id = envelope.original_sender_id()?;
5448        let buffer_id = BufferId::new(envelope.payload.id)?;
5449        let buffer = this
5450            .update(&mut cx, |this, cx| this.open_buffer_by_id(buffer_id, cx))
5451            .await?;
5452        Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
5453    }
5454
5455    async fn handle_open_buffer_by_path(
5456        this: Entity<Self>,
5457        envelope: TypedEnvelope<proto::OpenBufferByPath>,
5458        mut cx: AsyncApp,
5459    ) -> Result<proto::OpenBufferResponse> {
5460        let peer_id = envelope.original_sender_id()?;
5461        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
5462        let path = RelPath::from_proto(&envelope.payload.path)?;
5463        let open_buffer = this
5464            .update(&mut cx, |this, cx| {
5465                this.open_buffer(ProjectPath { worktree_id, path }, cx)
5466            })
5467            .await?;
5468        Project::respond_to_open_buffer_request(this, open_buffer, peer_id, &mut cx)
5469    }
5470
5471    async fn handle_open_new_buffer(
5472        this: Entity<Self>,
5473        envelope: TypedEnvelope<proto::OpenNewBuffer>,
5474        mut cx: AsyncApp,
5475    ) -> Result<proto::OpenBufferResponse> {
5476        let buffer = this
5477            .update(&mut cx, |this, cx| this.create_buffer(None, true, cx))
5478            .await?;
5479        let peer_id = envelope.original_sender_id()?;
5480
5481        Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
5482    }
5483
5484    fn respond_to_open_buffer_request(
5485        this: Entity<Self>,
5486        buffer: Entity<Buffer>,
5487        peer_id: proto::PeerId,
5488        cx: &mut AsyncApp,
5489    ) -> Result<proto::OpenBufferResponse> {
5490        this.update(cx, |this, cx| {
5491            let is_private = buffer
5492                .read(cx)
5493                .file()
5494                .map(|f| f.is_private())
5495                .unwrap_or_default();
5496            anyhow::ensure!(!is_private, ErrorCode::UnsharedItem);
5497            Ok(proto::OpenBufferResponse {
5498                buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx).into(),
5499            })
5500        })
5501    }
5502
5503    fn create_buffer_for_peer(
5504        &mut self,
5505        buffer: &Entity<Buffer>,
5506        peer_id: proto::PeerId,
5507        cx: &mut App,
5508    ) -> BufferId {
5509        self.buffer_store
5510            .update(cx, |buffer_store, cx| {
5511                buffer_store.create_buffer_for_peer(buffer, peer_id, cx)
5512            })
5513            .detach_and_log_err(cx);
5514        buffer.read(cx).remote_id()
5515    }
5516
5517    async fn handle_create_image_for_peer(
5518        this: Entity<Self>,
5519        envelope: TypedEnvelope<proto::CreateImageForPeer>,
5520        mut cx: AsyncApp,
5521    ) -> Result<()> {
5522        this.update(&mut cx, |this, cx| {
5523            this.image_store.update(cx, |image_store, cx| {
5524                image_store.handle_create_image_for_peer(envelope, cx)
5525            })
5526        })
5527    }
5528
5529    async fn handle_create_file_for_peer(
5530        this: Entity<Self>,
5531        envelope: TypedEnvelope<proto::CreateFileForPeer>,
5532        mut cx: AsyncApp,
5533    ) -> Result<()> {
5534        use proto::create_file_for_peer::Variant;
5535        log::debug!("handle_create_file_for_peer: received message");
5536
5537        let downloading_files: Arc<Mutex<HashMap<(WorktreeId, String), DownloadingFile>>> =
5538            this.update(&mut cx, |this, _| this.downloading_files.clone());
5539
5540        match &envelope.payload.variant {
5541            Some(Variant::State(state)) => {
5542                log::debug!(
5543                    "handle_create_file_for_peer: got State: id={}, content_size={}",
5544                    state.id,
5545                    state.content_size
5546                );
5547
5548                // Extract worktree_id and path from the File field
5549                if let Some(ref file) = state.file {
5550                    let worktree_id = WorktreeId::from_proto(file.worktree_id);
5551                    let path = file.path.clone();
5552                    let key = (worktree_id, path);
5553                    log::debug!("handle_create_file_for_peer: looking up key={:?}", key);
5554
5555                    let empty_file_destination: Option<PathBuf> = {
5556                        let mut files = downloading_files.lock();
5557                        log::trace!(
5558                            "handle_create_file_for_peer: current downloading_files keys: {:?}",
5559                            files.keys().collect::<Vec<_>>()
5560                        );
5561
5562                        if let Some(file_entry) = files.get_mut(&key) {
5563                            file_entry.total_size = state.content_size;
5564                            file_entry.file_id = Some(state.id);
5565                            log::debug!(
5566                                "handle_create_file_for_peer: updated file entry: total_size={}, file_id={}",
5567                                state.content_size,
5568                                state.id
5569                            );
5570                        } else {
5571                            log::warn!(
5572                                "handle_create_file_for_peer: key={:?} not found in downloading_files",
5573                                key
5574                            );
5575                        }
5576
5577                        if state.content_size == 0 {
5578                            // No chunks will arrive for an empty file; write it now.
5579                            files.remove(&key).map(|entry| entry.destination_path)
5580                        } else {
5581                            None
5582                        }
5583                    };
5584
5585                    if let Some(destination) = empty_file_destination {
5586                        log::debug!(
5587                            "handle_create_file_for_peer: writing empty file to {:?}",
5588                            destination
5589                        );
5590                        match smol::fs::write(&destination, &[] as &[u8]).await {
5591                            Ok(_) => log::info!(
5592                                "handle_create_file_for_peer: successfully wrote file to {:?}",
5593                                destination
5594                            ),
5595                            Err(e) => log::error!(
5596                                "handle_create_file_for_peer: failed to write empty file: {:?}",
5597                                e
5598                            ),
5599                        }
5600                    }
5601                } else {
5602                    log::warn!("handle_create_file_for_peer: State has no file field");
5603                }
5604            }
5605            Some(Variant::Chunk(chunk)) => {
5606                log::debug!(
5607                    "handle_create_file_for_peer: got Chunk: file_id={}, data_len={}",
5608                    chunk.file_id,
5609                    chunk.data.len()
5610                );
5611
5612                // Extract data while holding the lock, then release it before await
5613                let (key_to_remove, write_info): (
5614                    Option<(WorktreeId, String)>,
5615                    Option<(PathBuf, Vec<u8>)>,
5616                ) = {
5617                    let mut files = downloading_files.lock();
5618                    let mut found_key: Option<(WorktreeId, String)> = None;
5619                    let mut write_data: Option<(PathBuf, Vec<u8>)> = None;
5620
5621                    for (key, file_entry) in files.iter_mut() {
5622                        if file_entry.file_id == Some(chunk.file_id) {
5623                            file_entry.chunks.extend_from_slice(&chunk.data);
5624                            log::debug!(
5625                                "handle_create_file_for_peer: accumulated {} bytes, total_size={}",
5626                                file_entry.chunks.len(),
5627                                file_entry.total_size
5628                            );
5629
5630                            if file_entry.chunks.len() as u64 >= file_entry.total_size
5631                                && file_entry.total_size > 0
5632                            {
5633                                let destination = file_entry.destination_path.clone();
5634                                let content = std::mem::take(&mut file_entry.chunks);
5635                                found_key = Some(key.clone());
5636                                write_data = Some((destination, content));
5637                            }
5638                            break;
5639                        }
5640                    }
5641                    (found_key, write_data)
5642                }; // MutexGuard is dropped here
5643
5644                // Perform the async write outside the lock
5645                if let Some((destination, content)) = write_info {
5646                    log::debug!(
5647                        "handle_create_file_for_peer: writing {} bytes to {:?}",
5648                        content.len(),
5649                        destination
5650                    );
5651                    match smol::fs::write(&destination, &content).await {
5652                        Ok(_) => log::info!(
5653                            "handle_create_file_for_peer: successfully wrote file to {:?}",
5654                            destination
5655                        ),
5656                        Err(e) => log::error!(
5657                            "handle_create_file_for_peer: failed to write file: {:?}",
5658                            e
5659                        ),
5660                    }
5661                }
5662
5663                // Remove the completed entry
5664                if let Some(key) = key_to_remove {
5665                    downloading_files.lock().remove(&key);
5666                    log::debug!("handle_create_file_for_peer: removed completed download entry");
5667                }
5668            }
5669            None => {
5670                log::warn!("handle_create_file_for_peer: got None variant");
5671            }
5672        }
5673
5674        Ok(())
5675    }
5676
5677    fn synchronize_remote_buffers(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
5678        let project_id = match self.client_state {
5679            ProjectClientState::Collab {
5680                sharing_has_stopped,
5681                remote_id,
5682                ..
5683            } => {
5684                if sharing_has_stopped {
5685                    return Task::ready(Err(anyhow!(
5686                        "can't synchronize remote buffers on a readonly project"
5687                    )));
5688                } else {
5689                    remote_id
5690                }
5691            }
5692            ProjectClientState::Shared { .. } | ProjectClientState::Local => {
5693                return Task::ready(Err(anyhow!(
5694                    "can't synchronize remote buffers on a local project"
5695                )));
5696            }
5697        };
5698
5699        let client = self.collab_client.clone();
5700        cx.spawn(async move |this, cx| {
5701            let (buffers, incomplete_buffer_ids) = this.update(cx, |this, cx| {
5702                this.buffer_store.read(cx).buffer_version_info(cx)
5703            })?;
5704            let response = client
5705                .request(proto::SynchronizeBuffers {
5706                    project_id,
5707                    buffers,
5708                })
5709                .await?;
5710
5711            let send_updates_for_buffers = this.update(cx, |this, cx| {
5712                response
5713                    .buffers
5714                    .into_iter()
5715                    .map(|buffer| {
5716                        let client = client.clone();
5717                        let buffer_id = match BufferId::new(buffer.id) {
5718                            Ok(id) => id,
5719                            Err(e) => {
5720                                return Task::ready(Err(e));
5721                            }
5722                        };
5723                        let remote_version = language::proto::deserialize_version(&buffer.version);
5724                        if let Some(buffer) = this.buffer_for_id(buffer_id, cx) {
5725                            let operations =
5726                                buffer.read(cx).serialize_ops(Some(remote_version), cx);
5727                            cx.background_spawn(async move {
5728                                let operations = operations.await;
5729                                for chunk in split_operations(operations) {
5730                                    client
5731                                        .request(proto::UpdateBuffer {
5732                                            project_id,
5733                                            buffer_id: buffer_id.into(),
5734                                            operations: chunk,
5735                                        })
5736                                        .await?;
5737                                }
5738                                anyhow::Ok(())
5739                            })
5740                        } else {
5741                            Task::ready(Ok(()))
5742                        }
5743                    })
5744                    .collect::<Vec<_>>()
5745            })?;
5746
5747            // Any incomplete buffers have open requests waiting. Request that the host sends
5748            // creates these buffers for us again to unblock any waiting futures.
5749            for id in incomplete_buffer_ids {
5750                cx.background_spawn(client.request(proto::OpenBufferById {
5751                    project_id,
5752                    id: id.into(),
5753                }))
5754                .detach();
5755            }
5756
5757            futures::future::join_all(send_updates_for_buffers)
5758                .await
5759                .into_iter()
5760                .collect()
5761        })
5762    }
5763
5764    pub fn worktree_metadata_protos(&self, cx: &App) -> Vec<proto::WorktreeMetadata> {
5765        self.worktree_store.read(cx).worktree_metadata_protos(cx)
5766    }
5767
5768    /// Iterator of all open buffers that have unsaved changes
5769    pub fn dirty_buffers<'a>(&'a self, cx: &'a App) -> impl Iterator<Item = ProjectPath> + 'a {
5770        self.buffer_store.read(cx).buffers().filter_map(|buf| {
5771            let buf = buf.read(cx);
5772            if buf.is_dirty() {
5773                buf.project_path(cx)
5774            } else {
5775                None
5776            }
5777        })
5778    }
5779
5780    fn set_worktrees_from_proto(
5781        &mut self,
5782        worktrees: Vec<proto::WorktreeMetadata>,
5783        cx: &mut Context<Project>,
5784    ) -> Result<()> {
5785        self.worktree_store.update(cx, |worktree_store, cx| {
5786            worktree_store.set_worktrees_from_proto(worktrees, self.replica_id(), cx)
5787        })
5788    }
5789
5790    fn set_collaborators_from_proto(
5791        &mut self,
5792        messages: Vec<proto::Collaborator>,
5793        cx: &mut Context<Self>,
5794    ) -> Result<()> {
5795        let mut collaborators = HashMap::default();
5796        for message in messages {
5797            let collaborator = Collaborator::from_proto(message)?;
5798            collaborators.insert(collaborator.peer_id, collaborator);
5799        }
5800        for old_peer_id in self.collaborators.keys() {
5801            if !collaborators.contains_key(old_peer_id) {
5802                cx.emit(Event::CollaboratorLeft(*old_peer_id));
5803            }
5804        }
5805        self.collaborators = collaborators;
5806        Ok(())
5807    }
5808
5809    pub fn supplementary_language_servers<'a>(
5810        &'a self,
5811        cx: &'a App,
5812    ) -> impl 'a + Iterator<Item = (LanguageServerId, LanguageServerName)> {
5813        self.lsp_store.read(cx).supplementary_language_servers()
5814    }
5815
5816    pub fn any_language_server_supports_inlay_hints(&self, buffer: &Buffer, cx: &mut App) -> bool {
5817        let Some(language) = buffer.language().cloned() else {
5818            return false;
5819        };
5820        self.lsp_store.update(cx, |lsp_store, _| {
5821            let relevant_language_servers = lsp_store
5822                .languages
5823                .lsp_adapters(&language.name())
5824                .into_iter()
5825                .map(|lsp_adapter| lsp_adapter.name())
5826                .collect::<HashSet<_>>();
5827            lsp_store
5828                .language_server_statuses()
5829                .filter_map(|(server_id, server_status)| {
5830                    relevant_language_servers
5831                        .contains(&server_status.name)
5832                        .then_some(server_id)
5833                })
5834                .filter_map(|server_id| lsp_store.lsp_server_capabilities.get(&server_id))
5835                .any(InlayHints::check_capabilities)
5836        })
5837    }
5838
5839    pub fn any_language_server_supports_semantic_tokens(
5840        &self,
5841        buffer: &Buffer,
5842        cx: &mut App,
5843    ) -> bool {
5844        let Some(language) = buffer.language().cloned() else {
5845            return false;
5846        };
5847        let lsp_store = self.lsp_store.read(cx);
5848        let relevant_language_servers = lsp_store
5849            .languages
5850            .lsp_adapters(&language.name())
5851            .into_iter()
5852            .map(|lsp_adapter| lsp_adapter.name())
5853            .collect::<HashSet<_>>();
5854        lsp_store
5855            .language_server_statuses()
5856            .filter_map(|(server_id, server_status)| {
5857                relevant_language_servers
5858                    .contains(&server_status.name)
5859                    .then_some(server_id)
5860            })
5861            .filter_map(|server_id| lsp_store.lsp_server_capabilities.get(&server_id))
5862            .any(|capabilities| capabilities.semantic_tokens_provider.is_some())
5863    }
5864
5865    pub fn language_server_id_for_name(
5866        &self,
5867        buffer: &Buffer,
5868        name: &LanguageServerName,
5869        cx: &App,
5870    ) -> Option<LanguageServerId> {
5871        let language = buffer.language()?;
5872        let relevant_language_servers = self
5873            .languages
5874            .lsp_adapters(&language.name())
5875            .into_iter()
5876            .map(|lsp_adapter| lsp_adapter.name())
5877            .collect::<HashSet<_>>();
5878        if !relevant_language_servers.contains(name) {
5879            return None;
5880        }
5881        self.language_server_statuses(cx)
5882            .filter(|(_, server_status)| relevant_language_servers.contains(&server_status.name))
5883            .find_map(|(server_id, server_status)| {
5884                if &server_status.name == name {
5885                    Some(server_id)
5886                } else {
5887                    None
5888                }
5889            })
5890    }
5891
5892    #[cfg(feature = "test-support")]
5893    pub fn has_language_servers_for(&self, buffer: &Buffer, cx: &mut App) -> bool {
5894        self.lsp_store.update(cx, |this, cx| {
5895            this.running_language_servers_for_local_buffer(buffer, cx)
5896                .next()
5897                .is_some()
5898        })
5899    }
5900
5901    pub fn git_init(
5902        &self,
5903        path: Arc<Path>,
5904        fallback_branch_name: String,
5905        cx: &App,
5906    ) -> Task<Result<()>> {
5907        self.git_store
5908            .read(cx)
5909            .git_init(path, fallback_branch_name, cx)
5910    }
5911
5912    pub fn buffer_store(&self) -> &Entity<BufferStore> {
5913        &self.buffer_store
5914    }
5915
5916    pub fn git_store(&self) -> &Entity<GitStore> {
5917        &self.git_store
5918    }
5919
5920    pub fn agent_server_store(&self) -> &Entity<AgentServerStore> {
5921        &self.agent_server_store
5922    }
5923
5924    #[cfg(feature = "test-support")]
5925    pub fn git_scans_complete(&self, cx: &Context<Self>) -> Task<()> {
5926        use futures::future::join_all;
5927        cx.spawn(async move |this, cx| {
5928            let scans_complete = this
5929                .read_with(cx, |this, cx| {
5930                    this.worktrees(cx)
5931                        .filter_map(|worktree| Some(worktree.read(cx).as_local()?.scan_complete()))
5932                        .collect::<Vec<_>>()
5933                })
5934                .unwrap();
5935            join_all(scans_complete).await;
5936            let barriers = this
5937                .update(cx, |this, cx| {
5938                    let repos = this.repositories(cx).values().cloned().collect::<Vec<_>>();
5939                    repos
5940                        .into_iter()
5941                        .map(|repo| repo.update(cx, |repo, _| repo.barrier()))
5942                        .collect::<Vec<_>>()
5943                })
5944                .unwrap();
5945            join_all(barriers).await;
5946        })
5947    }
5948
5949    pub fn active_repository(&self, cx: &App) -> Option<Entity<Repository>> {
5950        self.git_store.read(cx).active_repository()
5951    }
5952
5953    pub fn repositories<'a>(&self, cx: &'a App) -> &'a HashMap<RepositoryId, Entity<Repository>> {
5954        self.git_store.read(cx).repositories()
5955    }
5956
5957    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
5958        self.git_store.read(cx).status_for_buffer_id(buffer_id, cx)
5959    }
5960
5961    pub fn set_agent_location(
5962        &mut self,
5963        new_location: Option<AgentLocation>,
5964        cx: &mut Context<Self>,
5965    ) {
5966        if let Some(old_location) = self.agent_location.as_ref() {
5967            old_location
5968                .buffer
5969                .update(cx, |buffer, cx| buffer.remove_agent_selections(cx))
5970                .ok();
5971        }
5972
5973        if let Some(location) = new_location.as_ref() {
5974            location
5975                .buffer
5976                .update(cx, |buffer, cx| {
5977                    buffer.set_agent_selections(
5978                        Arc::from([language::Selection {
5979                            id: 0,
5980                            start: location.position,
5981                            end: location.position,
5982                            reversed: false,
5983                            goal: language::SelectionGoal::None,
5984                        }]),
5985                        false,
5986                        CursorShape::Hollow,
5987                        cx,
5988                    )
5989                })
5990                .ok();
5991        }
5992
5993        self.agent_location = new_location;
5994        cx.emit(Event::AgentLocationChanged);
5995    }
5996
5997    pub fn agent_location(&self) -> Option<AgentLocation> {
5998        self.agent_location.clone()
5999    }
6000
6001    pub fn path_style(&self, cx: &App) -> PathStyle {
6002        self.worktree_store.read(cx).path_style()
6003    }
6004
6005    pub fn contains_local_settings_file(
6006        &self,
6007        worktree_id: WorktreeId,
6008        rel_path: &RelPath,
6009        cx: &App,
6010    ) -> bool {
6011        self.worktree_for_id(worktree_id, cx)
6012            .map_or(false, |worktree| {
6013                worktree.read(cx).entry_for_path(rel_path).is_some()
6014            })
6015    }
6016}
6017
6018pub struct PathMatchCandidateSet {
6019    pub snapshot: Snapshot,
6020    pub include_ignored: bool,
6021    pub include_root_name: bool,
6022    pub candidates: Candidates,
6023}
6024
6025pub enum Candidates {
6026    /// Only consider directories.
6027    Directories,
6028    /// Only consider files.
6029    Files,
6030    /// Consider directories and files.
6031    Entries,
6032}
6033
6034impl<'a> fuzzy::PathMatchCandidateSet<'a> for PathMatchCandidateSet {
6035    type Candidates = PathMatchCandidateSetIter<'a>;
6036
6037    fn id(&self) -> usize {
6038        self.snapshot.id().to_usize()
6039    }
6040
6041    fn len(&self) -> usize {
6042        match self.candidates {
6043            Candidates::Files => {
6044                if self.include_ignored {
6045                    self.snapshot.file_count()
6046                } else {
6047                    self.snapshot.visible_file_count()
6048                }
6049            }
6050
6051            Candidates::Directories => {
6052                if self.include_ignored {
6053                    self.snapshot.dir_count()
6054                } else {
6055                    self.snapshot.visible_dir_count()
6056                }
6057            }
6058
6059            Candidates::Entries => {
6060                if self.include_ignored {
6061                    self.snapshot.entry_count()
6062                } else {
6063                    self.snapshot.visible_entry_count()
6064                }
6065            }
6066        }
6067    }
6068
6069    fn prefix(&self) -> Arc<RelPath> {
6070        if self.snapshot.root_entry().is_some_and(|e| e.is_file()) || self.include_root_name {
6071            self.snapshot.root_name().into()
6072        } else {
6073            RelPath::empty().into()
6074        }
6075    }
6076
6077    fn root_is_file(&self) -> bool {
6078        self.snapshot.root_entry().is_some_and(|f| f.is_file())
6079    }
6080
6081    fn path_style(&self) -> PathStyle {
6082        self.snapshot.path_style()
6083    }
6084
6085    fn candidates(&'a self, start: usize) -> Self::Candidates {
6086        PathMatchCandidateSetIter {
6087            traversal: match self.candidates {
6088                Candidates::Directories => self.snapshot.directories(self.include_ignored, start),
6089                Candidates::Files => self.snapshot.files(self.include_ignored, start),
6090                Candidates::Entries => self.snapshot.entries(self.include_ignored, start),
6091            },
6092        }
6093    }
6094}
6095
6096pub struct PathMatchCandidateSetIter<'a> {
6097    traversal: Traversal<'a>,
6098}
6099
6100impl<'a> Iterator for PathMatchCandidateSetIter<'a> {
6101    type Item = fuzzy::PathMatchCandidate<'a>;
6102
6103    fn next(&mut self) -> Option<Self::Item> {
6104        self.traversal
6105            .next()
6106            .map(|entry| fuzzy::PathMatchCandidate {
6107                is_dir: entry.kind.is_dir(),
6108                path: &entry.path,
6109                char_bag: entry.char_bag,
6110            })
6111    }
6112}
6113
6114impl EventEmitter<Event> for Project {}
6115
6116impl<'a> From<&'a ProjectPath> for SettingsLocation<'a> {
6117    fn from(val: &'a ProjectPath) -> Self {
6118        SettingsLocation {
6119            worktree_id: val.worktree_id,
6120            path: val.path.as_ref(),
6121        }
6122    }
6123}
6124
6125impl<P: Into<Arc<RelPath>>> From<(WorktreeId, P)> for ProjectPath {
6126    fn from((worktree_id, path): (WorktreeId, P)) -> Self {
6127        Self {
6128            worktree_id,
6129            path: path.into(),
6130        }
6131    }
6132}
6133
6134/// ResolvedPath is a path that has been resolved to either a ProjectPath
6135/// or an AbsPath and that *exists*.
6136#[derive(Debug, Clone)]
6137pub enum ResolvedPath {
6138    ProjectPath {
6139        project_path: ProjectPath,
6140        is_dir: bool,
6141    },
6142    AbsPath {
6143        path: String,
6144        is_dir: bool,
6145    },
6146}
6147
6148impl ResolvedPath {
6149    pub fn abs_path(&self) -> Option<&str> {
6150        match self {
6151            Self::AbsPath { path, .. } => Some(path),
6152            _ => None,
6153        }
6154    }
6155
6156    pub fn into_abs_path(self) -> Option<String> {
6157        match self {
6158            Self::AbsPath { path, .. } => Some(path),
6159            _ => None,
6160        }
6161    }
6162
6163    pub fn project_path(&self) -> Option<&ProjectPath> {
6164        match self {
6165            Self::ProjectPath { project_path, .. } => Some(project_path),
6166            _ => None,
6167        }
6168    }
6169
6170    pub fn is_file(&self) -> bool {
6171        !self.is_dir()
6172    }
6173
6174    pub fn is_dir(&self) -> bool {
6175        match self {
6176            Self::ProjectPath { is_dir, .. } => *is_dir,
6177            Self::AbsPath { is_dir, .. } => *is_dir,
6178        }
6179    }
6180}
6181
6182impl ProjectItem for Buffer {
6183    fn try_open(
6184        project: &Entity<Project>,
6185        path: &ProjectPath,
6186        cx: &mut App,
6187    ) -> Option<Task<Result<Entity<Self>>>> {
6188        Some(project.update(cx, |project, cx| project.open_buffer(path.clone(), cx)))
6189    }
6190
6191    fn entry_id(&self, _cx: &App) -> Option<ProjectEntryId> {
6192        File::from_dyn(self.file()).and_then(|file| file.project_entry_id())
6193    }
6194
6195    fn project_path(&self, cx: &App) -> Option<ProjectPath> {
6196        let file = self.file()?;
6197
6198        (!matches!(file.disk_state(), DiskState::Historic { .. })).then(|| ProjectPath {
6199            worktree_id: file.worktree_id(cx),
6200            path: file.path().clone(),
6201        })
6202    }
6203
6204    fn is_dirty(&self) -> bool {
6205        self.is_dirty()
6206    }
6207}
6208
6209impl Completion {
6210    pub fn kind(&self) -> Option<CompletionItemKind> {
6211        self.source
6212            // `lsp::CompletionListItemDefaults` has no `kind` field
6213            .lsp_completion(false)
6214            .and_then(|lsp_completion| lsp_completion.kind)
6215    }
6216
6217    pub fn label(&self) -> Option<String> {
6218        self.source
6219            .lsp_completion(false)
6220            .map(|lsp_completion| lsp_completion.label.clone())
6221    }
6222
6223    /// A key that can be used to sort completions when displaying
6224    /// them to the user.
6225    pub fn sort_key(&self) -> (usize, &str) {
6226        const DEFAULT_KIND_KEY: usize = 4;
6227        let kind_key = self
6228            .kind()
6229            .and_then(|lsp_completion_kind| match lsp_completion_kind {
6230                lsp::CompletionItemKind::KEYWORD => Some(0),
6231                lsp::CompletionItemKind::VARIABLE => Some(1),
6232                lsp::CompletionItemKind::CONSTANT => Some(2),
6233                lsp::CompletionItemKind::PROPERTY => Some(3),
6234                _ => None,
6235            })
6236            .unwrap_or(DEFAULT_KIND_KEY);
6237        (kind_key, self.label.filter_text())
6238    }
6239
6240    /// Whether this completion is a snippet.
6241    pub fn is_snippet_kind(&self) -> bool {
6242        matches!(
6243            &self.source,
6244            CompletionSource::Lsp { lsp_completion, .. }
6245            if lsp_completion.kind == Some(CompletionItemKind::SNIPPET)
6246        )
6247    }
6248
6249    /// Whether this completion is a snippet or snippet-style LSP completion.
6250    pub fn is_snippet(&self) -> bool {
6251        self.source
6252            // `lsp::CompletionListItemDefaults` has `insert_text_format` field
6253            .lsp_completion(true)
6254            .is_some_and(|lsp_completion| {
6255                lsp_completion.insert_text_format == Some(lsp::InsertTextFormat::SNIPPET)
6256            })
6257    }
6258
6259    /// Returns the corresponding color for this completion.
6260    ///
6261    /// Will return `None` if this completion's kind is not [`CompletionItemKind::COLOR`].
6262    pub fn color(&self) -> Option<Hsla> {
6263        // `lsp::CompletionListItemDefaults` has no `kind` field
6264        let lsp_completion = self.source.lsp_completion(false)?;
6265        if lsp_completion.kind? == CompletionItemKind::COLOR {
6266            return color_extractor::extract_color(&lsp_completion);
6267        }
6268        None
6269    }
6270}
6271
6272fn proto_to_prompt(level: proto::language_server_prompt_request::Level) -> gpui::PromptLevel {
6273    match level {
6274        proto::language_server_prompt_request::Level::Info(_) => gpui::PromptLevel::Info,
6275        proto::language_server_prompt_request::Level::Warning(_) => gpui::PromptLevel::Warning,
6276        proto::language_server_prompt_request::Level::Critical(_) => gpui::PromptLevel::Critical,
6277    }
6278}
6279
6280fn provide_inline_values(
6281    captures: impl Iterator<Item = (Range<usize>, language::DebuggerTextObject)>,
6282    snapshot: &language::BufferSnapshot,
6283    max_row: usize,
6284) -> Vec<InlineValueLocation> {
6285    let mut variables = Vec::new();
6286    let mut variable_position = HashSet::default();
6287    let mut scopes = Vec::new();
6288
6289    let active_debug_line_offset = snapshot.point_to_offset(Point::new(max_row as u32, 0));
6290
6291    for (capture_range, capture_kind) in captures {
6292        match capture_kind {
6293            language::DebuggerTextObject::Variable => {
6294                let variable_name = snapshot
6295                    .text_for_range(capture_range.clone())
6296                    .collect::<String>();
6297                let point = snapshot.offset_to_point(capture_range.end);
6298
6299                while scopes
6300                    .last()
6301                    .is_some_and(|scope: &Range<_>| !scope.contains(&capture_range.start))
6302                {
6303                    scopes.pop();
6304                }
6305
6306                if point.row as usize > max_row {
6307                    break;
6308                }
6309
6310                let scope = if scopes
6311                    .last()
6312                    .is_none_or(|scope| !scope.contains(&active_debug_line_offset))
6313                {
6314                    VariableScope::Global
6315                } else {
6316                    VariableScope::Local
6317                };
6318
6319                if variable_position.insert(capture_range.end) {
6320                    variables.push(InlineValueLocation {
6321                        variable_name,
6322                        scope,
6323                        lookup: VariableLookupKind::Variable,
6324                        row: point.row as usize,
6325                        column: point.column as usize,
6326                    });
6327                }
6328            }
6329            language::DebuggerTextObject::Scope => {
6330                while scopes.last().map_or_else(
6331                    || false,
6332                    |scope: &Range<usize>| {
6333                        !(scope.contains(&capture_range.start)
6334                            && scope.contains(&capture_range.end))
6335                    },
6336                ) {
6337                    scopes.pop();
6338                }
6339                scopes.push(capture_range);
6340            }
6341        }
6342    }
6343
6344    variables
6345}