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