project.rs

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