project.rs

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