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