project.rs

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