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