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