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