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