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