project.rs

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