project.rs

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