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