project.rs

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