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