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