project.rs

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