project.rs

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