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