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