project.rs

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