project.rs

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