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