project.rs

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