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