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