project.rs

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