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