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