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