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