project.rs

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