project.rs

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