project.rs

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