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