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