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