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