project.rs

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