project.rs

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