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