project.rs

   1pub mod agent_server_store;
   2pub mod buffer_store;
   3mod color_extractor;
   4pub mod connection_manager;
   5pub mod context_server_store;
   6pub mod debounced_delay;
   7pub mod debugger;
   8pub mod git_store;
   9pub mod image_store;
  10pub mod lsp_command;
  11pub mod lsp_store;
  12mod manifest_tree;
  13pub mod prettier_store;
  14pub mod project_settings;
  15pub mod search;
  16mod task_inventory;
  17pub mod task_store;
  18pub mod telemetry_snapshot;
  19pub mod terminals;
  20pub mod toolchain_store;
  21pub mod worktree_store;
  22
  23#[cfg(test)]
  24mod project_tests;
  25
  26mod environment;
  27use buffer_diff::BufferDiff;
  28use context_server_store::ContextServerStore;
  29pub use environment::ProjectEnvironmentEvent;
  30use git::repository::get_git_committer;
  31use git_store::{Repository, RepositoryId};
  32pub mod search_history;
  33mod yarn;
  34
  35use dap::inline_value::{InlineValueLocation, VariableLookupKind, VariableScope};
  36
  37use crate::{
  38    agent_server_store::AllAgentServersSettings,
  39    git_store::GitStore,
  40    lsp_store::{SymbolLocation, log_store::LogKind},
  41};
  42pub use agent_server_store::{AgentServerStore, AgentServersUpdated, ExternalAgentServerName};
  43pub use git_store::{
  44    ConflictRegion, ConflictSet, ConflictSetSnapshot, ConflictSetUpdate,
  45    git_traversal::{ChildEntriesGitIter, GitEntry, GitEntryRef, GitTraversal},
  46};
  47pub use manifest_tree::ManifestTree;
  48
  49use anyhow::{Context as _, Result, anyhow};
  50use buffer_store::{BufferStore, BufferStoreEvent};
  51use client::{Client, Collaborator, PendingEntitySubscription, TypedEnvelope, UserStore, proto};
  52use clock::ReplicaId;
  53
  54use dap::client::DebugAdapterClient;
  55
  56use collections::{BTreeSet, HashMap, HashSet, IndexSet};
  57use debounced_delay::DebouncedDelay;
  58pub use debugger::breakpoint_store::BreakpointWithPosition;
  59use debugger::{
  60    breakpoint_store::{ActiveStackFrame, BreakpointStore},
  61    dap_store::{DapStore, DapStoreEvent},
  62    session::Session,
  63};
  64pub use environment::ProjectEnvironment;
  65#[cfg(test)]
  66use futures::future::join_all;
  67use futures::{
  68    StreamExt,
  69    channel::mpsc::{self, UnboundedReceiver},
  70    future::try_join_all,
  71};
  72pub use image_store::{ImageItem, ImageStore};
  73use image_store::{ImageItemEvent, ImageStoreEvent};
  74
  75use ::git::{blame::Blame, status::FileStatus};
  76use gpui::{
  77    App, AppContext, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Hsla, SharedString,
  78    Task, WeakEntity, Window,
  79};
  80use language::{
  81    Buffer, BufferEvent, Capability, CodeLabel, CursorShape, Language, LanguageName,
  82    LanguageRegistry, PointUtf16, ToOffset, ToPointUtf16, Toolchain, ToolchainMetadata,
  83    ToolchainScope, Transaction, Unclipped, language_settings::InlayHintKind,
  84    proto::split_operations,
  85};
  86use lsp::{
  87    CodeActionKind, CompletionContext, CompletionItemKind, DocumentHighlightKind, InsertTextMode,
  88    LanguageServerId, LanguageServerName, LanguageServerSelector, MessageActionItem,
  89};
  90use lsp_command::*;
  91use lsp_store::{CompletionDocumentation, LspFormatTarget, OpenLspBufferHandle};
  92pub use manifest_tree::ManifestProvidersStore;
  93use node_runtime::NodeRuntime;
  94use parking_lot::Mutex;
  95pub use prettier_store::PrettierStore;
  96use project_settings::{ProjectSettings, SettingsObserver, SettingsObserverEvent};
  97use remote::{RemoteClient, RemoteConnectionOptions};
  98use rpc::{
  99    AnyProtoClient, ErrorCode,
 100    proto::{LanguageServerPromptResponse, REMOTE_SERVER_PROJECT_ID},
 101};
 102use search::{SearchInputKind, SearchQuery, SearchResult};
 103use search_history::SearchHistory;
 104use settings::{InvalidSettingsError, Settings, SettingsLocation, SettingsStore};
 105use smol::channel::Receiver;
 106use snippet::Snippet;
 107use snippet_provider::SnippetProvider;
 108use std::{
 109    borrow::Cow,
 110    collections::BTreeMap,
 111    ops::Range,
 112    path::{Path, PathBuf},
 113    pin::pin,
 114    str,
 115    sync::Arc,
 116    time::Duration,
 117};
 118
 119use task_store::TaskStore;
 120use terminals::Terminals;
 121use text::{Anchor, BufferId, OffsetRangeExt, Point, Rope};
 122use toolchain_store::EmptyToolchainStore;
 123use util::{
 124    ResultExt as _, maybe,
 125    paths::{PathStyle, SanitizedPath, compare_paths, is_absolute},
 126    rel_path::RelPath,
 127};
 128use worktree::{CreatedEntry, Snapshot, Traversal};
 129pub use worktree::{
 130    Entry, EntryKind, FS_WATCH_LATENCY, File, LocalWorktree, PathChange, ProjectEntryId,
 131    UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings,
 132};
 133use worktree_store::{WorktreeStore, WorktreeStoreEvent};
 134
 135pub use fs::*;
 136pub use language::Location;
 137#[cfg(any(test, feature = "test-support"))]
 138pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
 139pub use task_inventory::{
 140    BasicContextProvider, ContextProviderWithTasks, DebugScenarioContext, Inventory, TaskContexts,
 141    TaskSourceKind,
 142};
 143
 144pub use buffer_store::ProjectTransaction;
 145pub use lsp_store::{
 146    DiagnosticSummary, InvalidationStrategy, LanguageServerLogType, LanguageServerProgress,
 147    LanguageServerPromptRequest, LanguageServerStatus, LanguageServerToQuery, LspStore,
 148    LspStoreEvent, ProgressToken, SERVER_PROGRESS_THROTTLE_TIMEOUT,
 149};
 150pub use toolchain_store::{ToolchainStore, Toolchains};
 151const MAX_PROJECT_SEARCH_HISTORY_SIZE: usize = 500;
 152const MAX_SEARCH_RESULT_FILES: usize = 5_000;
 153const MAX_SEARCH_RESULT_RANGES: usize = 10_000;
 154
 155pub trait ProjectItem: 'static {
 156    fn try_open(
 157        project: &Entity<Project>,
 158        path: &ProjectPath,
 159        cx: &mut App,
 160    ) -> Option<Task<Result<Entity<Self>>>>
 161    where
 162        Self: Sized;
 163    fn entry_id(&self, cx: &App) -> Option<ProjectEntryId>;
 164    fn project_path(&self, cx: &App) -> Option<ProjectPath>;
 165    fn is_dirty(&self) -> bool;
 166}
 167
 168#[derive(Clone)]
 169pub enum OpenedBufferEvent {
 170    Disconnected,
 171    Ok(BufferId),
 172    Err(BufferId, Arc<anyhow::Error>),
 173}
 174
 175/// Semantics-aware entity that is relevant to one or more [`Worktree`] with the files.
 176/// `Project` is responsible for tasks, LSP and collab queries, synchronizing worktree states accordingly.
 177/// Maps [`Worktree`] entries with its own logic using [`ProjectEntryId`] and [`ProjectPath`] structs.
 178///
 179/// Can be either local (for the project opened on the same host) or remote.(for collab projects, browsed by multiple remote users).
 180pub struct Project {
 181    active_entry: Option<ProjectEntryId>,
 182    buffer_ordered_messages_tx: mpsc::UnboundedSender<BufferOrderedMessage>,
 183    languages: Arc<LanguageRegistry>,
 184    dap_store: Entity<DapStore>,
 185    agent_server_store: Entity<AgentServerStore>,
 186
 187    breakpoint_store: Entity<BreakpointStore>,
 188    collab_client: Arc<client::Client>,
 189    join_project_response_message_id: u32,
 190    task_store: Entity<TaskStore>,
 191    user_store: Entity<UserStore>,
 192    fs: Arc<dyn Fs>,
 193    remote_client: Option<Entity<RemoteClient>>,
 194    client_state: ProjectClientState,
 195    git_store: Entity<GitStore>,
 196    collaborators: HashMap<proto::PeerId, Collaborator>,
 197    client_subscriptions: Vec<client::Subscription>,
 198    worktree_store: Entity<WorktreeStore>,
 199    buffer_store: Entity<BufferStore>,
 200    context_server_store: Entity<ContextServerStore>,
 201    image_store: Entity<ImageStore>,
 202    lsp_store: Entity<LspStore>,
 203    _subscriptions: Vec<gpui::Subscription>,
 204    buffers_needing_diff: HashSet<WeakEntity<Buffer>>,
 205    git_diff_debouncer: DebouncedDelay<Self>,
 206    remotely_created_models: Arc<Mutex<RemotelyCreatedModels>>,
 207    terminals: Terminals,
 208    node: Option<NodeRuntime>,
 209    search_history: SearchHistory,
 210    search_included_history: SearchHistory,
 211    search_excluded_history: SearchHistory,
 212    snippets: Entity<SnippetProvider>,
 213    environment: Entity<ProjectEnvironment>,
 214    settings_observer: Entity<SettingsObserver>,
 215    toolchain_store: Option<Entity<ToolchainStore>>,
 216    agent_location: Option<AgentLocation>,
 217}
 218
 219#[derive(Clone, Debug, PartialEq, Eq)]
 220pub struct AgentLocation {
 221    pub buffer: WeakEntity<Buffer>,
 222    pub position: Anchor,
 223}
 224
 225#[derive(Default)]
 226struct RemotelyCreatedModels {
 227    worktrees: Vec<Entity<Worktree>>,
 228    buffers: Vec<Entity<Buffer>>,
 229    retain_count: usize,
 230}
 231
 232struct RemotelyCreatedModelGuard {
 233    remote_models: std::sync::Weak<Mutex<RemotelyCreatedModels>>,
 234}
 235
 236impl Drop for RemotelyCreatedModelGuard {
 237    fn drop(&mut self) {
 238        if let Some(remote_models) = self.remote_models.upgrade() {
 239            let mut remote_models = remote_models.lock();
 240            assert!(
 241                remote_models.retain_count > 0,
 242                "RemotelyCreatedModelGuard dropped too many times"
 243            );
 244            remote_models.retain_count -= 1;
 245            if remote_models.retain_count == 0 {
 246                remote_models.buffers.clear();
 247                remote_models.worktrees.clear();
 248            }
 249        }
 250    }
 251}
 252/// Message ordered with respect to buffer operations
 253#[derive(Debug)]
 254enum BufferOrderedMessage {
 255    Operation {
 256        buffer_id: BufferId,
 257        operation: proto::Operation,
 258    },
 259    LanguageServerUpdate {
 260        language_server_id: LanguageServerId,
 261        message: proto::update_language_server::Variant,
 262        name: Option<LanguageServerName>,
 263    },
 264    Resync,
 265}
 266
 267#[derive(Debug)]
 268enum ProjectClientState {
 269    /// Single-player mode.
 270    Local,
 271    /// Multi-player mode but still a local project.
 272    Shared { remote_id: u64 },
 273    /// Multi-player mode but working on a remote project.
 274    Remote {
 275        sharing_has_stopped: bool,
 276        capability: Capability,
 277        remote_id: u64,
 278        replica_id: ReplicaId,
 279    },
 280}
 281
 282#[derive(Clone, Debug, PartialEq)]
 283pub enum Event {
 284    LanguageServerAdded(LanguageServerId, LanguageServerName, Option<WorktreeId>),
 285    LanguageServerRemoved(LanguageServerId),
 286    LanguageServerLog(LanguageServerId, LanguageServerLogType, String),
 287    // [`lsp::notification::DidOpenTextDocument`] was sent to this server using the buffer data.
 288    // Zed's buffer-related data is updated accordingly.
 289    LanguageServerBufferRegistered {
 290        server_id: LanguageServerId,
 291        buffer_id: BufferId,
 292        buffer_abs_path: PathBuf,
 293        name: Option<LanguageServerName>,
 294    },
 295    ToggleLspLogs {
 296        server_id: LanguageServerId,
 297        enabled: bool,
 298        toggled_log_kind: LogKind,
 299    },
 300    Toast {
 301        notification_id: SharedString,
 302        message: String,
 303    },
 304    HideToast {
 305        notification_id: SharedString,
 306    },
 307    LanguageServerPrompt(LanguageServerPromptRequest),
 308    LanguageNotFound(Entity<Buffer>),
 309    ActiveEntryChanged(Option<ProjectEntryId>),
 310    ActivateProjectPanel,
 311    WorktreeAdded(WorktreeId),
 312    WorktreeOrderChanged,
 313    WorktreeRemoved(WorktreeId),
 314    WorktreeUpdatedEntries(WorktreeId, UpdatedEntriesSet),
 315    DiskBasedDiagnosticsStarted {
 316        language_server_id: LanguageServerId,
 317    },
 318    DiskBasedDiagnosticsFinished {
 319        language_server_id: LanguageServerId,
 320    },
 321    DiagnosticsUpdated {
 322        paths: Vec<ProjectPath>,
 323        language_server_id: LanguageServerId,
 324    },
 325    RemoteIdChanged(Option<u64>),
 326    DisconnectedFromHost,
 327    DisconnectedFromSshRemote,
 328    Closed,
 329    DeletedEntry(WorktreeId, ProjectEntryId),
 330    CollaboratorUpdated {
 331        old_peer_id: proto::PeerId,
 332        new_peer_id: proto::PeerId,
 333    },
 334    CollaboratorJoined(proto::PeerId),
 335    CollaboratorLeft(proto::PeerId),
 336    HostReshared,
 337    Reshared,
 338    Rejoined,
 339    RefreshInlayHints {
 340        server_id: LanguageServerId,
 341        request_id: Option<usize>,
 342    },
 343    RefreshCodeLens,
 344    RevealInProjectPanel(ProjectEntryId),
 345    SnippetEdit(BufferId, Vec<(lsp::Range, Snippet)>),
 346    ExpandedAllForEntry(WorktreeId, ProjectEntryId),
 347    EntryRenamed(ProjectTransaction, ProjectPath, PathBuf),
 348    AgentLocationChanged,
 349}
 350
 351pub struct AgentLocationChanged;
 352
 353pub enum DebugAdapterClientState {
 354    Starting(Task<Option<Arc<DebugAdapterClient>>>),
 355    Running(Arc<DebugAdapterClient>),
 356}
 357
 358#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
 359pub struct ProjectPath {
 360    pub worktree_id: WorktreeId,
 361    pub path: Arc<RelPath>,
 362}
 363
 364impl ProjectPath {
 365    pub fn from_file(value: &dyn language::File, cx: &App) -> Self {
 366        ProjectPath {
 367            worktree_id: value.worktree_id(cx),
 368            path: value.path().clone(),
 369        }
 370    }
 371
 372    pub fn from_proto(p: proto::ProjectPath) -> Option<Self> {
 373        Some(Self {
 374            worktree_id: WorktreeId::from_proto(p.worktree_id),
 375            path: RelPath::from_proto(&p.path).log_err()?,
 376        })
 377    }
 378
 379    pub fn to_proto(&self) -> proto::ProjectPath {
 380        proto::ProjectPath {
 381            worktree_id: self.worktree_id.to_proto(),
 382            path: self.path.as_ref().to_proto(),
 383        }
 384    }
 385
 386    pub fn root_path(worktree_id: WorktreeId) -> Self {
 387        Self {
 388            worktree_id,
 389            path: RelPath::empty().into(),
 390        }
 391    }
 392
 393    pub fn starts_with(&self, other: &ProjectPath) -> bool {
 394        self.worktree_id == other.worktree_id && self.path.starts_with(&other.path)
 395    }
 396}
 397
 398#[derive(Debug, Default)]
 399pub enum PrepareRenameResponse {
 400    Success(Range<Anchor>),
 401    OnlyUnpreparedRenameSupported,
 402    #[default]
 403    InvalidPosition,
 404}
 405
 406#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 407pub enum InlayId {
 408    EditPrediction(usize),
 409    DebuggerValue(usize),
 410    // LSP
 411    Hint(usize),
 412    Color(usize),
 413}
 414
 415impl InlayId {
 416    pub fn id(&self) -> usize {
 417        match self {
 418            Self::EditPrediction(id) => *id,
 419            Self::DebuggerValue(id) => *id,
 420            Self::Hint(id) => *id,
 421            Self::Color(id) => *id,
 422        }
 423    }
 424}
 425
 426#[derive(Debug, Clone, PartialEq, Eq)]
 427pub struct InlayHint {
 428    pub position: language::Anchor,
 429    pub label: InlayHintLabel,
 430    pub kind: Option<InlayHintKind>,
 431    pub padding_left: bool,
 432    pub padding_right: bool,
 433    pub tooltip: Option<InlayHintTooltip>,
 434    pub resolve_state: ResolveState,
 435}
 436
 437/// The user's intent behind a given completion confirmation
 438#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
 439pub enum CompletionIntent {
 440    /// The user intends to 'commit' this result, if possible
 441    /// completion confirmations should run side effects.
 442    ///
 443    /// For LSP completions, will respect the setting `completions.lsp_insert_mode`.
 444    Complete,
 445    /// Similar to [Self::Complete], but behaves like `lsp_insert_mode` is set to `insert`.
 446    CompleteWithInsert,
 447    /// Similar to [Self::Complete], but behaves like `lsp_insert_mode` is set to `replace`.
 448    CompleteWithReplace,
 449    /// The user intends to continue 'composing' this completion
 450    /// completion confirmations should not run side effects and
 451    /// let the user continue composing their action
 452    Compose,
 453}
 454
 455impl CompletionIntent {
 456    pub fn is_complete(&self) -> bool {
 457        self == &Self::Complete
 458    }
 459
 460    pub fn is_compose(&self) -> bool {
 461        self == &Self::Compose
 462    }
 463}
 464
 465/// Similar to `CoreCompletion`, but with extra metadata attached.
 466#[derive(Clone)]
 467pub struct Completion {
 468    /// The range of text that will be replaced by this completion.
 469    pub replace_range: Range<Anchor>,
 470    /// The new text that will be inserted.
 471    pub new_text: String,
 472    /// A label for this completion that is shown in the menu.
 473    pub label: CodeLabel,
 474    /// The documentation for this completion.
 475    pub documentation: Option<CompletionDocumentation>,
 476    /// Completion data source which it was constructed from.
 477    pub source: CompletionSource,
 478    /// A path to an icon for this completion that is shown in the menu.
 479    pub icon_path: Option<SharedString>,
 480    /// Whether to adjust indentation (the default) or not.
 481    pub insert_text_mode: Option<InsertTextMode>,
 482    /// An optional callback to invoke when this completion is confirmed.
 483    /// Returns, whether new completions should be retriggered after the current one.
 484    /// If `true` is returned, the editor will show a new completion menu after this completion is confirmed.
 485    /// if no confirmation is provided or `false` is returned, the completion will be committed.
 486    pub confirm: Option<Arc<dyn Send + Sync + Fn(CompletionIntent, &mut Window, &mut App) -> bool>>,
 487}
 488
 489#[derive(Debug, Clone)]
 490pub enum CompletionSource {
 491    Lsp {
 492        /// The alternate `insert` range, if provided by the LSP server.
 493        insert_range: Option<Range<Anchor>>,
 494        /// The id of the language server that produced this completion.
 495        server_id: LanguageServerId,
 496        /// The raw completion provided by the language server.
 497        lsp_completion: Box<lsp::CompletionItem>,
 498        /// A set of defaults for this completion item.
 499        lsp_defaults: Option<Arc<lsp::CompletionListItemDefaults>>,
 500        /// Whether this completion has been resolved, to ensure it happens once per completion.
 501        resolved: bool,
 502    },
 503    Dap {
 504        /// The sort text for this completion.
 505        sort_text: String,
 506    },
 507    Custom,
 508    BufferWord {
 509        word_range: Range<Anchor>,
 510        resolved: bool,
 511    },
 512}
 513
 514impl CompletionSource {
 515    pub fn server_id(&self) -> Option<LanguageServerId> {
 516        if let CompletionSource::Lsp { server_id, .. } = self {
 517            Some(*server_id)
 518        } else {
 519            None
 520        }
 521    }
 522
 523    pub fn lsp_completion(&self, apply_defaults: bool) -> Option<Cow<'_, lsp::CompletionItem>> {
 524        if let Self::Lsp {
 525            lsp_completion,
 526            lsp_defaults,
 527            ..
 528        } = self
 529        {
 530            if apply_defaults && let Some(lsp_defaults) = lsp_defaults {
 531                let mut completion_with_defaults = *lsp_completion.clone();
 532                let default_commit_characters = lsp_defaults.commit_characters.as_ref();
 533                let default_edit_range = lsp_defaults.edit_range.as_ref();
 534                let default_insert_text_format = lsp_defaults.insert_text_format.as_ref();
 535                let default_insert_text_mode = lsp_defaults.insert_text_mode.as_ref();
 536
 537                if default_commit_characters.is_some()
 538                    || default_edit_range.is_some()
 539                    || default_insert_text_format.is_some()
 540                    || default_insert_text_mode.is_some()
 541                {
 542                    if completion_with_defaults.commit_characters.is_none()
 543                        && default_commit_characters.is_some()
 544                    {
 545                        completion_with_defaults.commit_characters =
 546                            default_commit_characters.cloned()
 547                    }
 548                    if completion_with_defaults.text_edit.is_none() {
 549                        match default_edit_range {
 550                            Some(lsp::CompletionListItemDefaultsEditRange::Range(range)) => {
 551                                completion_with_defaults.text_edit =
 552                                    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 553                                        range: *range,
 554                                        new_text: completion_with_defaults.label.clone(),
 555                                    }))
 556                            }
 557                            Some(lsp::CompletionListItemDefaultsEditRange::InsertAndReplace {
 558                                insert,
 559                                replace,
 560                            }) => {
 561                                completion_with_defaults.text_edit =
 562                                    Some(lsp::CompletionTextEdit::InsertAndReplace(
 563                                        lsp::InsertReplaceEdit {
 564                                            new_text: completion_with_defaults.label.clone(),
 565                                            insert: *insert,
 566                                            replace: *replace,
 567                                        },
 568                                    ))
 569                            }
 570                            None => {}
 571                        }
 572                    }
 573                    if completion_with_defaults.insert_text_format.is_none()
 574                        && default_insert_text_format.is_some()
 575                    {
 576                        completion_with_defaults.insert_text_format =
 577                            default_insert_text_format.cloned()
 578                    }
 579                    if completion_with_defaults.insert_text_mode.is_none()
 580                        && default_insert_text_mode.is_some()
 581                    {
 582                        completion_with_defaults.insert_text_mode =
 583                            default_insert_text_mode.cloned()
 584                    }
 585                }
 586                return Some(Cow::Owned(completion_with_defaults));
 587            }
 588            Some(Cow::Borrowed(lsp_completion))
 589        } else {
 590            None
 591        }
 592    }
 593}
 594
 595impl std::fmt::Debug for Completion {
 596    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 597        f.debug_struct("Completion")
 598            .field("replace_range", &self.replace_range)
 599            .field("new_text", &self.new_text)
 600            .field("label", &self.label)
 601            .field("documentation", &self.documentation)
 602            .field("source", &self.source)
 603            .finish()
 604    }
 605}
 606
 607/// Response from a source of completions.
 608pub struct CompletionResponse {
 609    pub completions: Vec<Completion>,
 610    pub display_options: CompletionDisplayOptions,
 611    /// When false, indicates that the list is complete and so does not need to be re-queried if it
 612    /// can be filtered instead.
 613    pub is_incomplete: bool,
 614}
 615
 616#[derive(Default)]
 617pub struct CompletionDisplayOptions {
 618    pub dynamic_width: bool,
 619}
 620
 621impl CompletionDisplayOptions {
 622    pub fn merge(&mut self, other: &CompletionDisplayOptions) {
 623        self.dynamic_width = self.dynamic_width && other.dynamic_width;
 624    }
 625}
 626
 627/// Response from language server completion request.
 628#[derive(Clone, Debug, Default)]
 629pub(crate) struct CoreCompletionResponse {
 630    pub completions: Vec<CoreCompletion>,
 631    /// When false, indicates that the list is complete and so does not need to be re-queried if it
 632    /// can be filtered instead.
 633    pub is_incomplete: bool,
 634}
 635
 636/// A generic completion that can come from different sources.
 637#[derive(Clone, Debug)]
 638pub(crate) struct CoreCompletion {
 639    replace_range: Range<Anchor>,
 640    new_text: String,
 641    source: CompletionSource,
 642}
 643
 644/// A code action provided by a language server.
 645#[derive(Clone, Debug, PartialEq)]
 646pub struct CodeAction {
 647    /// The id of the language server that produced this code action.
 648    pub server_id: LanguageServerId,
 649    /// The range of the buffer where this code action is applicable.
 650    pub range: Range<Anchor>,
 651    /// The raw code action provided by the language server.
 652    /// Can be either an action or a command.
 653    pub lsp_action: LspAction,
 654    /// Whether the action needs to be resolved using the language server.
 655    pub resolved: bool,
 656}
 657
 658/// An action sent back by a language server.
 659#[derive(Clone, Debug, PartialEq)]
 660pub enum LspAction {
 661    /// An action with the full data, may have a command or may not.
 662    /// May require resolving.
 663    Action(Box<lsp::CodeAction>),
 664    /// A command data to run as an action.
 665    Command(lsp::Command),
 666    /// A code lens data to run as an action.
 667    CodeLens(lsp::CodeLens),
 668}
 669
 670impl LspAction {
 671    pub fn title(&self) -> &str {
 672        match self {
 673            Self::Action(action) => &action.title,
 674            Self::Command(command) => &command.title,
 675            Self::CodeLens(lens) => lens
 676                .command
 677                .as_ref()
 678                .map(|command| command.title.as_str())
 679                .unwrap_or("Unknown command"),
 680        }
 681    }
 682
 683    fn action_kind(&self) -> Option<lsp::CodeActionKind> {
 684        match self {
 685            Self::Action(action) => action.kind.clone(),
 686            Self::Command(_) => Some(lsp::CodeActionKind::new("command")),
 687            Self::CodeLens(_) => Some(lsp::CodeActionKind::new("code lens")),
 688        }
 689    }
 690
 691    fn edit(&self) -> Option<&lsp::WorkspaceEdit> {
 692        match self {
 693            Self::Action(action) => action.edit.as_ref(),
 694            Self::Command(_) => None,
 695            Self::CodeLens(_) => None,
 696        }
 697    }
 698
 699    fn command(&self) -> Option<&lsp::Command> {
 700        match self {
 701            Self::Action(action) => action.command.as_ref(),
 702            Self::Command(command) => Some(command),
 703            Self::CodeLens(lens) => lens.command.as_ref(),
 704        }
 705    }
 706}
 707
 708#[derive(Debug, Clone, PartialEq, Eq)]
 709pub enum ResolveState {
 710    Resolved,
 711    CanResolve(LanguageServerId, Option<lsp::LSPAny>),
 712    Resolving,
 713}
 714impl InlayHint {
 715    pub fn text(&self) -> Rope {
 716        match &self.label {
 717            InlayHintLabel::String(s) => Rope::from(s),
 718            InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &*part.value).collect(),
 719        }
 720    }
 721}
 722
 723#[derive(Debug, Clone, PartialEq, Eq)]
 724pub enum InlayHintLabel {
 725    String(String),
 726    LabelParts(Vec<InlayHintLabelPart>),
 727}
 728
 729#[derive(Debug, Clone, PartialEq, Eq)]
 730pub struct InlayHintLabelPart {
 731    pub value: String,
 732    pub tooltip: Option<InlayHintLabelPartTooltip>,
 733    pub location: Option<(LanguageServerId, lsp::Location)>,
 734}
 735
 736#[derive(Debug, Clone, PartialEq, Eq)]
 737pub enum InlayHintTooltip {
 738    String(String),
 739    MarkupContent(MarkupContent),
 740}
 741
 742#[derive(Debug, Clone, PartialEq, Eq)]
 743pub enum InlayHintLabelPartTooltip {
 744    String(String),
 745    MarkupContent(MarkupContent),
 746}
 747
 748#[derive(Debug, Clone, PartialEq, Eq)]
 749pub struct MarkupContent {
 750    pub kind: HoverBlockKind,
 751    pub value: String,
 752}
 753
 754#[derive(Debug, Clone, PartialEq)]
 755pub struct LocationLink {
 756    pub origin: Option<Location>,
 757    pub target: Location,
 758}
 759
 760#[derive(Debug)]
 761pub struct DocumentHighlight {
 762    pub range: Range<language::Anchor>,
 763    pub kind: DocumentHighlightKind,
 764}
 765
 766#[derive(Clone, Debug)]
 767pub struct Symbol {
 768    pub language_server_name: LanguageServerName,
 769    pub source_worktree_id: WorktreeId,
 770    pub source_language_server_id: LanguageServerId,
 771    pub path: SymbolLocation,
 772    pub label: CodeLabel,
 773    pub name: String,
 774    pub kind: lsp::SymbolKind,
 775    pub range: Range<Unclipped<PointUtf16>>,
 776}
 777
 778#[derive(Clone, Debug)]
 779pub struct DocumentSymbol {
 780    pub name: String,
 781    pub kind: lsp::SymbolKind,
 782    pub range: Range<Unclipped<PointUtf16>>,
 783    pub selection_range: Range<Unclipped<PointUtf16>>,
 784    pub children: Vec<DocumentSymbol>,
 785}
 786
 787#[derive(Clone, Debug, PartialEq)]
 788pub struct HoverBlock {
 789    pub text: String,
 790    pub kind: HoverBlockKind,
 791}
 792
 793#[derive(Clone, Debug, PartialEq, Eq)]
 794pub enum HoverBlockKind {
 795    PlainText,
 796    Markdown,
 797    Code { language: String },
 798}
 799
 800#[derive(Debug, Clone)]
 801pub struct Hover {
 802    pub contents: Vec<HoverBlock>,
 803    pub range: Option<Range<language::Anchor>>,
 804    pub language: Option<Arc<Language>>,
 805}
 806
 807impl Hover {
 808    pub fn is_empty(&self) -> bool {
 809        self.contents.iter().all(|block| block.text.is_empty())
 810    }
 811}
 812
 813enum EntitySubscription {
 814    Project(PendingEntitySubscription<Project>),
 815    BufferStore(PendingEntitySubscription<BufferStore>),
 816    GitStore(PendingEntitySubscription<GitStore>),
 817    WorktreeStore(PendingEntitySubscription<WorktreeStore>),
 818    LspStore(PendingEntitySubscription<LspStore>),
 819    SettingsObserver(PendingEntitySubscription<SettingsObserver>),
 820    DapStore(PendingEntitySubscription<DapStore>),
 821}
 822
 823#[derive(Debug, Clone)]
 824pub struct DirectoryItem {
 825    pub path: PathBuf,
 826    pub is_dir: bool,
 827}
 828
 829#[derive(Clone, Debug, PartialEq)]
 830pub struct DocumentColor {
 831    pub lsp_range: lsp::Range,
 832    pub color: lsp::Color,
 833    pub resolved: bool,
 834    pub color_presentations: Vec<ColorPresentation>,
 835}
 836
 837impl Eq for DocumentColor {}
 838
 839impl std::hash::Hash for DocumentColor {
 840    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 841        self.lsp_range.hash(state);
 842        self.color.red.to_bits().hash(state);
 843        self.color.green.to_bits().hash(state);
 844        self.color.blue.to_bits().hash(state);
 845        self.color.alpha.to_bits().hash(state);
 846        self.resolved.hash(state);
 847        self.color_presentations.hash(state);
 848    }
 849}
 850
 851#[derive(Clone, Debug, PartialEq, Eq)]
 852pub struct ColorPresentation {
 853    pub label: SharedString,
 854    pub text_edit: Option<lsp::TextEdit>,
 855    pub additional_text_edits: Vec<lsp::TextEdit>,
 856}
 857
 858impl std::hash::Hash for ColorPresentation {
 859    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 860        self.label.hash(state);
 861        if let Some(ref edit) = self.text_edit {
 862            edit.range.hash(state);
 863            edit.new_text.hash(state);
 864        }
 865        self.additional_text_edits.len().hash(state);
 866        for edit in &self.additional_text_edits {
 867            edit.range.hash(state);
 868            edit.new_text.hash(state);
 869        }
 870    }
 871}
 872
 873#[derive(Clone)]
 874pub enum DirectoryLister {
 875    Project(Entity<Project>),
 876    Local(Entity<Project>, Arc<dyn Fs>),
 877}
 878
 879impl std::fmt::Debug for DirectoryLister {
 880    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 881        match self {
 882            DirectoryLister::Project(project) => {
 883                write!(f, "DirectoryLister::Project({project:?})")
 884            }
 885            DirectoryLister::Local(project, _) => {
 886                write!(f, "DirectoryLister::Local({project:?})")
 887            }
 888        }
 889    }
 890}
 891
 892impl DirectoryLister {
 893    pub fn is_local(&self, cx: &App) -> bool {
 894        match self {
 895            DirectoryLister::Local(..) => true,
 896            DirectoryLister::Project(project) => project.read(cx).is_local(),
 897        }
 898    }
 899
 900    pub fn resolve_tilde<'a>(&self, path: &'a String, cx: &App) -> Cow<'a, str> {
 901        if self.is_local(cx) {
 902            shellexpand::tilde(path)
 903        } else {
 904            Cow::from(path)
 905        }
 906    }
 907
 908    pub fn default_query(&self, cx: &mut App) -> String {
 909        let project = match self {
 910            DirectoryLister::Project(project) => project,
 911            DirectoryLister::Local(project, _) => project,
 912        }
 913        .read(cx);
 914        let path_style = project.path_style(cx);
 915        project
 916            .visible_worktrees(cx)
 917            .next()
 918            .map(|worktree| worktree.read(cx).abs_path().to_string_lossy().into_owned())
 919            .or_else(|| std::env::home_dir().map(|dir| dir.to_string_lossy().into_owned()))
 920            .map(|mut s| {
 921                s.push_str(path_style.separator());
 922                s
 923            })
 924            .unwrap_or_else(|| {
 925                if path_style.is_windows() {
 926                    "C:\\"
 927                } else {
 928                    "~/"
 929                }
 930                .to_string()
 931            })
 932    }
 933
 934    pub fn list_directory(&self, path: String, cx: &mut App) -> Task<Result<Vec<DirectoryItem>>> {
 935        match self {
 936            DirectoryLister::Project(project) => {
 937                project.update(cx, |project, cx| project.list_directory(path, cx))
 938            }
 939            DirectoryLister::Local(_, fs) => {
 940                let fs = fs.clone();
 941                cx.background_spawn(async move {
 942                    let mut results = vec![];
 943                    let expanded = shellexpand::tilde(&path);
 944                    let query = Path::new(expanded.as_ref());
 945                    let mut response = fs.read_dir(query).await?;
 946                    while let Some(path) = response.next().await {
 947                        let path = path?;
 948                        if let Some(file_name) = path.file_name() {
 949                            results.push(DirectoryItem {
 950                                path: PathBuf::from(file_name.to_os_string()),
 951                                is_dir: fs.is_dir(&path).await,
 952                            });
 953                        }
 954                    }
 955                    Ok(results)
 956                })
 957            }
 958        }
 959    }
 960}
 961
 962#[cfg(any(test, feature = "test-support"))]
 963pub const DEFAULT_COMPLETION_CONTEXT: CompletionContext = CompletionContext {
 964    trigger_kind: lsp::CompletionTriggerKind::INVOKED,
 965    trigger_character: None,
 966};
 967
 968/// An LSP diagnostics associated with a certain language server.
 969#[derive(Clone, Debug, Default)]
 970pub enum LspPullDiagnostics {
 971    #[default]
 972    Default,
 973    Response {
 974        /// The id of the language server that produced diagnostics.
 975        server_id: LanguageServerId,
 976        /// URI of the resource,
 977        uri: lsp::Uri,
 978        /// The diagnostics produced by this language server.
 979        diagnostics: PulledDiagnostics,
 980    },
 981}
 982
 983#[derive(Clone, Debug)]
 984pub enum PulledDiagnostics {
 985    Unchanged {
 986        /// An ID the current pulled batch for this file.
 987        /// If given, can be used to query workspace diagnostics partially.
 988        result_id: String,
 989    },
 990    Changed {
 991        result_id: Option<String>,
 992        diagnostics: Vec<lsp::Diagnostic>,
 993    },
 994}
 995
 996/// Whether to disable all AI features in Zed.
 997///
 998/// Default: false
 999#[derive(Copy, Clone, Debug)]
1000pub struct DisableAiSettings {
1001    pub disable_ai: bool,
1002}
1003
1004impl settings::Settings for DisableAiSettings {
1005    fn from_settings(content: &settings::SettingsContent) -> Self {
1006        Self {
1007            disable_ai: content.disable_ai.unwrap().0,
1008        }
1009    }
1010}
1011
1012impl Project {
1013    pub fn init_settings(cx: &mut App) {
1014        WorktreeSettings::register(cx);
1015        ProjectSettings::register(cx);
1016        DisableAiSettings::register(cx);
1017        AllAgentServersSettings::register(cx);
1018    }
1019
1020    pub fn init(client: &Arc<Client>, cx: &mut App) {
1021        connection_manager::init(client.clone(), cx);
1022        Self::init_settings(cx);
1023
1024        let client: AnyProtoClient = client.clone().into();
1025        client.add_entity_message_handler(Self::handle_add_collaborator);
1026        client.add_entity_message_handler(Self::handle_update_project_collaborator);
1027        client.add_entity_message_handler(Self::handle_remove_collaborator);
1028        client.add_entity_message_handler(Self::handle_update_project);
1029        client.add_entity_message_handler(Self::handle_unshare_project);
1030        client.add_entity_request_handler(Self::handle_update_buffer);
1031        client.add_entity_message_handler(Self::handle_update_worktree);
1032        client.add_entity_request_handler(Self::handle_synchronize_buffers);
1033
1034        client.add_entity_request_handler(Self::handle_search_candidate_buffers);
1035        client.add_entity_request_handler(Self::handle_open_buffer_by_id);
1036        client.add_entity_request_handler(Self::handle_open_buffer_by_path);
1037        client.add_entity_request_handler(Self::handle_open_new_buffer);
1038        client.add_entity_message_handler(Self::handle_create_buffer_for_peer);
1039        client.add_entity_message_handler(Self::handle_toggle_lsp_logs);
1040        client.add_entity_message_handler(Self::handle_create_image_for_peer);
1041
1042        WorktreeStore::init(&client);
1043        BufferStore::init(&client);
1044        LspStore::init(&client);
1045        GitStore::init(&client);
1046        SettingsObserver::init(&client);
1047        TaskStore::init(Some(&client));
1048        ToolchainStore::init(&client);
1049        DapStore::init(&client, cx);
1050        BreakpointStore::init(&client);
1051        context_server_store::init(cx);
1052    }
1053
1054    pub fn local(
1055        client: Arc<Client>,
1056        node: NodeRuntime,
1057        user_store: Entity<UserStore>,
1058        languages: Arc<LanguageRegistry>,
1059        fs: Arc<dyn Fs>,
1060        env: Option<HashMap<String, String>>,
1061        cx: &mut App,
1062    ) -> Entity<Self> {
1063        cx.new(|cx: &mut Context<Self>| {
1064            let (tx, rx) = mpsc::unbounded();
1065            cx.spawn(async move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx).await)
1066                .detach();
1067            let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx);
1068            let worktree_store = cx.new(|_| WorktreeStore::local(false, fs.clone()));
1069            cx.subscribe(&worktree_store, Self::on_worktree_store_event)
1070                .detach();
1071
1072            let weak_self = cx.weak_entity();
1073            let context_server_store =
1074                cx.new(|cx| ContextServerStore::new(worktree_store.clone(), weak_self.clone(), cx));
1075
1076            let environment = cx.new(|cx| {
1077                ProjectEnvironment::new(env, worktree_store.downgrade(), None, false, cx)
1078            });
1079            let manifest_tree = ManifestTree::new(worktree_store.clone(), cx);
1080            let toolchain_store = cx.new(|cx| {
1081                ToolchainStore::local(
1082                    languages.clone(),
1083                    worktree_store.clone(),
1084                    environment.clone(),
1085                    manifest_tree.clone(),
1086                    fs.clone(),
1087                    cx,
1088                )
1089            });
1090
1091            let buffer_store = cx.new(|cx| BufferStore::local(worktree_store.clone(), cx));
1092            cx.subscribe(&buffer_store, Self::on_buffer_store_event)
1093                .detach();
1094
1095            let breakpoint_store =
1096                cx.new(|_| BreakpointStore::local(worktree_store.clone(), buffer_store.clone()));
1097
1098            let dap_store = cx.new(|cx| {
1099                DapStore::new_local(
1100                    client.http_client(),
1101                    node.clone(),
1102                    fs.clone(),
1103                    environment.clone(),
1104                    toolchain_store.read(cx).as_language_toolchain_store(),
1105                    worktree_store.clone(),
1106                    breakpoint_store.clone(),
1107                    false,
1108                    cx,
1109                )
1110            });
1111            cx.subscribe(&dap_store, Self::on_dap_store_event).detach();
1112
1113            let image_store = cx.new(|cx| ImageStore::local(worktree_store.clone(), cx));
1114            cx.subscribe(&image_store, Self::on_image_store_event)
1115                .detach();
1116
1117            let prettier_store = cx.new(|cx| {
1118                PrettierStore::new(
1119                    node.clone(),
1120                    fs.clone(),
1121                    languages.clone(),
1122                    worktree_store.clone(),
1123                    cx,
1124                )
1125            });
1126
1127            let task_store = cx.new(|cx| {
1128                TaskStore::local(
1129                    buffer_store.downgrade(),
1130                    worktree_store.clone(),
1131                    toolchain_store.read(cx).as_language_toolchain_store(),
1132                    environment.clone(),
1133                    cx,
1134                )
1135            });
1136
1137            let settings_observer = cx.new(|cx| {
1138                SettingsObserver::new_local(
1139                    fs.clone(),
1140                    worktree_store.clone(),
1141                    task_store.clone(),
1142                    cx,
1143                )
1144            });
1145            cx.subscribe(&settings_observer, Self::on_settings_observer_event)
1146                .detach();
1147
1148            let lsp_store = cx.new(|cx| {
1149                LspStore::new_local(
1150                    buffer_store.clone(),
1151                    worktree_store.clone(),
1152                    prettier_store.clone(),
1153                    toolchain_store
1154                        .read(cx)
1155                        .as_local_store()
1156                        .expect("Toolchain store to be local")
1157                        .clone(),
1158                    environment.clone(),
1159                    manifest_tree,
1160                    languages.clone(),
1161                    client.http_client(),
1162                    fs.clone(),
1163                    cx,
1164                )
1165            });
1166
1167            let git_store = cx.new(|cx| {
1168                GitStore::local(
1169                    &worktree_store,
1170                    buffer_store.clone(),
1171                    environment.clone(),
1172                    fs.clone(),
1173                    cx,
1174                )
1175            });
1176
1177            let agent_server_store = cx.new(|cx| {
1178                AgentServerStore::local(
1179                    node.clone(),
1180                    fs.clone(),
1181                    environment.clone(),
1182                    client.http_client(),
1183                    cx,
1184                )
1185            });
1186
1187            cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
1188
1189            Self {
1190                buffer_ordered_messages_tx: tx,
1191                collaborators: Default::default(),
1192                worktree_store,
1193                buffer_store,
1194                image_store,
1195                lsp_store,
1196                context_server_store,
1197                join_project_response_message_id: 0,
1198                client_state: ProjectClientState::Local,
1199                git_store,
1200                client_subscriptions: Vec::new(),
1201                _subscriptions: vec![cx.on_release(Self::release)],
1202                active_entry: None,
1203                snippets,
1204                languages,
1205                collab_client: client,
1206                task_store,
1207                user_store,
1208                settings_observer,
1209                fs,
1210                remote_client: None,
1211                breakpoint_store,
1212                dap_store,
1213                agent_server_store,
1214
1215                buffers_needing_diff: Default::default(),
1216                git_diff_debouncer: DebouncedDelay::new(),
1217                terminals: Terminals {
1218                    local_handles: Vec::new(),
1219                },
1220                node: Some(node),
1221                search_history: Self::new_search_history(),
1222                environment,
1223                remotely_created_models: Default::default(),
1224
1225                search_included_history: Self::new_search_history(),
1226                search_excluded_history: Self::new_search_history(),
1227
1228                toolchain_store: Some(toolchain_store),
1229
1230                agent_location: None,
1231            }
1232        })
1233    }
1234
1235    pub fn remote(
1236        remote: Entity<RemoteClient>,
1237        client: Arc<Client>,
1238        node: NodeRuntime,
1239        user_store: Entity<UserStore>,
1240        languages: Arc<LanguageRegistry>,
1241        fs: Arc<dyn Fs>,
1242        cx: &mut App,
1243    ) -> Entity<Self> {
1244        cx.new(|cx: &mut Context<Self>| {
1245            let (tx, rx) = mpsc::unbounded();
1246            cx.spawn(async move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx).await)
1247                .detach();
1248            let global_snippets_dir = paths::snippets_dir().to_owned();
1249            let snippets =
1250                SnippetProvider::new(fs.clone(), BTreeSet::from_iter([global_snippets_dir]), cx);
1251
1252            let (remote_proto, path_style) =
1253                remote.read_with(cx, |remote, _| (remote.proto_client(), remote.path_style()));
1254            let worktree_store = cx.new(|_| {
1255                WorktreeStore::remote(
1256                    false,
1257                    remote_proto.clone(),
1258                    REMOTE_SERVER_PROJECT_ID,
1259                    path_style,
1260                )
1261            });
1262            cx.subscribe(&worktree_store, Self::on_worktree_store_event)
1263                .detach();
1264
1265            let weak_self = cx.weak_entity();
1266            let context_server_store =
1267                cx.new(|cx| ContextServerStore::new(worktree_store.clone(), weak_self.clone(), cx));
1268
1269            let buffer_store = cx.new(|cx| {
1270                BufferStore::remote(
1271                    worktree_store.clone(),
1272                    remote.read(cx).proto_client(),
1273                    REMOTE_SERVER_PROJECT_ID,
1274                    cx,
1275                )
1276            });
1277            let image_store = cx.new(|cx| {
1278                ImageStore::remote(
1279                    worktree_store.clone(),
1280                    remote.read(cx).proto_client(),
1281                    REMOTE_SERVER_PROJECT_ID,
1282                    cx,
1283                )
1284            });
1285            cx.subscribe(&buffer_store, Self::on_buffer_store_event)
1286                .detach();
1287            let toolchain_store = cx.new(|cx| {
1288                ToolchainStore::remote(REMOTE_SERVER_PROJECT_ID, remote.read(cx).proto_client(), cx)
1289            });
1290            let task_store = cx.new(|cx| {
1291                TaskStore::remote(
1292                    buffer_store.downgrade(),
1293                    worktree_store.clone(),
1294                    toolchain_store.read(cx).as_language_toolchain_store(),
1295                    remote.read(cx).proto_client(),
1296                    REMOTE_SERVER_PROJECT_ID,
1297                    cx,
1298                )
1299            });
1300
1301            let settings_observer = cx.new(|cx| {
1302                SettingsObserver::new_remote(
1303                    fs.clone(),
1304                    worktree_store.clone(),
1305                    task_store.clone(),
1306                    Some(remote_proto.clone()),
1307                    cx,
1308                )
1309            });
1310            cx.subscribe(&settings_observer, Self::on_settings_observer_event)
1311                .detach();
1312
1313            let environment = cx.new(|cx| {
1314                ProjectEnvironment::new(
1315                    None,
1316                    worktree_store.downgrade(),
1317                    Some(remote.downgrade()),
1318                    false,
1319                    cx,
1320                )
1321            });
1322
1323            let lsp_store = cx.new(|cx| {
1324                LspStore::new_remote(
1325                    buffer_store.clone(),
1326                    worktree_store.clone(),
1327                    languages.clone(),
1328                    remote_proto.clone(),
1329                    REMOTE_SERVER_PROJECT_ID,
1330                    cx,
1331                )
1332            });
1333            cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
1334
1335            let breakpoint_store =
1336                cx.new(|_| BreakpointStore::remote(REMOTE_SERVER_PROJECT_ID, remote_proto.clone()));
1337
1338            let dap_store = cx.new(|cx| {
1339                DapStore::new_remote(
1340                    REMOTE_SERVER_PROJECT_ID,
1341                    remote.clone(),
1342                    breakpoint_store.clone(),
1343                    worktree_store.clone(),
1344                    node.clone(),
1345                    client.http_client(),
1346                    fs.clone(),
1347                    cx,
1348                )
1349            });
1350
1351            let git_store = cx.new(|cx| {
1352                GitStore::remote(
1353                    &worktree_store,
1354                    buffer_store.clone(),
1355                    remote_proto.clone(),
1356                    REMOTE_SERVER_PROJECT_ID,
1357                    cx,
1358                )
1359            });
1360
1361            let agent_server_store =
1362                cx.new(|_| AgentServerStore::remote(REMOTE_SERVER_PROJECT_ID, remote.clone()));
1363
1364            cx.subscribe(&remote, Self::on_remote_client_event).detach();
1365
1366            let this = Self {
1367                buffer_ordered_messages_tx: tx,
1368                collaborators: Default::default(),
1369                worktree_store,
1370                buffer_store,
1371                image_store,
1372                lsp_store,
1373                context_server_store,
1374                breakpoint_store,
1375                dap_store,
1376                join_project_response_message_id: 0,
1377                client_state: ProjectClientState::Local,
1378                git_store,
1379                agent_server_store,
1380                client_subscriptions: Vec::new(),
1381                _subscriptions: vec![
1382                    cx.on_release(Self::release),
1383                    cx.on_app_quit(|this, cx| {
1384                        let shutdown = this.remote_client.take().and_then(|client| {
1385                            client.update(cx, |client, cx| {
1386                                client.shutdown_processes(
1387                                    Some(proto::ShutdownRemoteServer {}),
1388                                    cx.background_executor().clone(),
1389                                )
1390                            })
1391                        });
1392
1393                        cx.background_executor().spawn(async move {
1394                            if let Some(shutdown) = shutdown {
1395                                shutdown.await;
1396                            }
1397                        })
1398                    }),
1399                ],
1400                active_entry: None,
1401                snippets,
1402                languages,
1403                collab_client: client,
1404                task_store,
1405                user_store,
1406                settings_observer,
1407                fs,
1408                remote_client: Some(remote.clone()),
1409                buffers_needing_diff: Default::default(),
1410                git_diff_debouncer: DebouncedDelay::new(),
1411                terminals: Terminals {
1412                    local_handles: Vec::new(),
1413                },
1414                node: Some(node),
1415                search_history: Self::new_search_history(),
1416                environment,
1417                remotely_created_models: Default::default(),
1418
1419                search_included_history: Self::new_search_history(),
1420                search_excluded_history: Self::new_search_history(),
1421
1422                toolchain_store: Some(toolchain_store),
1423                agent_location: None,
1424            };
1425
1426            // remote server -> local machine handlers
1427            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &cx.entity());
1428            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.buffer_store);
1429            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.worktree_store);
1430            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.lsp_store);
1431            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.dap_store);
1432            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.settings_observer);
1433            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.git_store);
1434            remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.agent_server_store);
1435
1436            remote_proto.add_entity_message_handler(Self::handle_create_buffer_for_peer);
1437            remote_proto.add_entity_message_handler(Self::handle_create_image_for_peer);
1438            remote_proto.add_entity_message_handler(Self::handle_update_worktree);
1439            remote_proto.add_entity_message_handler(Self::handle_update_project);
1440            remote_proto.add_entity_message_handler(Self::handle_toast);
1441            remote_proto.add_entity_request_handler(Self::handle_language_server_prompt_request);
1442            remote_proto.add_entity_message_handler(Self::handle_hide_toast);
1443            remote_proto.add_entity_request_handler(Self::handle_update_buffer_from_remote_server);
1444            BufferStore::init(&remote_proto);
1445            LspStore::init(&remote_proto);
1446            SettingsObserver::init(&remote_proto);
1447            TaskStore::init(Some(&remote_proto));
1448            ToolchainStore::init(&remote_proto);
1449            DapStore::init(&remote_proto, cx);
1450            GitStore::init(&remote_proto);
1451            AgentServerStore::init_remote(&remote_proto);
1452
1453            this
1454        })
1455    }
1456
1457    pub async fn in_room(
1458        remote_id: u64,
1459        client: Arc<Client>,
1460        user_store: Entity<UserStore>,
1461        languages: Arc<LanguageRegistry>,
1462        fs: Arc<dyn Fs>,
1463        cx: AsyncApp,
1464    ) -> Result<Entity<Self>> {
1465        client.connect(true, &cx).await.into_response()?;
1466
1467        let subscriptions = [
1468            EntitySubscription::Project(client.subscribe_to_entity::<Self>(remote_id)?),
1469            EntitySubscription::BufferStore(client.subscribe_to_entity::<BufferStore>(remote_id)?),
1470            EntitySubscription::GitStore(client.subscribe_to_entity::<GitStore>(remote_id)?),
1471            EntitySubscription::WorktreeStore(
1472                client.subscribe_to_entity::<WorktreeStore>(remote_id)?,
1473            ),
1474            EntitySubscription::LspStore(client.subscribe_to_entity::<LspStore>(remote_id)?),
1475            EntitySubscription::SettingsObserver(
1476                client.subscribe_to_entity::<SettingsObserver>(remote_id)?,
1477            ),
1478            EntitySubscription::DapStore(client.subscribe_to_entity::<DapStore>(remote_id)?),
1479        ];
1480        let committer = get_git_committer(&cx).await;
1481        let response = client
1482            .request_envelope(proto::JoinProject {
1483                project_id: remote_id,
1484                committer_email: committer.email,
1485                committer_name: committer.name,
1486            })
1487            .await?;
1488        Self::from_join_project_response(
1489            response,
1490            subscriptions,
1491            client,
1492            false,
1493            user_store,
1494            languages,
1495            fs,
1496            cx,
1497        )
1498        .await
1499    }
1500
1501    async fn from_join_project_response(
1502        response: TypedEnvelope<proto::JoinProjectResponse>,
1503        subscriptions: [EntitySubscription; 7],
1504        client: Arc<Client>,
1505        run_tasks: bool,
1506        user_store: Entity<UserStore>,
1507        languages: Arc<LanguageRegistry>,
1508        fs: Arc<dyn Fs>,
1509        mut cx: AsyncApp,
1510    ) -> Result<Entity<Self>> {
1511        let remote_id = response.payload.project_id;
1512        let role = response.payload.role();
1513
1514        let path_style = if response.payload.windows_paths {
1515            PathStyle::Windows
1516        } else {
1517            PathStyle::Posix
1518        };
1519
1520        let worktree_store = cx.new(|_| {
1521            WorktreeStore::remote(
1522                true,
1523                client.clone().into(),
1524                response.payload.project_id,
1525                path_style,
1526            )
1527        })?;
1528        let buffer_store = cx.new(|cx| {
1529            BufferStore::remote(worktree_store.clone(), client.clone().into(), remote_id, cx)
1530        })?;
1531        let image_store = cx.new(|cx| {
1532            ImageStore::remote(worktree_store.clone(), client.clone().into(), remote_id, cx)
1533        })?;
1534
1535        let environment =
1536            cx.new(|cx| ProjectEnvironment::new(None, worktree_store.downgrade(), None, true, cx))?;
1537        let breakpoint_store =
1538            cx.new(|_| BreakpointStore::remote(remote_id, client.clone().into()))?;
1539        let dap_store = cx.new(|cx| {
1540            DapStore::new_collab(
1541                remote_id,
1542                client.clone().into(),
1543                breakpoint_store.clone(),
1544                worktree_store.clone(),
1545                fs.clone(),
1546                cx,
1547            )
1548        })?;
1549
1550        let lsp_store = cx.new(|cx| {
1551            LspStore::new_remote(
1552                buffer_store.clone(),
1553                worktree_store.clone(),
1554                languages.clone(),
1555                client.clone().into(),
1556                remote_id,
1557                cx,
1558            )
1559        })?;
1560
1561        let task_store = cx.new(|cx| {
1562            if run_tasks {
1563                TaskStore::remote(
1564                    buffer_store.downgrade(),
1565                    worktree_store.clone(),
1566                    Arc::new(EmptyToolchainStore),
1567                    client.clone().into(),
1568                    remote_id,
1569                    cx,
1570                )
1571            } else {
1572                TaskStore::Noop
1573            }
1574        })?;
1575
1576        let settings_observer = cx.new(|cx| {
1577            SettingsObserver::new_remote(
1578                fs.clone(),
1579                worktree_store.clone(),
1580                task_store.clone(),
1581                None,
1582                cx,
1583            )
1584        })?;
1585
1586        let git_store = cx.new(|cx| {
1587            GitStore::remote(
1588                // In this remote case we pass None for the environment
1589                &worktree_store,
1590                buffer_store.clone(),
1591                client.clone().into(),
1592                remote_id,
1593                cx,
1594            )
1595        })?;
1596
1597        let agent_server_store = cx.new(|cx| AgentServerStore::collab(cx))?;
1598        let replica_id = ReplicaId::new(response.payload.replica_id as u16);
1599
1600        let project = cx.new(|cx| {
1601            let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx);
1602
1603            let weak_self = cx.weak_entity();
1604            let context_server_store =
1605                cx.new(|cx| ContextServerStore::new(worktree_store.clone(), weak_self, cx));
1606
1607            let mut worktrees = Vec::new();
1608            for worktree in response.payload.worktrees {
1609                let worktree = Worktree::remote(
1610                    remote_id,
1611                    replica_id,
1612                    worktree,
1613                    client.clone().into(),
1614                    path_style,
1615                    cx,
1616                );
1617                worktrees.push(worktree);
1618            }
1619
1620            let (tx, rx) = mpsc::unbounded();
1621            cx.spawn(async move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx).await)
1622                .detach();
1623
1624            cx.subscribe(&worktree_store, Self::on_worktree_store_event)
1625                .detach();
1626
1627            cx.subscribe(&buffer_store, Self::on_buffer_store_event)
1628                .detach();
1629            cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
1630            cx.subscribe(&settings_observer, Self::on_settings_observer_event)
1631                .detach();
1632
1633            cx.subscribe(&dap_store, Self::on_dap_store_event).detach();
1634
1635            let mut project = Self {
1636                buffer_ordered_messages_tx: tx,
1637                buffer_store: buffer_store.clone(),
1638                image_store,
1639                worktree_store: worktree_store.clone(),
1640                lsp_store: lsp_store.clone(),
1641                context_server_store,
1642                active_entry: None,
1643                collaborators: Default::default(),
1644                join_project_response_message_id: response.message_id,
1645                languages,
1646                user_store: user_store.clone(),
1647                task_store,
1648                snippets,
1649                fs,
1650                remote_client: None,
1651                settings_observer: settings_observer.clone(),
1652                client_subscriptions: Default::default(),
1653                _subscriptions: vec![cx.on_release(Self::release)],
1654                collab_client: client.clone(),
1655                client_state: ProjectClientState::Remote {
1656                    sharing_has_stopped: false,
1657                    capability: Capability::ReadWrite,
1658                    remote_id,
1659                    replica_id,
1660                },
1661                breakpoint_store,
1662                dap_store: dap_store.clone(),
1663                git_store: git_store.clone(),
1664                agent_server_store,
1665                buffers_needing_diff: Default::default(),
1666                git_diff_debouncer: DebouncedDelay::new(),
1667                terminals: Terminals {
1668                    local_handles: Vec::new(),
1669                },
1670                node: None,
1671                search_history: Self::new_search_history(),
1672                search_included_history: Self::new_search_history(),
1673                search_excluded_history: Self::new_search_history(),
1674                environment,
1675                remotely_created_models: Arc::new(Mutex::new(RemotelyCreatedModels::default())),
1676                toolchain_store: None,
1677                agent_location: None,
1678            };
1679            project.set_role(role, cx);
1680            for worktree in worktrees {
1681                project.add_worktree(&worktree, cx);
1682            }
1683            project
1684        })?;
1685
1686        let weak_project = project.downgrade();
1687        lsp_store
1688            .update(&mut cx, |lsp_store, cx| {
1689                lsp_store.set_language_server_statuses_from_proto(
1690                    weak_project,
1691                    response.payload.language_servers,
1692                    response.payload.language_server_capabilities,
1693                    cx,
1694                );
1695            })
1696            .ok();
1697
1698        let subscriptions = subscriptions
1699            .into_iter()
1700            .map(|s| match s {
1701                EntitySubscription::BufferStore(subscription) => {
1702                    subscription.set_entity(&buffer_store, &cx)
1703                }
1704                EntitySubscription::WorktreeStore(subscription) => {
1705                    subscription.set_entity(&worktree_store, &cx)
1706                }
1707                EntitySubscription::GitStore(subscription) => {
1708                    subscription.set_entity(&git_store, &cx)
1709                }
1710                EntitySubscription::SettingsObserver(subscription) => {
1711                    subscription.set_entity(&settings_observer, &cx)
1712                }
1713                EntitySubscription::Project(subscription) => subscription.set_entity(&project, &cx),
1714                EntitySubscription::LspStore(subscription) => {
1715                    subscription.set_entity(&lsp_store, &cx)
1716                }
1717                EntitySubscription::DapStore(subscription) => {
1718                    subscription.set_entity(&dap_store, &cx)
1719                }
1720            })
1721            .collect::<Vec<_>>();
1722
1723        let user_ids = response
1724            .payload
1725            .collaborators
1726            .iter()
1727            .map(|peer| peer.user_id)
1728            .collect();
1729        user_store
1730            .update(&mut cx, |user_store, cx| user_store.get_users(user_ids, cx))?
1731            .await?;
1732
1733        project.update(&mut cx, |this, cx| {
1734            this.set_collaborators_from_proto(response.payload.collaborators, cx)?;
1735            this.client_subscriptions.extend(subscriptions);
1736            anyhow::Ok(())
1737        })??;
1738
1739        Ok(project)
1740    }
1741
1742    fn new_search_history() -> SearchHistory {
1743        SearchHistory::new(
1744            Some(MAX_PROJECT_SEARCH_HISTORY_SIZE),
1745            search_history::QueryInsertionBehavior::AlwaysInsert,
1746        )
1747    }
1748
1749    fn release(&mut self, cx: &mut App) {
1750        if let Some(client) = self.remote_client.take() {
1751            let shutdown = client.update(cx, |client, cx| {
1752                client.shutdown_processes(
1753                    Some(proto::ShutdownRemoteServer {}),
1754                    cx.background_executor().clone(),
1755                )
1756            });
1757
1758            cx.background_spawn(async move {
1759                if let Some(shutdown) = shutdown {
1760                    shutdown.await;
1761                }
1762            })
1763            .detach()
1764        }
1765
1766        match &self.client_state {
1767            ProjectClientState::Local => {}
1768            ProjectClientState::Shared { .. } => {
1769                let _ = self.unshare_internal(cx);
1770            }
1771            ProjectClientState::Remote { remote_id, .. } => {
1772                let _ = self.collab_client.send(proto::LeaveProject {
1773                    project_id: *remote_id,
1774                });
1775                self.disconnected_from_host_internal(cx);
1776            }
1777        }
1778    }
1779
1780    #[cfg(any(test, feature = "test-support"))]
1781    pub async fn example(
1782        root_paths: impl IntoIterator<Item = &Path>,
1783        cx: &mut AsyncApp,
1784    ) -> Entity<Project> {
1785        use clock::FakeSystemClock;
1786
1787        let fs = Arc::new(RealFs::new(None, cx.background_executor().clone()));
1788        let languages = LanguageRegistry::test(cx.background_executor().clone());
1789        let clock = Arc::new(FakeSystemClock::new());
1790        let http_client = http_client::FakeHttpClient::with_404_response();
1791        let client = cx
1792            .update(|cx| client::Client::new(clock, http_client.clone(), cx))
1793            .unwrap();
1794        let user_store = cx.new(|cx| UserStore::new(client.clone(), cx)).unwrap();
1795        let project = cx
1796            .update(|cx| {
1797                Project::local(
1798                    client,
1799                    node_runtime::NodeRuntime::unavailable(),
1800                    user_store,
1801                    Arc::new(languages),
1802                    fs,
1803                    None,
1804                    cx,
1805                )
1806            })
1807            .unwrap();
1808        for path in root_paths {
1809            let (tree, _) = project
1810                .update(cx, |project, cx| {
1811                    project.find_or_create_worktree(path, true, cx)
1812                })
1813                .unwrap()
1814                .await
1815                .unwrap();
1816            tree.read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
1817                .unwrap()
1818                .await;
1819        }
1820        project
1821    }
1822
1823    #[cfg(any(test, feature = "test-support"))]
1824    pub async fn test(
1825        fs: Arc<dyn Fs>,
1826        root_paths: impl IntoIterator<Item = &Path>,
1827        cx: &mut gpui::TestAppContext,
1828    ) -> Entity<Project> {
1829        use clock::FakeSystemClock;
1830
1831        let languages = LanguageRegistry::test(cx.executor());
1832        let clock = Arc::new(FakeSystemClock::new());
1833        let http_client = http_client::FakeHttpClient::with_404_response();
1834        let client = cx.update(|cx| client::Client::new(clock, http_client.clone(), cx));
1835        let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
1836        let project = cx.update(|cx| {
1837            Project::local(
1838                client,
1839                node_runtime::NodeRuntime::unavailable(),
1840                user_store,
1841                Arc::new(languages),
1842                fs,
1843                None,
1844                cx,
1845            )
1846        });
1847        for path in root_paths {
1848            let (tree, _) = project
1849                .update(cx, |project, cx| {
1850                    project.find_or_create_worktree(path, true, cx)
1851                })
1852                .await
1853                .unwrap();
1854
1855            tree.read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
1856                .await;
1857        }
1858        project
1859    }
1860
1861    #[inline]
1862    pub fn dap_store(&self) -> Entity<DapStore> {
1863        self.dap_store.clone()
1864    }
1865
1866    #[inline]
1867    pub fn breakpoint_store(&self) -> Entity<BreakpointStore> {
1868        self.breakpoint_store.clone()
1869    }
1870
1871    pub fn active_debug_session(&self, cx: &App) -> Option<(Entity<Session>, ActiveStackFrame)> {
1872        let active_position = self.breakpoint_store.read(cx).active_position()?;
1873        let session = self
1874            .dap_store
1875            .read(cx)
1876            .session_by_id(active_position.session_id)?;
1877        Some((session, active_position.clone()))
1878    }
1879
1880    #[inline]
1881    pub fn lsp_store(&self) -> Entity<LspStore> {
1882        self.lsp_store.clone()
1883    }
1884
1885    #[inline]
1886    pub fn worktree_store(&self) -> Entity<WorktreeStore> {
1887        self.worktree_store.clone()
1888    }
1889
1890    #[inline]
1891    pub fn context_server_store(&self) -> Entity<ContextServerStore> {
1892        self.context_server_store.clone()
1893    }
1894
1895    #[inline]
1896    pub fn buffer_for_id(&self, remote_id: BufferId, cx: &App) -> Option<Entity<Buffer>> {
1897        self.buffer_store.read(cx).get(remote_id)
1898    }
1899
1900    #[inline]
1901    pub fn languages(&self) -> &Arc<LanguageRegistry> {
1902        &self.languages
1903    }
1904
1905    #[inline]
1906    pub fn client(&self) -> Arc<Client> {
1907        self.collab_client.clone()
1908    }
1909
1910    #[inline]
1911    pub fn remote_client(&self) -> Option<Entity<RemoteClient>> {
1912        self.remote_client.clone()
1913    }
1914
1915    #[inline]
1916    pub fn user_store(&self) -> Entity<UserStore> {
1917        self.user_store.clone()
1918    }
1919
1920    #[inline]
1921    pub fn node_runtime(&self) -> Option<&NodeRuntime> {
1922        self.node.as_ref()
1923    }
1924
1925    #[inline]
1926    pub fn opened_buffers(&self, cx: &App) -> Vec<Entity<Buffer>> {
1927        self.buffer_store.read(cx).buffers().collect()
1928    }
1929
1930    #[inline]
1931    pub fn environment(&self) -> &Entity<ProjectEnvironment> {
1932        &self.environment
1933    }
1934
1935    #[inline]
1936    pub fn cli_environment(&self, cx: &App) -> Option<HashMap<String, String>> {
1937        self.environment.read(cx).get_cli_environment()
1938    }
1939
1940    #[inline]
1941    pub fn peek_environment_error<'a>(&'a self, cx: &'a App) -> Option<&'a String> {
1942        self.environment.read(cx).peek_environment_error()
1943    }
1944
1945    #[inline]
1946    pub fn pop_environment_error(&mut self, cx: &mut Context<Self>) {
1947        self.environment.update(cx, |environment, _| {
1948            environment.pop_environment_error();
1949        });
1950    }
1951
1952    #[cfg(any(test, feature = "test-support"))]
1953    #[inline]
1954    pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &App) -> bool {
1955        self.buffer_store
1956            .read(cx)
1957            .get_by_path(&path.into())
1958            .is_some()
1959    }
1960
1961    #[inline]
1962    pub fn fs(&self) -> &Arc<dyn Fs> {
1963        &self.fs
1964    }
1965
1966    #[inline]
1967    pub fn remote_id(&self) -> Option<u64> {
1968        match self.client_state {
1969            ProjectClientState::Local => None,
1970            ProjectClientState::Shared { remote_id, .. }
1971            | ProjectClientState::Remote { remote_id, .. } => Some(remote_id),
1972        }
1973    }
1974
1975    #[inline]
1976    pub fn supports_terminal(&self, _cx: &App) -> bool {
1977        if self.is_local() {
1978            return true;
1979        }
1980        if self.is_via_remote_server() {
1981            return true;
1982        }
1983
1984        false
1985    }
1986
1987    #[inline]
1988    pub fn remote_connection_state(&self, cx: &App) -> Option<remote::ConnectionState> {
1989        self.remote_client
1990            .as_ref()
1991            .map(|remote| remote.read(cx).connection_state())
1992    }
1993
1994    #[inline]
1995    pub fn remote_connection_options(&self, cx: &App) -> Option<RemoteConnectionOptions> {
1996        self.remote_client
1997            .as_ref()
1998            .map(|remote| remote.read(cx).connection_options())
1999    }
2000
2001    #[inline]
2002    pub fn replica_id(&self) -> ReplicaId {
2003        match self.client_state {
2004            ProjectClientState::Remote { replica_id, .. } => replica_id,
2005            _ => {
2006                if self.remote_client.is_some() {
2007                    ReplicaId::REMOTE_SERVER
2008                } else {
2009                    ReplicaId::LOCAL
2010                }
2011            }
2012        }
2013    }
2014
2015    #[inline]
2016    pub fn task_store(&self) -> &Entity<TaskStore> {
2017        &self.task_store
2018    }
2019
2020    #[inline]
2021    pub fn snippets(&self) -> &Entity<SnippetProvider> {
2022        &self.snippets
2023    }
2024
2025    #[inline]
2026    pub fn search_history(&self, kind: SearchInputKind) -> &SearchHistory {
2027        match kind {
2028            SearchInputKind::Query => &self.search_history,
2029            SearchInputKind::Include => &self.search_included_history,
2030            SearchInputKind::Exclude => &self.search_excluded_history,
2031        }
2032    }
2033
2034    #[inline]
2035    pub fn search_history_mut(&mut self, kind: SearchInputKind) -> &mut SearchHistory {
2036        match kind {
2037            SearchInputKind::Query => &mut self.search_history,
2038            SearchInputKind::Include => &mut self.search_included_history,
2039            SearchInputKind::Exclude => &mut self.search_excluded_history,
2040        }
2041    }
2042
2043    #[inline]
2044    pub fn collaborators(&self) -> &HashMap<proto::PeerId, Collaborator> {
2045        &self.collaborators
2046    }
2047
2048    #[inline]
2049    pub fn host(&self) -> Option<&Collaborator> {
2050        self.collaborators.values().find(|c| c.is_host)
2051    }
2052
2053    #[inline]
2054    pub fn set_worktrees_reordered(&mut self, worktrees_reordered: bool, cx: &mut App) {
2055        self.worktree_store.update(cx, |store, _| {
2056            store.set_worktrees_reordered(worktrees_reordered);
2057        });
2058    }
2059
2060    /// Collect all worktrees, including ones that don't appear in the project panel
2061    #[inline]
2062    pub fn worktrees<'a>(
2063        &self,
2064        cx: &'a App,
2065    ) -> impl 'a + DoubleEndedIterator<Item = Entity<Worktree>> {
2066        self.worktree_store.read(cx).worktrees()
2067    }
2068
2069    /// Collect all user-visible worktrees, the ones that appear in the project panel.
2070    #[inline]
2071    pub fn visible_worktrees<'a>(
2072        &'a self,
2073        cx: &'a App,
2074    ) -> impl 'a + DoubleEndedIterator<Item = Entity<Worktree>> {
2075        self.worktree_store.read(cx).visible_worktrees(cx)
2076    }
2077
2078    #[inline]
2079    pub fn worktree_for_root_name(&self, root_name: &str, cx: &App) -> Option<Entity<Worktree>> {
2080        self.visible_worktrees(cx)
2081            .find(|tree| tree.read(cx).root_name() == root_name)
2082    }
2083
2084    #[inline]
2085    pub fn worktree_root_names<'a>(&'a self, cx: &'a App) -> impl Iterator<Item = &'a str> {
2086        self.visible_worktrees(cx)
2087            .map(|tree| tree.read(cx).root_name().as_unix_str())
2088    }
2089
2090    #[inline]
2091    pub fn worktree_for_id(&self, id: WorktreeId, cx: &App) -> Option<Entity<Worktree>> {
2092        self.worktree_store.read(cx).worktree_for_id(id, cx)
2093    }
2094
2095    pub fn worktree_for_entry(
2096        &self,
2097        entry_id: ProjectEntryId,
2098        cx: &App,
2099    ) -> Option<Entity<Worktree>> {
2100        self.worktree_store
2101            .read(cx)
2102            .worktree_for_entry(entry_id, cx)
2103    }
2104
2105    #[inline]
2106    pub fn worktree_id_for_entry(&self, entry_id: ProjectEntryId, cx: &App) -> Option<WorktreeId> {
2107        self.worktree_for_entry(entry_id, cx)
2108            .map(|worktree| worktree.read(cx).id())
2109    }
2110
2111    /// Checks if the entry is the root of a worktree.
2112    #[inline]
2113    pub fn entry_is_worktree_root(&self, entry_id: ProjectEntryId, cx: &App) -> bool {
2114        self.worktree_for_entry(entry_id, cx)
2115            .map(|worktree| {
2116                worktree
2117                    .read(cx)
2118                    .root_entry()
2119                    .is_some_and(|e| e.id == entry_id)
2120            })
2121            .unwrap_or(false)
2122    }
2123
2124    #[inline]
2125    pub fn project_path_git_status(
2126        &self,
2127        project_path: &ProjectPath,
2128        cx: &App,
2129    ) -> Option<FileStatus> {
2130        self.git_store
2131            .read(cx)
2132            .project_path_git_status(project_path, cx)
2133    }
2134
2135    #[inline]
2136    pub fn visibility_for_paths(
2137        &self,
2138        paths: &[PathBuf],
2139        metadatas: &[Metadata],
2140        exclude_sub_dirs: bool,
2141        cx: &App,
2142    ) -> Option<bool> {
2143        paths
2144            .iter()
2145            .zip(metadatas)
2146            .map(|(path, metadata)| self.visibility_for_path(path, metadata, exclude_sub_dirs, cx))
2147            .max()
2148            .flatten()
2149    }
2150
2151    pub fn visibility_for_path(
2152        &self,
2153        path: &Path,
2154        metadata: &Metadata,
2155        exclude_sub_dirs: bool,
2156        cx: &App,
2157    ) -> Option<bool> {
2158        let path = SanitizedPath::new(path).as_path();
2159        self.worktrees(cx)
2160            .filter_map(|worktree| {
2161                let worktree = worktree.read(cx);
2162                let abs_path = worktree.as_local()?.abs_path();
2163                let contains = path == abs_path.as_ref()
2164                    || (path.starts_with(abs_path) && (!exclude_sub_dirs || !metadata.is_dir));
2165                contains.then(|| worktree.is_visible())
2166            })
2167            .max()
2168    }
2169
2170    pub fn create_entry(
2171        &mut self,
2172        project_path: impl Into<ProjectPath>,
2173        is_directory: bool,
2174        cx: &mut Context<Self>,
2175    ) -> Task<Result<CreatedEntry>> {
2176        let project_path = project_path.into();
2177        let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) else {
2178            return Task::ready(Err(anyhow!(format!(
2179                "No worktree for path {project_path:?}"
2180            ))));
2181        };
2182        worktree.update(cx, |worktree, cx| {
2183            worktree.create_entry(project_path.path, is_directory, None, cx)
2184        })
2185    }
2186
2187    #[inline]
2188    pub fn copy_entry(
2189        &mut self,
2190        entry_id: ProjectEntryId,
2191        new_project_path: ProjectPath,
2192        cx: &mut Context<Self>,
2193    ) -> Task<Result<Option<Entry>>> {
2194        self.worktree_store.update(cx, |worktree_store, cx| {
2195            worktree_store.copy_entry(entry_id, new_project_path, cx)
2196        })
2197    }
2198
2199    /// Renames the project entry with given `entry_id`.
2200    ///
2201    /// `new_path` is a relative path to worktree root.
2202    /// If root entry is renamed then its new root name is used instead.
2203    pub fn rename_entry(
2204        &mut self,
2205        entry_id: ProjectEntryId,
2206        new_path: ProjectPath,
2207        cx: &mut Context<Self>,
2208    ) -> Task<Result<CreatedEntry>> {
2209        let worktree_store = self.worktree_store.clone();
2210        let Some((worktree, old_path, is_dir)) = worktree_store
2211            .read(cx)
2212            .worktree_and_entry_for_id(entry_id, cx)
2213            .map(|(worktree, entry)| (worktree, entry.path.clone(), entry.is_dir()))
2214        else {
2215            return Task::ready(Err(anyhow!(format!("No worktree for entry {entry_id:?}"))));
2216        };
2217
2218        let worktree_id = worktree.read(cx).id();
2219        let is_root_entry = self.entry_is_worktree_root(entry_id, cx);
2220
2221        let lsp_store = self.lsp_store().downgrade();
2222        cx.spawn(async move |project, cx| {
2223            let (old_abs_path, new_abs_path) = {
2224                let root_path = worktree.read_with(cx, |this, _| this.abs_path())?;
2225                let new_abs_path = if is_root_entry {
2226                    root_path
2227                        .parent()
2228                        .unwrap()
2229                        .join(new_path.path.as_std_path())
2230                } else {
2231                    root_path.join(&new_path.path.as_std_path())
2232                };
2233                (root_path.join(old_path.as_std_path()), new_abs_path)
2234            };
2235            let transaction = LspStore::will_rename_entry(
2236                lsp_store.clone(),
2237                worktree_id,
2238                &old_abs_path,
2239                &new_abs_path,
2240                is_dir,
2241                cx.clone(),
2242            )
2243            .await;
2244
2245            let entry = worktree_store
2246                .update(cx, |worktree_store, cx| {
2247                    worktree_store.rename_entry(entry_id, new_path.clone(), cx)
2248                })?
2249                .await?;
2250
2251            project
2252                .update(cx, |_, cx| {
2253                    cx.emit(Event::EntryRenamed(
2254                        transaction,
2255                        new_path.clone(),
2256                        new_abs_path.clone(),
2257                    ));
2258                })
2259                .ok();
2260
2261            lsp_store
2262                .read_with(cx, |this, _| {
2263                    this.did_rename_entry(worktree_id, &old_abs_path, &new_abs_path, is_dir);
2264                })
2265                .ok();
2266            Ok(entry)
2267        })
2268    }
2269
2270    #[inline]
2271    pub fn delete_file(
2272        &mut self,
2273        path: ProjectPath,
2274        trash: bool,
2275        cx: &mut Context<Self>,
2276    ) -> Option<Task<Result<()>>> {
2277        let entry = self.entry_for_path(&path, cx)?;
2278        self.delete_entry(entry.id, trash, cx)
2279    }
2280
2281    #[inline]
2282    pub fn delete_entry(
2283        &mut self,
2284        entry_id: ProjectEntryId,
2285        trash: bool,
2286        cx: &mut Context<Self>,
2287    ) -> Option<Task<Result<()>>> {
2288        let worktree = self.worktree_for_entry(entry_id, cx)?;
2289        cx.emit(Event::DeletedEntry(worktree.read(cx).id(), entry_id));
2290        worktree.update(cx, |worktree, cx| {
2291            worktree.delete_entry(entry_id, trash, cx)
2292        })
2293    }
2294
2295    #[inline]
2296    pub fn expand_entry(
2297        &mut self,
2298        worktree_id: WorktreeId,
2299        entry_id: ProjectEntryId,
2300        cx: &mut Context<Self>,
2301    ) -> Option<Task<Result<()>>> {
2302        let worktree = self.worktree_for_id(worktree_id, cx)?;
2303        worktree.update(cx, |worktree, cx| worktree.expand_entry(entry_id, cx))
2304    }
2305
2306    pub fn expand_all_for_entry(
2307        &mut self,
2308        worktree_id: WorktreeId,
2309        entry_id: ProjectEntryId,
2310        cx: &mut Context<Self>,
2311    ) -> Option<Task<Result<()>>> {
2312        let worktree = self.worktree_for_id(worktree_id, cx)?;
2313        let task = worktree.update(cx, |worktree, cx| {
2314            worktree.expand_all_for_entry(entry_id, cx)
2315        });
2316        Some(cx.spawn(async move |this, cx| {
2317            task.context("no task")?.await?;
2318            this.update(cx, |_, cx| {
2319                cx.emit(Event::ExpandedAllForEntry(worktree_id, entry_id));
2320            })?;
2321            Ok(())
2322        }))
2323    }
2324
2325    pub fn shared(&mut self, project_id: u64, cx: &mut Context<Self>) -> Result<()> {
2326        anyhow::ensure!(
2327            matches!(self.client_state, ProjectClientState::Local),
2328            "project was already shared"
2329        );
2330
2331        self.client_subscriptions.extend([
2332            self.collab_client
2333                .subscribe_to_entity(project_id)?
2334                .set_entity(&cx.entity(), &cx.to_async()),
2335            self.collab_client
2336                .subscribe_to_entity(project_id)?
2337                .set_entity(&self.worktree_store, &cx.to_async()),
2338            self.collab_client
2339                .subscribe_to_entity(project_id)?
2340                .set_entity(&self.buffer_store, &cx.to_async()),
2341            self.collab_client
2342                .subscribe_to_entity(project_id)?
2343                .set_entity(&self.lsp_store, &cx.to_async()),
2344            self.collab_client
2345                .subscribe_to_entity(project_id)?
2346                .set_entity(&self.settings_observer, &cx.to_async()),
2347            self.collab_client
2348                .subscribe_to_entity(project_id)?
2349                .set_entity(&self.dap_store, &cx.to_async()),
2350            self.collab_client
2351                .subscribe_to_entity(project_id)?
2352                .set_entity(&self.breakpoint_store, &cx.to_async()),
2353            self.collab_client
2354                .subscribe_to_entity(project_id)?
2355                .set_entity(&self.git_store, &cx.to_async()),
2356        ]);
2357
2358        self.buffer_store.update(cx, |buffer_store, cx| {
2359            buffer_store.shared(project_id, self.collab_client.clone().into(), cx)
2360        });
2361        self.worktree_store.update(cx, |worktree_store, cx| {
2362            worktree_store.shared(project_id, self.collab_client.clone().into(), cx);
2363        });
2364        self.lsp_store.update(cx, |lsp_store, cx| {
2365            lsp_store.shared(project_id, self.collab_client.clone().into(), cx)
2366        });
2367        self.breakpoint_store.update(cx, |breakpoint_store, _| {
2368            breakpoint_store.shared(project_id, self.collab_client.clone().into())
2369        });
2370        self.dap_store.update(cx, |dap_store, cx| {
2371            dap_store.shared(project_id, self.collab_client.clone().into(), cx);
2372        });
2373        self.task_store.update(cx, |task_store, cx| {
2374            task_store.shared(project_id, self.collab_client.clone().into(), cx);
2375        });
2376        self.settings_observer.update(cx, |settings_observer, cx| {
2377            settings_observer.shared(project_id, self.collab_client.clone().into(), cx)
2378        });
2379        self.git_store.update(cx, |git_store, cx| {
2380            git_store.shared(project_id, self.collab_client.clone().into(), cx)
2381        });
2382
2383        self.client_state = ProjectClientState::Shared {
2384            remote_id: project_id,
2385        };
2386
2387        cx.emit(Event::RemoteIdChanged(Some(project_id)));
2388        Ok(())
2389    }
2390
2391    pub fn reshared(
2392        &mut self,
2393        message: proto::ResharedProject,
2394        cx: &mut Context<Self>,
2395    ) -> Result<()> {
2396        self.buffer_store
2397            .update(cx, |buffer_store, _| buffer_store.forget_shared_buffers());
2398        self.set_collaborators_from_proto(message.collaborators, cx)?;
2399
2400        self.worktree_store.update(cx, |worktree_store, cx| {
2401            worktree_store.send_project_updates(cx);
2402        });
2403        if let Some(remote_id) = self.remote_id() {
2404            self.git_store.update(cx, |git_store, cx| {
2405                git_store.shared(remote_id, self.collab_client.clone().into(), cx)
2406            });
2407        }
2408        cx.emit(Event::Reshared);
2409        Ok(())
2410    }
2411
2412    pub fn rejoined(
2413        &mut self,
2414        message: proto::RejoinedProject,
2415        message_id: u32,
2416        cx: &mut Context<Self>,
2417    ) -> Result<()> {
2418        cx.update_global::<SettingsStore, _>(|store, cx| {
2419            self.worktree_store.update(cx, |worktree_store, cx| {
2420                for worktree in worktree_store.worktrees() {
2421                    store
2422                        .clear_local_settings(worktree.read(cx).id(), cx)
2423                        .log_err();
2424                }
2425            });
2426        });
2427
2428        self.join_project_response_message_id = message_id;
2429        self.set_worktrees_from_proto(message.worktrees, cx)?;
2430        self.set_collaborators_from_proto(message.collaborators, cx)?;
2431
2432        let project = cx.weak_entity();
2433        self.lsp_store.update(cx, |lsp_store, cx| {
2434            lsp_store.set_language_server_statuses_from_proto(
2435                project,
2436                message.language_servers,
2437                message.language_server_capabilities,
2438                cx,
2439            )
2440        });
2441        self.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
2442            .unwrap();
2443        cx.emit(Event::Rejoined);
2444        Ok(())
2445    }
2446
2447    #[inline]
2448    pub fn unshare(&mut self, cx: &mut Context<Self>) -> Result<()> {
2449        self.unshare_internal(cx)?;
2450        cx.emit(Event::RemoteIdChanged(None));
2451        Ok(())
2452    }
2453
2454    fn unshare_internal(&mut self, cx: &mut App) -> Result<()> {
2455        anyhow::ensure!(
2456            !self.is_via_collab(),
2457            "attempted to unshare a remote project"
2458        );
2459
2460        if let ProjectClientState::Shared { remote_id, .. } = self.client_state {
2461            self.client_state = ProjectClientState::Local;
2462            self.collaborators.clear();
2463            self.client_subscriptions.clear();
2464            self.worktree_store.update(cx, |store, cx| {
2465                store.unshared(cx);
2466            });
2467            self.buffer_store.update(cx, |buffer_store, cx| {
2468                buffer_store.forget_shared_buffers();
2469                buffer_store.unshared(cx)
2470            });
2471            self.task_store.update(cx, |task_store, cx| {
2472                task_store.unshared(cx);
2473            });
2474            self.breakpoint_store.update(cx, |breakpoint_store, cx| {
2475                breakpoint_store.unshared(cx);
2476            });
2477            self.dap_store.update(cx, |dap_store, cx| {
2478                dap_store.unshared(cx);
2479            });
2480            self.settings_observer.update(cx, |settings_observer, cx| {
2481                settings_observer.unshared(cx);
2482            });
2483            self.git_store.update(cx, |git_store, cx| {
2484                git_store.unshared(cx);
2485            });
2486
2487            self.collab_client
2488                .send(proto::UnshareProject {
2489                    project_id: remote_id,
2490                })
2491                .ok();
2492            Ok(())
2493        } else {
2494            anyhow::bail!("attempted to unshare an unshared project");
2495        }
2496    }
2497
2498    pub fn disconnected_from_host(&mut self, cx: &mut Context<Self>) {
2499        if self.is_disconnected(cx) {
2500            return;
2501        }
2502        self.disconnected_from_host_internal(cx);
2503        cx.emit(Event::DisconnectedFromHost);
2504    }
2505
2506    pub fn set_role(&mut self, role: proto::ChannelRole, cx: &mut Context<Self>) {
2507        let new_capability =
2508            if role == proto::ChannelRole::Member || role == proto::ChannelRole::Admin {
2509                Capability::ReadWrite
2510            } else {
2511                Capability::ReadOnly
2512            };
2513        if let ProjectClientState::Remote { capability, .. } = &mut self.client_state {
2514            if *capability == new_capability {
2515                return;
2516            }
2517
2518            *capability = new_capability;
2519            for buffer in self.opened_buffers(cx) {
2520                buffer.update(cx, |buffer, cx| buffer.set_capability(new_capability, cx));
2521            }
2522        }
2523    }
2524
2525    fn disconnected_from_host_internal(&mut self, cx: &mut App) {
2526        if let ProjectClientState::Remote {
2527            sharing_has_stopped,
2528            ..
2529        } = &mut self.client_state
2530        {
2531            *sharing_has_stopped = true;
2532            self.collaborators.clear();
2533            self.worktree_store.update(cx, |store, cx| {
2534                store.disconnected_from_host(cx);
2535            });
2536            self.buffer_store.update(cx, |buffer_store, cx| {
2537                buffer_store.disconnected_from_host(cx)
2538            });
2539            self.lsp_store
2540                .update(cx, |lsp_store, _cx| lsp_store.disconnected_from_host());
2541        }
2542    }
2543
2544    #[inline]
2545    pub fn close(&mut self, cx: &mut Context<Self>) {
2546        cx.emit(Event::Closed);
2547    }
2548
2549    #[inline]
2550    pub fn is_disconnected(&self, cx: &App) -> bool {
2551        match &self.client_state {
2552            ProjectClientState::Remote {
2553                sharing_has_stopped,
2554                ..
2555            } => *sharing_has_stopped,
2556            ProjectClientState::Local if self.is_via_remote_server() => {
2557                self.remote_client_is_disconnected(cx)
2558            }
2559            _ => false,
2560        }
2561    }
2562
2563    #[inline]
2564    fn remote_client_is_disconnected(&self, cx: &App) -> bool {
2565        self.remote_client
2566            .as_ref()
2567            .map(|remote| remote.read(cx).is_disconnected())
2568            .unwrap_or(false)
2569    }
2570
2571    #[inline]
2572    pub fn capability(&self) -> Capability {
2573        match &self.client_state {
2574            ProjectClientState::Remote { capability, .. } => *capability,
2575            ProjectClientState::Shared { .. } | ProjectClientState::Local => Capability::ReadWrite,
2576        }
2577    }
2578
2579    #[inline]
2580    pub fn is_read_only(&self, cx: &App) -> bool {
2581        self.is_disconnected(cx) || self.capability() == Capability::ReadOnly
2582    }
2583
2584    #[inline]
2585    pub fn is_local(&self) -> bool {
2586        match &self.client_state {
2587            ProjectClientState::Local | ProjectClientState::Shared { .. } => {
2588                self.remote_client.is_none()
2589            }
2590            ProjectClientState::Remote { .. } => false,
2591        }
2592    }
2593
2594    /// Whether this project is a remote server (not counting collab).
2595    #[inline]
2596    pub fn is_via_remote_server(&self) -> bool {
2597        match &self.client_state {
2598            ProjectClientState::Local | ProjectClientState::Shared { .. } => {
2599                self.remote_client.is_some()
2600            }
2601            ProjectClientState::Remote { .. } => false,
2602        }
2603    }
2604
2605    /// Whether this project is from collab (not counting remote servers).
2606    #[inline]
2607    pub fn is_via_collab(&self) -> bool {
2608        match &self.client_state {
2609            ProjectClientState::Local | ProjectClientState::Shared { .. } => false,
2610            ProjectClientState::Remote { .. } => true,
2611        }
2612    }
2613
2614    /// `!self.is_local()`
2615    #[inline]
2616    pub fn is_remote(&self) -> bool {
2617        debug_assert_eq!(
2618            !self.is_local(),
2619            self.is_via_collab() || self.is_via_remote_server()
2620        );
2621        !self.is_local()
2622    }
2623
2624    #[inline]
2625    pub fn create_buffer(
2626        &mut self,
2627        searchable: bool,
2628        cx: &mut Context<Self>,
2629    ) -> Task<Result<Entity<Buffer>>> {
2630        self.buffer_store.update(cx, |buffer_store, cx| {
2631            buffer_store.create_buffer(searchable, cx)
2632        })
2633    }
2634
2635    #[inline]
2636    pub fn create_local_buffer(
2637        &mut self,
2638        text: &str,
2639        language: Option<Arc<Language>>,
2640        project_searchable: bool,
2641        cx: &mut Context<Self>,
2642    ) -> Entity<Buffer> {
2643        if self.is_remote() {
2644            panic!("called create_local_buffer on a remote project")
2645        }
2646        self.buffer_store.update(cx, |buffer_store, cx| {
2647            buffer_store.create_local_buffer(text, language, project_searchable, cx)
2648        })
2649    }
2650
2651    pub fn open_path(
2652        &mut self,
2653        path: ProjectPath,
2654        cx: &mut Context<Self>,
2655    ) -> Task<Result<(Option<ProjectEntryId>, Entity<Buffer>)>> {
2656        let task = self.open_buffer(path, cx);
2657        cx.spawn(async move |_project, cx| {
2658            let buffer = task.await?;
2659            let project_entry_id = buffer.read_with(cx, |buffer, _cx| {
2660                File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id())
2661            })?;
2662
2663            Ok((project_entry_id, buffer))
2664        })
2665    }
2666
2667    pub fn open_local_buffer(
2668        &mut self,
2669        abs_path: impl AsRef<Path>,
2670        cx: &mut Context<Self>,
2671    ) -> Task<Result<Entity<Buffer>>> {
2672        let worktree_task = self.find_or_create_worktree(abs_path.as_ref(), false, cx);
2673        cx.spawn(async move |this, cx| {
2674            let (worktree, relative_path) = worktree_task.await?;
2675            this.update(cx, |this, cx| {
2676                this.open_buffer((worktree.read(cx).id(), relative_path), cx)
2677            })?
2678            .await
2679        })
2680    }
2681
2682    #[cfg(any(test, feature = "test-support"))]
2683    pub fn open_local_buffer_with_lsp(
2684        &mut self,
2685        abs_path: impl AsRef<Path>,
2686        cx: &mut Context<Self>,
2687    ) -> Task<Result<(Entity<Buffer>, lsp_store::OpenLspBufferHandle)>> {
2688        if let Some((worktree, relative_path)) = self.find_worktree(abs_path.as_ref(), cx) {
2689            self.open_buffer_with_lsp((worktree.read(cx).id(), relative_path), cx)
2690        } else {
2691            Task::ready(Err(anyhow!("no such path")))
2692        }
2693    }
2694
2695    pub fn open_buffer(
2696        &mut self,
2697        path: impl Into<ProjectPath>,
2698        cx: &mut App,
2699    ) -> Task<Result<Entity<Buffer>>> {
2700        if self.is_disconnected(cx) {
2701            return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
2702        }
2703
2704        self.buffer_store.update(cx, |buffer_store, cx| {
2705            buffer_store.open_buffer(path.into(), cx)
2706        })
2707    }
2708
2709    #[cfg(any(test, feature = "test-support"))]
2710    pub fn open_buffer_with_lsp(
2711        &mut self,
2712        path: impl Into<ProjectPath>,
2713        cx: &mut Context<Self>,
2714    ) -> Task<Result<(Entity<Buffer>, lsp_store::OpenLspBufferHandle)>> {
2715        let buffer = self.open_buffer(path, cx);
2716        cx.spawn(async move |this, cx| {
2717            let buffer = buffer.await?;
2718            let handle = this.update(cx, |project, cx| {
2719                project.register_buffer_with_language_servers(&buffer, cx)
2720            })?;
2721            Ok((buffer, handle))
2722        })
2723    }
2724
2725    pub fn register_buffer_with_language_servers(
2726        &self,
2727        buffer: &Entity<Buffer>,
2728        cx: &mut App,
2729    ) -> OpenLspBufferHandle {
2730        self.lsp_store.update(cx, |lsp_store, cx| {
2731            lsp_store.register_buffer_with_language_servers(buffer, HashSet::default(), false, cx)
2732        })
2733    }
2734
2735    pub fn open_unstaged_diff(
2736        &mut self,
2737        buffer: Entity<Buffer>,
2738        cx: &mut Context<Self>,
2739    ) -> Task<Result<Entity<BufferDiff>>> {
2740        if self.is_disconnected(cx) {
2741            return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
2742        }
2743        self.git_store
2744            .update(cx, |git_store, cx| git_store.open_unstaged_diff(buffer, cx))
2745    }
2746
2747    pub fn open_uncommitted_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.update(cx, |git_store, cx| {
2756            git_store.open_uncommitted_diff(buffer, cx)
2757        })
2758    }
2759
2760    pub fn open_buffer_by_id(
2761        &mut self,
2762        id: BufferId,
2763        cx: &mut Context<Self>,
2764    ) -> Task<Result<Entity<Buffer>>> {
2765        if let Some(buffer) = self.buffer_for_id(id, cx) {
2766            Task::ready(Ok(buffer))
2767        } else if self.is_local() || self.is_via_remote_server() {
2768            Task::ready(Err(anyhow!("buffer {id} does not exist")))
2769        } else if let Some(project_id) = self.remote_id() {
2770            let request = self.collab_client.request(proto::OpenBufferById {
2771                project_id,
2772                id: id.into(),
2773            });
2774            cx.spawn(async move |project, cx| {
2775                let buffer_id = BufferId::new(request.await?.buffer_id)?;
2776                project
2777                    .update(cx, |project, cx| {
2778                        project.buffer_store.update(cx, |buffer_store, cx| {
2779                            buffer_store.wait_for_remote_buffer(buffer_id, cx)
2780                        })
2781                    })?
2782                    .await
2783            })
2784        } else {
2785            Task::ready(Err(anyhow!("cannot open buffer while disconnected")))
2786        }
2787    }
2788
2789    pub fn save_buffers(
2790        &self,
2791        buffers: HashSet<Entity<Buffer>>,
2792        cx: &mut Context<Self>,
2793    ) -> Task<Result<()>> {
2794        cx.spawn(async move |this, cx| {
2795            let save_tasks = buffers.into_iter().filter_map(|buffer| {
2796                this.update(cx, |this, cx| this.save_buffer(buffer, cx))
2797                    .ok()
2798            });
2799            try_join_all(save_tasks).await?;
2800            Ok(())
2801        })
2802    }
2803
2804    pub fn save_buffer(&self, buffer: Entity<Buffer>, cx: &mut Context<Self>) -> Task<Result<()>> {
2805        self.buffer_store
2806            .update(cx, |buffer_store, cx| buffer_store.save_buffer(buffer, cx))
2807    }
2808
2809    pub fn save_buffer_as(
2810        &mut self,
2811        buffer: Entity<Buffer>,
2812        path: ProjectPath,
2813        cx: &mut Context<Self>,
2814    ) -> Task<Result<()>> {
2815        self.buffer_store.update(cx, |buffer_store, cx| {
2816            buffer_store.save_buffer_as(buffer.clone(), path, cx)
2817        })
2818    }
2819
2820    pub fn get_open_buffer(&self, path: &ProjectPath, cx: &App) -> Option<Entity<Buffer>> {
2821        self.buffer_store.read(cx).get_by_path(path)
2822    }
2823
2824    fn register_buffer(&mut self, buffer: &Entity<Buffer>, cx: &mut Context<Self>) -> Result<()> {
2825        {
2826            let mut remotely_created_models = self.remotely_created_models.lock();
2827            if remotely_created_models.retain_count > 0 {
2828                remotely_created_models.buffers.push(buffer.clone())
2829            }
2830        }
2831
2832        self.request_buffer_diff_recalculation(buffer, cx);
2833
2834        cx.subscribe(buffer, |this, buffer, event, cx| {
2835            this.on_buffer_event(buffer, event, cx);
2836        })
2837        .detach();
2838
2839        Ok(())
2840    }
2841
2842    pub fn open_image(
2843        &mut self,
2844        path: impl Into<ProjectPath>,
2845        cx: &mut Context<Self>,
2846    ) -> Task<Result<Entity<ImageItem>>> {
2847        if self.is_disconnected(cx) {
2848            return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
2849        }
2850
2851        let open_image_task = self.image_store.update(cx, |image_store, cx| {
2852            image_store.open_image(path.into(), cx)
2853        });
2854
2855        let weak_project = cx.entity().downgrade();
2856        cx.spawn(async move |_, cx| {
2857            let image_item = open_image_task.await?;
2858
2859            // Check if metadata already exists (e.g., for remote images)
2860            let needs_metadata =
2861                cx.read_entity(&image_item, |item, _| item.image_metadata.is_none())?;
2862
2863            if needs_metadata {
2864                let project = weak_project.upgrade().context("Project dropped")?;
2865                let metadata =
2866                    ImageItem::load_image_metadata(image_item.clone(), project, cx).await?;
2867                image_item.update(cx, |image_item, cx| {
2868                    image_item.image_metadata = Some(metadata);
2869                    cx.emit(ImageItemEvent::MetadataUpdated);
2870                })?;
2871            }
2872
2873            Ok(image_item)
2874        })
2875    }
2876
2877    async fn send_buffer_ordered_messages(
2878        project: WeakEntity<Self>,
2879        rx: UnboundedReceiver<BufferOrderedMessage>,
2880        cx: &mut AsyncApp,
2881    ) -> Result<()> {
2882        const MAX_BATCH_SIZE: usize = 128;
2883
2884        let mut operations_by_buffer_id = HashMap::default();
2885        async fn flush_operations(
2886            this: &WeakEntity<Project>,
2887            operations_by_buffer_id: &mut HashMap<BufferId, Vec<proto::Operation>>,
2888            needs_resync_with_host: &mut bool,
2889            is_local: bool,
2890            cx: &mut AsyncApp,
2891        ) -> Result<()> {
2892            for (buffer_id, operations) in operations_by_buffer_id.drain() {
2893                let request = this.read_with(cx, |this, _| {
2894                    let project_id = this.remote_id()?;
2895                    Some(this.collab_client.request(proto::UpdateBuffer {
2896                        buffer_id: buffer_id.into(),
2897                        project_id,
2898                        operations,
2899                    }))
2900                })?;
2901                if let Some(request) = request
2902                    && request.await.is_err()
2903                    && !is_local
2904                {
2905                    *needs_resync_with_host = true;
2906                    break;
2907                }
2908            }
2909            Ok(())
2910        }
2911
2912        let mut needs_resync_with_host = false;
2913        let mut changes = rx.ready_chunks(MAX_BATCH_SIZE);
2914
2915        while let Some(changes) = changes.next().await {
2916            let is_local = project.read_with(cx, |this, _| this.is_local())?;
2917
2918            for change in changes {
2919                match change {
2920                    BufferOrderedMessage::Operation {
2921                        buffer_id,
2922                        operation,
2923                    } => {
2924                        if needs_resync_with_host {
2925                            continue;
2926                        }
2927
2928                        operations_by_buffer_id
2929                            .entry(buffer_id)
2930                            .or_insert(Vec::new())
2931                            .push(operation);
2932                    }
2933
2934                    BufferOrderedMessage::Resync => {
2935                        operations_by_buffer_id.clear();
2936                        if project
2937                            .update(cx, |this, cx| this.synchronize_remote_buffers(cx))?
2938                            .await
2939                            .is_ok()
2940                        {
2941                            needs_resync_with_host = false;
2942                        }
2943                    }
2944
2945                    BufferOrderedMessage::LanguageServerUpdate {
2946                        language_server_id,
2947                        message,
2948                        name,
2949                    } => {
2950                        flush_operations(
2951                            &project,
2952                            &mut operations_by_buffer_id,
2953                            &mut needs_resync_with_host,
2954                            is_local,
2955                            cx,
2956                        )
2957                        .await?;
2958
2959                        project.read_with(cx, |project, _| {
2960                            if let Some(project_id) = project.remote_id() {
2961                                project
2962                                    .collab_client
2963                                    .send(proto::UpdateLanguageServer {
2964                                        project_id,
2965                                        server_name: name.map(|name| String::from(name.0)),
2966                                        language_server_id: language_server_id.to_proto(),
2967                                        variant: Some(message),
2968                                    })
2969                                    .log_err();
2970                            }
2971                        })?;
2972                    }
2973                }
2974            }
2975
2976            flush_operations(
2977                &project,
2978                &mut operations_by_buffer_id,
2979                &mut needs_resync_with_host,
2980                is_local,
2981                cx,
2982            )
2983            .await?;
2984        }
2985
2986        Ok(())
2987    }
2988
2989    fn on_buffer_store_event(
2990        &mut self,
2991        _: Entity<BufferStore>,
2992        event: &BufferStoreEvent,
2993        cx: &mut Context<Self>,
2994    ) {
2995        match event {
2996            BufferStoreEvent::BufferAdded(buffer) => {
2997                self.register_buffer(buffer, cx).log_err();
2998            }
2999            BufferStoreEvent::BufferDropped(buffer_id) => {
3000                if let Some(ref remote_client) = self.remote_client {
3001                    remote_client
3002                        .read(cx)
3003                        .proto_client()
3004                        .send(proto::CloseBuffer {
3005                            project_id: 0,
3006                            buffer_id: buffer_id.to_proto(),
3007                        })
3008                        .log_err();
3009                }
3010            }
3011            _ => {}
3012        }
3013    }
3014
3015    fn on_image_store_event(
3016        &mut self,
3017        _: Entity<ImageStore>,
3018        event: &ImageStoreEvent,
3019        cx: &mut Context<Self>,
3020    ) {
3021        match event {
3022            ImageStoreEvent::ImageAdded(image) => {
3023                cx.subscribe(image, |this, image, event, cx| {
3024                    this.on_image_event(image, event, cx);
3025                })
3026                .detach();
3027            }
3028        }
3029    }
3030
3031    fn on_dap_store_event(
3032        &mut self,
3033        _: Entity<DapStore>,
3034        event: &DapStoreEvent,
3035        cx: &mut Context<Self>,
3036    ) {
3037        if let DapStoreEvent::Notification(message) = event {
3038            cx.emit(Event::Toast {
3039                notification_id: "dap".into(),
3040                message: message.clone(),
3041            });
3042        }
3043    }
3044
3045    fn on_lsp_store_event(
3046        &mut self,
3047        _: Entity<LspStore>,
3048        event: &LspStoreEvent,
3049        cx: &mut Context<Self>,
3050    ) {
3051        match event {
3052            LspStoreEvent::DiagnosticsUpdated { server_id, paths } => {
3053                cx.emit(Event::DiagnosticsUpdated {
3054                    paths: paths.clone(),
3055                    language_server_id: *server_id,
3056                })
3057            }
3058            LspStoreEvent::LanguageServerAdded(server_id, name, worktree_id) => cx.emit(
3059                Event::LanguageServerAdded(*server_id, name.clone(), *worktree_id),
3060            ),
3061            LspStoreEvent::LanguageServerRemoved(server_id) => {
3062                cx.emit(Event::LanguageServerRemoved(*server_id))
3063            }
3064            LspStoreEvent::LanguageServerLog(server_id, log_type, string) => cx.emit(
3065                Event::LanguageServerLog(*server_id, log_type.clone(), string.clone()),
3066            ),
3067            LspStoreEvent::LanguageDetected {
3068                buffer,
3069                new_language,
3070            } => {
3071                let Some(_) = new_language else {
3072                    cx.emit(Event::LanguageNotFound(buffer.clone()));
3073                    return;
3074                };
3075            }
3076            LspStoreEvent::RefreshInlayHints {
3077                server_id,
3078                request_id,
3079            } => cx.emit(Event::RefreshInlayHints {
3080                server_id: *server_id,
3081                request_id: *request_id,
3082            }),
3083            LspStoreEvent::RefreshCodeLens => cx.emit(Event::RefreshCodeLens),
3084            LspStoreEvent::LanguageServerPrompt(prompt) => {
3085                cx.emit(Event::LanguageServerPrompt(prompt.clone()))
3086            }
3087            LspStoreEvent::DiskBasedDiagnosticsStarted { language_server_id } => {
3088                cx.emit(Event::DiskBasedDiagnosticsStarted {
3089                    language_server_id: *language_server_id,
3090                });
3091            }
3092            LspStoreEvent::DiskBasedDiagnosticsFinished { language_server_id } => {
3093                cx.emit(Event::DiskBasedDiagnosticsFinished {
3094                    language_server_id: *language_server_id,
3095                });
3096            }
3097            LspStoreEvent::LanguageServerUpdate {
3098                language_server_id,
3099                name,
3100                message,
3101            } => {
3102                if self.is_local() {
3103                    self.enqueue_buffer_ordered_message(
3104                        BufferOrderedMessage::LanguageServerUpdate {
3105                            language_server_id: *language_server_id,
3106                            message: message.clone(),
3107                            name: name.clone(),
3108                        },
3109                    )
3110                    .ok();
3111                }
3112
3113                match message {
3114                    proto::update_language_server::Variant::MetadataUpdated(update) => {
3115                        if let Some(capabilities) = update
3116                            .capabilities
3117                            .as_ref()
3118                            .and_then(|capabilities| serde_json::from_str(capabilities).ok())
3119                        {
3120                            self.lsp_store.update(cx, |lsp_store, _| {
3121                                lsp_store
3122                                    .lsp_server_capabilities
3123                                    .insert(*language_server_id, capabilities);
3124                            });
3125                        }
3126                    }
3127                    proto::update_language_server::Variant::RegisteredForBuffer(update) => {
3128                        if let Some(buffer_id) = BufferId::new(update.buffer_id).ok() {
3129                            cx.emit(Event::LanguageServerBufferRegistered {
3130                                buffer_id,
3131                                server_id: *language_server_id,
3132                                buffer_abs_path: PathBuf::from(&update.buffer_abs_path),
3133                                name: name.clone(),
3134                            });
3135                        }
3136                    }
3137                    _ => (),
3138                }
3139            }
3140            LspStoreEvent::Notification(message) => cx.emit(Event::Toast {
3141                notification_id: "lsp".into(),
3142                message: message.clone(),
3143            }),
3144            LspStoreEvent::SnippetEdit {
3145                buffer_id,
3146                edits,
3147                most_recent_edit,
3148            } => {
3149                if most_recent_edit.replica_id == self.replica_id() {
3150                    cx.emit(Event::SnippetEdit(*buffer_id, edits.clone()))
3151                }
3152            }
3153        }
3154    }
3155
3156    fn on_remote_client_event(
3157        &mut self,
3158        _: Entity<RemoteClient>,
3159        event: &remote::RemoteClientEvent,
3160        cx: &mut Context<Self>,
3161    ) {
3162        match event {
3163            remote::RemoteClientEvent::Disconnected => {
3164                self.worktree_store.update(cx, |store, cx| {
3165                    store.disconnected_from_host(cx);
3166                });
3167                self.buffer_store.update(cx, |buffer_store, cx| {
3168                    buffer_store.disconnected_from_host(cx)
3169                });
3170                self.lsp_store.update(cx, |lsp_store, _cx| {
3171                    lsp_store.disconnected_from_ssh_remote()
3172                });
3173                cx.emit(Event::DisconnectedFromSshRemote);
3174            }
3175        }
3176    }
3177
3178    fn on_settings_observer_event(
3179        &mut self,
3180        _: Entity<SettingsObserver>,
3181        event: &SettingsObserverEvent,
3182        cx: &mut Context<Self>,
3183    ) {
3184        match event {
3185            SettingsObserverEvent::LocalSettingsUpdated(result) => match result {
3186                Err(InvalidSettingsError::LocalSettings { message, path }) => {
3187                    let message = format!("Failed to set local settings in {path:?}:\n{message}");
3188                    cx.emit(Event::Toast {
3189                        notification_id: format!("local-settings-{path:?}").into(),
3190                        message,
3191                    });
3192                }
3193                Ok(path) => cx.emit(Event::HideToast {
3194                    notification_id: format!("local-settings-{path:?}").into(),
3195                }),
3196                Err(_) => {}
3197            },
3198            SettingsObserverEvent::LocalTasksUpdated(result) => match result {
3199                Err(InvalidSettingsError::Tasks { message, path }) => {
3200                    let message = format!("Failed to set local tasks in {path:?}:\n{message}");
3201                    cx.emit(Event::Toast {
3202                        notification_id: format!("local-tasks-{path:?}").into(),
3203                        message,
3204                    });
3205                }
3206                Ok(path) => cx.emit(Event::HideToast {
3207                    notification_id: format!("local-tasks-{path:?}").into(),
3208                }),
3209                Err(_) => {}
3210            },
3211            SettingsObserverEvent::LocalDebugScenariosUpdated(result) => match result {
3212                Err(InvalidSettingsError::Debug { message, path }) => {
3213                    let message =
3214                        format!("Failed to set local debug scenarios in {path:?}:\n{message}");
3215                    cx.emit(Event::Toast {
3216                        notification_id: format!("local-debug-scenarios-{path:?}").into(),
3217                        message,
3218                    });
3219                }
3220                Ok(path) => cx.emit(Event::HideToast {
3221                    notification_id: format!("local-debug-scenarios-{path:?}").into(),
3222                }),
3223                Err(_) => {}
3224            },
3225        }
3226    }
3227
3228    fn on_worktree_store_event(
3229        &mut self,
3230        _: Entity<WorktreeStore>,
3231        event: &WorktreeStoreEvent,
3232        cx: &mut Context<Self>,
3233    ) {
3234        match event {
3235            WorktreeStoreEvent::WorktreeAdded(worktree) => {
3236                self.on_worktree_added(worktree, cx);
3237                cx.emit(Event::WorktreeAdded(worktree.read(cx).id()));
3238            }
3239            WorktreeStoreEvent::WorktreeRemoved(_, id) => {
3240                cx.emit(Event::WorktreeRemoved(*id));
3241            }
3242            WorktreeStoreEvent::WorktreeReleased(_, id) => {
3243                self.on_worktree_released(*id, cx);
3244            }
3245            WorktreeStoreEvent::WorktreeOrderChanged => cx.emit(Event::WorktreeOrderChanged),
3246            WorktreeStoreEvent::WorktreeUpdateSent(_) => {}
3247            WorktreeStoreEvent::WorktreeUpdatedEntries(worktree_id, changes) => {
3248                self.client()
3249                    .telemetry()
3250                    .report_discovered_project_type_events(*worktree_id, changes);
3251                cx.emit(Event::WorktreeUpdatedEntries(*worktree_id, changes.clone()))
3252            }
3253            WorktreeStoreEvent::WorktreeDeletedEntry(worktree_id, id) => {
3254                cx.emit(Event::DeletedEntry(*worktree_id, *id))
3255            }
3256            // Listen to the GitStore instead.
3257            WorktreeStoreEvent::WorktreeUpdatedGitRepositories(_, _) => {}
3258        }
3259    }
3260
3261    fn on_worktree_added(&mut self, worktree: &Entity<Worktree>, _: &mut Context<Self>) {
3262        let mut remotely_created_models = self.remotely_created_models.lock();
3263        if remotely_created_models.retain_count > 0 {
3264            remotely_created_models.worktrees.push(worktree.clone())
3265        }
3266    }
3267
3268    fn on_worktree_released(&mut self, id_to_remove: WorktreeId, cx: &mut Context<Self>) {
3269        if let Some(remote) = &self.remote_client {
3270            remote
3271                .read(cx)
3272                .proto_client()
3273                .send(proto::RemoveWorktree {
3274                    worktree_id: id_to_remove.to_proto(),
3275                })
3276                .log_err();
3277        }
3278    }
3279
3280    fn on_buffer_event(
3281        &mut self,
3282        buffer: Entity<Buffer>,
3283        event: &BufferEvent,
3284        cx: &mut Context<Self>,
3285    ) -> Option<()> {
3286        if matches!(event, BufferEvent::Edited | BufferEvent::Reloaded) {
3287            self.request_buffer_diff_recalculation(&buffer, cx);
3288        }
3289
3290        let buffer_id = buffer.read(cx).remote_id();
3291        match event {
3292            BufferEvent::ReloadNeeded => {
3293                if !self.is_via_collab() {
3294                    self.reload_buffers([buffer.clone()].into_iter().collect(), true, cx)
3295                        .detach_and_log_err(cx);
3296                }
3297            }
3298            BufferEvent::Operation {
3299                operation,
3300                is_local: true,
3301            } => {
3302                let operation = language::proto::serialize_operation(operation);
3303
3304                if let Some(remote) = &self.remote_client {
3305                    remote
3306                        .read(cx)
3307                        .proto_client()
3308                        .send(proto::UpdateBuffer {
3309                            project_id: 0,
3310                            buffer_id: buffer_id.to_proto(),
3311                            operations: vec![operation.clone()],
3312                        })
3313                        .ok();
3314                }
3315
3316                self.enqueue_buffer_ordered_message(BufferOrderedMessage::Operation {
3317                    buffer_id,
3318                    operation,
3319                })
3320                .ok();
3321            }
3322
3323            _ => {}
3324        }
3325
3326        None
3327    }
3328
3329    fn on_image_event(
3330        &mut self,
3331        image: Entity<ImageItem>,
3332        event: &ImageItemEvent,
3333        cx: &mut Context<Self>,
3334    ) -> Option<()> {
3335        // TODO: handle image events from remote
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<ProgressToken>,
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    async fn handle_create_image_for_peer(
5074        this: Entity<Self>,
5075        envelope: TypedEnvelope<proto::CreateImageForPeer>,
5076        mut cx: AsyncApp,
5077    ) -> Result<()> {
5078        this.update(&mut cx, |this, cx| {
5079            this.image_store.update(cx, |image_store, cx| {
5080                image_store.handle_create_image_for_peer(envelope, cx)
5081            })
5082        })?
5083        .log_err();
5084        Ok(())
5085    }
5086
5087    fn synchronize_remote_buffers(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
5088        let project_id = match self.client_state {
5089            ProjectClientState::Remote {
5090                sharing_has_stopped,
5091                remote_id,
5092                ..
5093            } => {
5094                if sharing_has_stopped {
5095                    return Task::ready(Err(anyhow!(
5096                        "can't synchronize remote buffers on a readonly project"
5097                    )));
5098                } else {
5099                    remote_id
5100                }
5101            }
5102            ProjectClientState::Shared { .. } | ProjectClientState::Local => {
5103                return Task::ready(Err(anyhow!(
5104                    "can't synchronize remote buffers on a local project"
5105                )));
5106            }
5107        };
5108
5109        let client = self.collab_client.clone();
5110        cx.spawn(async move |this, cx| {
5111            let (buffers, incomplete_buffer_ids) = this.update(cx, |this, cx| {
5112                this.buffer_store.read(cx).buffer_version_info(cx)
5113            })?;
5114            let response = client
5115                .request(proto::SynchronizeBuffers {
5116                    project_id,
5117                    buffers,
5118                })
5119                .await?;
5120
5121            let send_updates_for_buffers = this.update(cx, |this, cx| {
5122                response
5123                    .buffers
5124                    .into_iter()
5125                    .map(|buffer| {
5126                        let client = client.clone();
5127                        let buffer_id = match BufferId::new(buffer.id) {
5128                            Ok(id) => id,
5129                            Err(e) => {
5130                                return Task::ready(Err(e));
5131                            }
5132                        };
5133                        let remote_version = language::proto::deserialize_version(&buffer.version);
5134                        if let Some(buffer) = this.buffer_for_id(buffer_id, cx) {
5135                            let operations =
5136                                buffer.read(cx).serialize_ops(Some(remote_version), cx);
5137                            cx.background_spawn(async move {
5138                                let operations = operations.await;
5139                                for chunk in split_operations(operations) {
5140                                    client
5141                                        .request(proto::UpdateBuffer {
5142                                            project_id,
5143                                            buffer_id: buffer_id.into(),
5144                                            operations: chunk,
5145                                        })
5146                                        .await?;
5147                                }
5148                                anyhow::Ok(())
5149                            })
5150                        } else {
5151                            Task::ready(Ok(()))
5152                        }
5153                    })
5154                    .collect::<Vec<_>>()
5155            })?;
5156
5157            // Any incomplete buffers have open requests waiting. Request that the host sends
5158            // creates these buffers for us again to unblock any waiting futures.
5159            for id in incomplete_buffer_ids {
5160                cx.background_spawn(client.request(proto::OpenBufferById {
5161                    project_id,
5162                    id: id.into(),
5163                }))
5164                .detach();
5165            }
5166
5167            futures::future::join_all(send_updates_for_buffers)
5168                .await
5169                .into_iter()
5170                .collect()
5171        })
5172    }
5173
5174    pub fn worktree_metadata_protos(&self, cx: &App) -> Vec<proto::WorktreeMetadata> {
5175        self.worktree_store.read(cx).worktree_metadata_protos(cx)
5176    }
5177
5178    /// Iterator of all open buffers that have unsaved changes
5179    pub fn dirty_buffers<'a>(&'a self, cx: &'a App) -> impl Iterator<Item = ProjectPath> + 'a {
5180        self.buffer_store.read(cx).buffers().filter_map(|buf| {
5181            let buf = buf.read(cx);
5182            if buf.is_dirty() {
5183                buf.project_path(cx)
5184            } else {
5185                None
5186            }
5187        })
5188    }
5189
5190    fn set_worktrees_from_proto(
5191        &mut self,
5192        worktrees: Vec<proto::WorktreeMetadata>,
5193        cx: &mut Context<Project>,
5194    ) -> Result<()> {
5195        self.worktree_store.update(cx, |worktree_store, cx| {
5196            worktree_store.set_worktrees_from_proto(worktrees, self.replica_id(), cx)
5197        })
5198    }
5199
5200    fn set_collaborators_from_proto(
5201        &mut self,
5202        messages: Vec<proto::Collaborator>,
5203        cx: &mut Context<Self>,
5204    ) -> Result<()> {
5205        let mut collaborators = HashMap::default();
5206        for message in messages {
5207            let collaborator = Collaborator::from_proto(message)?;
5208            collaborators.insert(collaborator.peer_id, collaborator);
5209        }
5210        for old_peer_id in self.collaborators.keys() {
5211            if !collaborators.contains_key(old_peer_id) {
5212                cx.emit(Event::CollaboratorLeft(*old_peer_id));
5213            }
5214        }
5215        self.collaborators = collaborators;
5216        Ok(())
5217    }
5218
5219    pub fn supplementary_language_servers<'a>(
5220        &'a self,
5221        cx: &'a App,
5222    ) -> impl 'a + Iterator<Item = (LanguageServerId, LanguageServerName)> {
5223        self.lsp_store.read(cx).supplementary_language_servers()
5224    }
5225
5226    pub fn any_language_server_supports_inlay_hints(&self, buffer: &Buffer, cx: &mut App) -> bool {
5227        let Some(language) = buffer.language().cloned() else {
5228            return false;
5229        };
5230        self.lsp_store.update(cx, |lsp_store, _| {
5231            let relevant_language_servers = lsp_store
5232                .languages
5233                .lsp_adapters(&language.name())
5234                .into_iter()
5235                .map(|lsp_adapter| lsp_adapter.name())
5236                .collect::<HashSet<_>>();
5237            lsp_store
5238                .language_server_statuses()
5239                .filter_map(|(server_id, server_status)| {
5240                    relevant_language_servers
5241                        .contains(&server_status.name)
5242                        .then_some(server_id)
5243                })
5244                .filter_map(|server_id| lsp_store.lsp_server_capabilities.get(&server_id))
5245                .any(InlayHints::check_capabilities)
5246        })
5247    }
5248
5249    pub fn language_server_id_for_name(
5250        &self,
5251        buffer: &Buffer,
5252        name: &LanguageServerName,
5253        cx: &App,
5254    ) -> Option<LanguageServerId> {
5255        let language = buffer.language()?;
5256        let relevant_language_servers = self
5257            .languages
5258            .lsp_adapters(&language.name())
5259            .into_iter()
5260            .map(|lsp_adapter| lsp_adapter.name())
5261            .collect::<HashSet<_>>();
5262        if !relevant_language_servers.contains(name) {
5263            return None;
5264        }
5265        self.language_server_statuses(cx)
5266            .filter(|(_, server_status)| relevant_language_servers.contains(&server_status.name))
5267            .find_map(|(server_id, server_status)| {
5268                if &server_status.name == name {
5269                    Some(server_id)
5270                } else {
5271                    None
5272                }
5273            })
5274    }
5275
5276    #[cfg(any(test, feature = "test-support"))]
5277    pub fn has_language_servers_for(&self, buffer: &Buffer, cx: &mut App) -> bool {
5278        self.lsp_store.update(cx, |this, cx| {
5279            this.language_servers_for_local_buffer(buffer, cx)
5280                .next()
5281                .is_some()
5282        })
5283    }
5284
5285    pub fn git_init(
5286        &self,
5287        path: Arc<Path>,
5288        fallback_branch_name: String,
5289        cx: &App,
5290    ) -> Task<Result<()>> {
5291        self.git_store
5292            .read(cx)
5293            .git_init(path, fallback_branch_name, cx)
5294    }
5295
5296    pub fn buffer_store(&self) -> &Entity<BufferStore> {
5297        &self.buffer_store
5298    }
5299
5300    pub fn git_store(&self) -> &Entity<GitStore> {
5301        &self.git_store
5302    }
5303
5304    pub fn agent_server_store(&self) -> &Entity<AgentServerStore> {
5305        &self.agent_server_store
5306    }
5307
5308    #[cfg(test)]
5309    fn git_scans_complete(&self, cx: &Context<Self>) -> Task<()> {
5310        cx.spawn(async move |this, cx| {
5311            let scans_complete = this
5312                .read_with(cx, |this, cx| {
5313                    this.worktrees(cx)
5314                        .filter_map(|worktree| Some(worktree.read(cx).as_local()?.scan_complete()))
5315                        .collect::<Vec<_>>()
5316                })
5317                .unwrap();
5318            join_all(scans_complete).await;
5319            let barriers = this
5320                .update(cx, |this, cx| {
5321                    let repos = this.repositories(cx).values().cloned().collect::<Vec<_>>();
5322                    repos
5323                        .into_iter()
5324                        .map(|repo| repo.update(cx, |repo, _| repo.barrier()))
5325                        .collect::<Vec<_>>()
5326                })
5327                .unwrap();
5328            join_all(barriers).await;
5329        })
5330    }
5331
5332    pub fn active_repository(&self, cx: &App) -> Option<Entity<Repository>> {
5333        self.git_store.read(cx).active_repository()
5334    }
5335
5336    pub fn repositories<'a>(&self, cx: &'a App) -> &'a HashMap<RepositoryId, Entity<Repository>> {
5337        self.git_store.read(cx).repositories()
5338    }
5339
5340    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
5341        self.git_store.read(cx).status_for_buffer_id(buffer_id, cx)
5342    }
5343
5344    pub fn set_agent_location(
5345        &mut self,
5346        new_location: Option<AgentLocation>,
5347        cx: &mut Context<Self>,
5348    ) {
5349        if let Some(old_location) = self.agent_location.as_ref() {
5350            old_location
5351                .buffer
5352                .update(cx, |buffer, cx| buffer.remove_agent_selections(cx))
5353                .ok();
5354        }
5355
5356        if let Some(location) = new_location.as_ref() {
5357            location
5358                .buffer
5359                .update(cx, |buffer, cx| {
5360                    buffer.set_agent_selections(
5361                        Arc::from([language::Selection {
5362                            id: 0,
5363                            start: location.position,
5364                            end: location.position,
5365                            reversed: false,
5366                            goal: language::SelectionGoal::None,
5367                        }]),
5368                        false,
5369                        CursorShape::Hollow,
5370                        cx,
5371                    )
5372                })
5373                .ok();
5374        }
5375
5376        self.agent_location = new_location;
5377        cx.emit(Event::AgentLocationChanged);
5378    }
5379
5380    pub fn agent_location(&self) -> Option<AgentLocation> {
5381        self.agent_location.clone()
5382    }
5383
5384    pub fn path_style(&self, cx: &App) -> PathStyle {
5385        self.worktree_store.read(cx).path_style()
5386    }
5387
5388    pub fn contains_local_settings_file(
5389        &self,
5390        worktree_id: WorktreeId,
5391        rel_path: &RelPath,
5392        cx: &App,
5393    ) -> bool {
5394        self.worktree_for_id(worktree_id, cx)
5395            .map_or(false, |worktree| {
5396                worktree.read(cx).entry_for_path(rel_path).is_some()
5397            })
5398    }
5399
5400    pub fn update_local_settings_file(
5401        &self,
5402        worktree_id: WorktreeId,
5403        rel_path: Arc<RelPath>,
5404        cx: &mut App,
5405        update: impl 'static + Send + FnOnce(&mut settings::SettingsContent, &App),
5406    ) {
5407        let Some(worktree) = self.worktree_for_id(worktree_id, cx) else {
5408            // todo(settings_ui) error?
5409            return;
5410        };
5411        cx.spawn(async move |cx| {
5412            let file = worktree
5413                .update(cx, |worktree, cx| worktree.load_file(&rel_path, cx))?
5414                .await
5415                .context("Failed to load settings file")?;
5416
5417            let new_text = cx.read_global::<SettingsStore, _>(|store, cx| {
5418                store.new_text_for_update(file.text, move |settings| update(settings, cx))
5419            })?;
5420            worktree
5421                .update(cx, |worktree, cx| {
5422                    let line_ending = text::LineEnding::detect(&new_text);
5423                    worktree.write_file(rel_path.clone(), new_text.into(), line_ending, cx)
5424                })?
5425                .await
5426                .context("Failed to write settings file")?;
5427
5428            anyhow::Ok(())
5429        })
5430        .detach_and_log_err(cx);
5431    }
5432}
5433
5434pub struct PathMatchCandidateSet {
5435    pub snapshot: Snapshot,
5436    pub include_ignored: bool,
5437    pub include_root_name: bool,
5438    pub candidates: Candidates,
5439}
5440
5441pub enum Candidates {
5442    /// Only consider directories.
5443    Directories,
5444    /// Only consider files.
5445    Files,
5446    /// Consider directories and files.
5447    Entries,
5448}
5449
5450impl<'a> fuzzy::PathMatchCandidateSet<'a> for PathMatchCandidateSet {
5451    type Candidates = PathMatchCandidateSetIter<'a>;
5452
5453    fn id(&self) -> usize {
5454        self.snapshot.id().to_usize()
5455    }
5456
5457    fn len(&self) -> usize {
5458        match self.candidates {
5459            Candidates::Files => {
5460                if self.include_ignored {
5461                    self.snapshot.file_count()
5462                } else {
5463                    self.snapshot.visible_file_count()
5464                }
5465            }
5466
5467            Candidates::Directories => {
5468                if self.include_ignored {
5469                    self.snapshot.dir_count()
5470                } else {
5471                    self.snapshot.visible_dir_count()
5472                }
5473            }
5474
5475            Candidates::Entries => {
5476                if self.include_ignored {
5477                    self.snapshot.entry_count()
5478                } else {
5479                    self.snapshot.visible_entry_count()
5480                }
5481            }
5482        }
5483    }
5484
5485    fn prefix(&self) -> Arc<RelPath> {
5486        if self.snapshot.root_entry().is_some_and(|e| e.is_file()) || self.include_root_name {
5487            self.snapshot.root_name().into()
5488        } else {
5489            RelPath::empty().into()
5490        }
5491    }
5492
5493    fn root_is_file(&self) -> bool {
5494        self.snapshot.root_entry().is_some_and(|f| f.is_file())
5495    }
5496
5497    fn path_style(&self) -> PathStyle {
5498        self.snapshot.path_style()
5499    }
5500
5501    fn candidates(&'a self, start: usize) -> Self::Candidates {
5502        PathMatchCandidateSetIter {
5503            traversal: match self.candidates {
5504                Candidates::Directories => self.snapshot.directories(self.include_ignored, start),
5505                Candidates::Files => self.snapshot.files(self.include_ignored, start),
5506                Candidates::Entries => self.snapshot.entries(self.include_ignored, start),
5507            },
5508        }
5509    }
5510}
5511
5512pub struct PathMatchCandidateSetIter<'a> {
5513    traversal: Traversal<'a>,
5514}
5515
5516impl<'a> Iterator for PathMatchCandidateSetIter<'a> {
5517    type Item = fuzzy::PathMatchCandidate<'a>;
5518
5519    fn next(&mut self) -> Option<Self::Item> {
5520        self.traversal
5521            .next()
5522            .map(|entry| fuzzy::PathMatchCandidate {
5523                is_dir: entry.kind.is_dir(),
5524                path: &entry.path,
5525                char_bag: entry.char_bag,
5526            })
5527    }
5528}
5529
5530impl EventEmitter<Event> for Project {}
5531
5532impl<'a> From<&'a ProjectPath> for SettingsLocation<'a> {
5533    fn from(val: &'a ProjectPath) -> Self {
5534        SettingsLocation {
5535            worktree_id: val.worktree_id,
5536            path: val.path.as_ref(),
5537        }
5538    }
5539}
5540
5541impl<P: Into<Arc<RelPath>>> From<(WorktreeId, P)> for ProjectPath {
5542    fn from((worktree_id, path): (WorktreeId, P)) -> Self {
5543        Self {
5544            worktree_id,
5545            path: path.into(),
5546        }
5547    }
5548}
5549
5550/// ResolvedPath is a path that has been resolved to either a ProjectPath
5551/// or an AbsPath and that *exists*.
5552#[derive(Debug, Clone)]
5553pub enum ResolvedPath {
5554    ProjectPath {
5555        project_path: ProjectPath,
5556        is_dir: bool,
5557    },
5558    AbsPath {
5559        path: String,
5560        is_dir: bool,
5561    },
5562}
5563
5564impl ResolvedPath {
5565    pub fn abs_path(&self) -> Option<&str> {
5566        match self {
5567            Self::AbsPath { path, .. } => Some(path),
5568            _ => None,
5569        }
5570    }
5571
5572    pub fn into_abs_path(self) -> Option<String> {
5573        match self {
5574            Self::AbsPath { path, .. } => Some(path),
5575            _ => None,
5576        }
5577    }
5578
5579    pub fn project_path(&self) -> Option<&ProjectPath> {
5580        match self {
5581            Self::ProjectPath { project_path, .. } => Some(project_path),
5582            _ => None,
5583        }
5584    }
5585
5586    pub fn is_file(&self) -> bool {
5587        !self.is_dir()
5588    }
5589
5590    pub fn is_dir(&self) -> bool {
5591        match self {
5592            Self::ProjectPath { is_dir, .. } => *is_dir,
5593            Self::AbsPath { is_dir, .. } => *is_dir,
5594        }
5595    }
5596}
5597
5598impl ProjectItem for Buffer {
5599    fn try_open(
5600        project: &Entity<Project>,
5601        path: &ProjectPath,
5602        cx: &mut App,
5603    ) -> Option<Task<Result<Entity<Self>>>> {
5604        Some(project.update(cx, |project, cx| project.open_buffer(path.clone(), cx)))
5605    }
5606
5607    fn entry_id(&self, _cx: &App) -> Option<ProjectEntryId> {
5608        File::from_dyn(self.file()).and_then(|file| file.project_entry_id())
5609    }
5610
5611    fn project_path(&self, cx: &App) -> Option<ProjectPath> {
5612        self.file().map(|file| ProjectPath {
5613            worktree_id: file.worktree_id(cx),
5614            path: file.path().clone(),
5615        })
5616    }
5617
5618    fn is_dirty(&self) -> bool {
5619        self.is_dirty()
5620    }
5621}
5622
5623impl Completion {
5624    pub fn kind(&self) -> Option<CompletionItemKind> {
5625        self.source
5626            // `lsp::CompletionListItemDefaults` has no `kind` field
5627            .lsp_completion(false)
5628            .and_then(|lsp_completion| lsp_completion.kind)
5629    }
5630
5631    pub fn label(&self) -> Option<String> {
5632        self.source
5633            .lsp_completion(false)
5634            .map(|lsp_completion| lsp_completion.label.clone())
5635    }
5636
5637    /// A key that can be used to sort completions when displaying
5638    /// them to the user.
5639    pub fn sort_key(&self) -> (usize, &str) {
5640        const DEFAULT_KIND_KEY: usize = 4;
5641        let kind_key = self
5642            .kind()
5643            .and_then(|lsp_completion_kind| match lsp_completion_kind {
5644                lsp::CompletionItemKind::KEYWORD => Some(0),
5645                lsp::CompletionItemKind::VARIABLE => Some(1),
5646                lsp::CompletionItemKind::CONSTANT => Some(2),
5647                lsp::CompletionItemKind::PROPERTY => Some(3),
5648                _ => None,
5649            })
5650            .unwrap_or(DEFAULT_KIND_KEY);
5651        (kind_key, self.label.filter_text())
5652    }
5653
5654    /// Whether this completion is a snippet.
5655    pub fn is_snippet(&self) -> bool {
5656        self.source
5657            // `lsp::CompletionListItemDefaults` has `insert_text_format` field
5658            .lsp_completion(true)
5659            .is_some_and(|lsp_completion| {
5660                lsp_completion.insert_text_format == Some(lsp::InsertTextFormat::SNIPPET)
5661            })
5662    }
5663
5664    /// Returns the corresponding color for this completion.
5665    ///
5666    /// Will return `None` if this completion's kind is not [`CompletionItemKind::COLOR`].
5667    pub fn color(&self) -> Option<Hsla> {
5668        // `lsp::CompletionListItemDefaults` has no `kind` field
5669        let lsp_completion = self.source.lsp_completion(false)?;
5670        if lsp_completion.kind? == CompletionItemKind::COLOR {
5671            return color_extractor::extract_color(&lsp_completion);
5672        }
5673        None
5674    }
5675}
5676
5677fn proto_to_prompt(level: proto::language_server_prompt_request::Level) -> gpui::PromptLevel {
5678    match level {
5679        proto::language_server_prompt_request::Level::Info(_) => gpui::PromptLevel::Info,
5680        proto::language_server_prompt_request::Level::Warning(_) => gpui::PromptLevel::Warning,
5681        proto::language_server_prompt_request::Level::Critical(_) => gpui::PromptLevel::Critical,
5682    }
5683}
5684
5685fn provide_inline_values(
5686    captures: impl Iterator<Item = (Range<usize>, language::DebuggerTextObject)>,
5687    snapshot: &language::BufferSnapshot,
5688    max_row: usize,
5689) -> Vec<InlineValueLocation> {
5690    let mut variables = Vec::new();
5691    let mut variable_position = HashSet::default();
5692    let mut scopes = Vec::new();
5693
5694    let active_debug_line_offset = snapshot.point_to_offset(Point::new(max_row as u32, 0));
5695
5696    for (capture_range, capture_kind) in captures {
5697        match capture_kind {
5698            language::DebuggerTextObject::Variable => {
5699                let variable_name = snapshot
5700                    .text_for_range(capture_range.clone())
5701                    .collect::<String>();
5702                let point = snapshot.offset_to_point(capture_range.end);
5703
5704                while scopes
5705                    .last()
5706                    .is_some_and(|scope: &Range<_>| !scope.contains(&capture_range.start))
5707                {
5708                    scopes.pop();
5709                }
5710
5711                if point.row as usize > max_row {
5712                    break;
5713                }
5714
5715                let scope = if scopes
5716                    .last()
5717                    .is_none_or(|scope| !scope.contains(&active_debug_line_offset))
5718                {
5719                    VariableScope::Global
5720                } else {
5721                    VariableScope::Local
5722                };
5723
5724                if variable_position.insert(capture_range.end) {
5725                    variables.push(InlineValueLocation {
5726                        variable_name,
5727                        scope,
5728                        lookup: VariableLookupKind::Variable,
5729                        row: point.row as usize,
5730                        column: point.column as usize,
5731                    });
5732                }
5733            }
5734            language::DebuggerTextObject::Scope => {
5735                while scopes.last().map_or_else(
5736                    || false,
5737                    |scope: &Range<usize>| {
5738                        !(scope.contains(&capture_range.start)
5739                            && scope.contains(&capture_range.end))
5740                    },
5741                ) {
5742                    scopes.pop();
5743                }
5744                scopes.push(capture_range);
5745            }
5746        }
5747    }
5748
5749    variables
5750}
5751
5752#[cfg(test)]
5753mod disable_ai_settings_tests {
5754    use super::*;
5755    use gpui::TestAppContext;
5756    use settings::Settings;
5757
5758    #[gpui::test]
5759    async fn test_disable_ai_settings_security(cx: &mut TestAppContext) {
5760        cx.update(|cx| {
5761            settings::init(cx);
5762            Project::init_settings(cx);
5763
5764            // Test 1: Default is false (AI enabled)
5765            assert!(
5766                !DisableAiSettings::get_global(cx).disable_ai,
5767                "Default should allow AI"
5768            );
5769        });
5770
5771        let disable_true = serde_json::json!({
5772            "disable_ai": true
5773        })
5774        .to_string();
5775        let disable_false = serde_json::json!({
5776            "disable_ai": false
5777        })
5778        .to_string();
5779
5780        cx.update_global::<SettingsStore, _>(|store, cx| {
5781            store.set_user_settings(&disable_false, cx).unwrap();
5782            store.set_global_settings(&disable_true, cx).unwrap();
5783        });
5784        cx.update(|cx| {
5785            assert!(
5786                DisableAiSettings::get_global(cx).disable_ai,
5787                "Local false cannot override global true"
5788            );
5789        });
5790
5791        cx.update_global::<SettingsStore, _>(|store, cx| {
5792            store.set_global_settings(&disable_false, cx).unwrap();
5793            store.set_user_settings(&disable_true, cx).unwrap();
5794        });
5795
5796        cx.update(|cx| {
5797            assert!(
5798                DisableAiSettings::get_global(cx).disable_ai,
5799                "Local false cannot override global true"
5800            );
5801        });
5802    }
5803}