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