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