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