project.rs

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