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