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(), true, 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 update_diagnostics(
2418        &mut self,
2419        language_server_id: LanguageServerId,
2420        params: lsp::PublishDiagnosticsParams,
2421        disk_based_sources: &[String],
2422        cx: &mut ModelContext<Self>,
2423    ) -> Result<()> {
2424        self.lsp_store.update(cx, |lsp_store, cx| {
2425            lsp_store.update_diagnostics(language_server_id, params, disk_based_sources, cx)
2426        })
2427    }
2428
2429    pub fn update_diagnostic_entries(
2430        &mut self,
2431        server_id: LanguageServerId,
2432        abs_path: PathBuf,
2433        version: Option<i32>,
2434        diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
2435        cx: &mut ModelContext<Project>,
2436    ) -> Result<(), anyhow::Error> {
2437        self.lsp_store.update(cx, |lsp_store, cx| {
2438            lsp_store.update_diagnostic_entries(server_id, abs_path, version, diagnostics, cx)
2439        })
2440    }
2441
2442    pub fn reload_buffers(
2443        &self,
2444        buffers: HashSet<Model<Buffer>>,
2445        push_to_history: bool,
2446        cx: &mut ModelContext<Self>,
2447    ) -> Task<Result<ProjectTransaction>> {
2448        self.buffer_store.update(cx, |buffer_store, cx| {
2449            buffer_store.reload_buffers(buffers, push_to_history, cx)
2450        })
2451    }
2452
2453    pub fn format(
2454        &mut self,
2455        buffers: HashSet<Model<Buffer>>,
2456        push_to_history: bool,
2457        trigger: lsp_store::FormatTrigger,
2458        target: lsp_store::FormatTarget,
2459        cx: &mut ModelContext<Project>,
2460    ) -> Task<anyhow::Result<ProjectTransaction>> {
2461        self.lsp_store.update(cx, |lsp_store, cx| {
2462            lsp_store.format(buffers, push_to_history, trigger, target, cx)
2463        })
2464    }
2465
2466    #[inline(never)]
2467    fn definition_impl(
2468        &mut self,
2469        buffer: &Model<Buffer>,
2470        position: PointUtf16,
2471        cx: &mut ModelContext<Self>,
2472    ) -> Task<Result<Vec<LocationLink>>> {
2473        self.request_lsp(
2474            buffer.clone(),
2475            LanguageServerToQuery::Primary,
2476            GetDefinition { position },
2477            cx,
2478        )
2479    }
2480    pub fn definition<T: ToPointUtf16>(
2481        &mut self,
2482        buffer: &Model<Buffer>,
2483        position: T,
2484        cx: &mut ModelContext<Self>,
2485    ) -> Task<Result<Vec<LocationLink>>> {
2486        let position = position.to_point_utf16(buffer.read(cx));
2487        self.definition_impl(buffer, position, cx)
2488    }
2489
2490    fn declaration_impl(
2491        &mut self,
2492        buffer: &Model<Buffer>,
2493        position: PointUtf16,
2494        cx: &mut ModelContext<Self>,
2495    ) -> Task<Result<Vec<LocationLink>>> {
2496        self.request_lsp(
2497            buffer.clone(),
2498            LanguageServerToQuery::Primary,
2499            GetDeclaration { position },
2500            cx,
2501        )
2502    }
2503
2504    pub fn declaration<T: ToPointUtf16>(
2505        &mut self,
2506        buffer: &Model<Buffer>,
2507        position: T,
2508        cx: &mut ModelContext<Self>,
2509    ) -> Task<Result<Vec<LocationLink>>> {
2510        let position = position.to_point_utf16(buffer.read(cx));
2511        self.declaration_impl(buffer, position, cx)
2512    }
2513
2514    fn type_definition_impl(
2515        &mut self,
2516        buffer: &Model<Buffer>,
2517        position: PointUtf16,
2518        cx: &mut ModelContext<Self>,
2519    ) -> Task<Result<Vec<LocationLink>>> {
2520        self.request_lsp(
2521            buffer.clone(),
2522            LanguageServerToQuery::Primary,
2523            GetTypeDefinition { position },
2524            cx,
2525        )
2526    }
2527
2528    pub fn type_definition<T: ToPointUtf16>(
2529        &mut self,
2530        buffer: &Model<Buffer>,
2531        position: T,
2532        cx: &mut ModelContext<Self>,
2533    ) -> Task<Result<Vec<LocationLink>>> {
2534        let position = position.to_point_utf16(buffer.read(cx));
2535        self.type_definition_impl(buffer, position, cx)
2536    }
2537
2538    pub fn implementation<T: ToPointUtf16>(
2539        &mut self,
2540        buffer: &Model<Buffer>,
2541        position: T,
2542        cx: &mut ModelContext<Self>,
2543    ) -> Task<Result<Vec<LocationLink>>> {
2544        let position = position.to_point_utf16(buffer.read(cx));
2545        self.request_lsp(
2546            buffer.clone(),
2547            LanguageServerToQuery::Primary,
2548            GetImplementation { position },
2549            cx,
2550        )
2551    }
2552
2553    pub fn references<T: ToPointUtf16>(
2554        &mut self,
2555        buffer: &Model<Buffer>,
2556        position: T,
2557        cx: &mut ModelContext<Self>,
2558    ) -> Task<Result<Vec<Location>>> {
2559        let position = position.to_point_utf16(buffer.read(cx));
2560        self.request_lsp(
2561            buffer.clone(),
2562            LanguageServerToQuery::Primary,
2563            GetReferences { position },
2564            cx,
2565        )
2566    }
2567
2568    fn document_highlights_impl(
2569        &mut self,
2570        buffer: &Model<Buffer>,
2571        position: PointUtf16,
2572        cx: &mut ModelContext<Self>,
2573    ) -> Task<Result<Vec<DocumentHighlight>>> {
2574        self.request_lsp(
2575            buffer.clone(),
2576            LanguageServerToQuery::Primary,
2577            GetDocumentHighlights { position },
2578            cx,
2579        )
2580    }
2581
2582    pub fn document_highlights<T: ToPointUtf16>(
2583        &mut self,
2584        buffer: &Model<Buffer>,
2585        position: T,
2586        cx: &mut ModelContext<Self>,
2587    ) -> Task<Result<Vec<DocumentHighlight>>> {
2588        let position = position.to_point_utf16(buffer.read(cx));
2589        self.document_highlights_impl(buffer, position, cx)
2590    }
2591
2592    pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
2593        self.lsp_store
2594            .update(cx, |lsp_store, cx| lsp_store.symbols(query, cx))
2595    }
2596
2597    pub fn open_buffer_for_symbol(
2598        &mut self,
2599        symbol: &Symbol,
2600        cx: &mut ModelContext<Self>,
2601    ) -> Task<Result<Model<Buffer>>> {
2602        self.lsp_store.update(cx, |lsp_store, cx| {
2603            lsp_store.open_buffer_for_symbol(symbol, cx)
2604        })
2605    }
2606
2607    pub fn open_server_settings(
2608        &mut self,
2609        cx: &mut ModelContext<Self>,
2610    ) -> Task<Result<Model<Buffer>>> {
2611        let guard = self.retain_remotely_created_models(cx);
2612        let Some(ssh_client) = self.ssh_client.as_ref() else {
2613            return Task::ready(Err(anyhow!("not an ssh project")));
2614        };
2615
2616        let proto_client = ssh_client.read(cx).proto_client();
2617
2618        cx.spawn(|this, mut cx| async move {
2619            let buffer = proto_client
2620                .request(proto::OpenServerSettings {
2621                    project_id: SSH_PROJECT_ID,
2622                })
2623                .await?;
2624
2625            let buffer = this
2626                .update(&mut cx, |this, cx| {
2627                    anyhow::Ok(this.wait_for_remote_buffer(BufferId::new(buffer.buffer_id)?, cx))
2628                })??
2629                .await;
2630
2631            drop(guard);
2632            buffer
2633        })
2634    }
2635
2636    pub fn open_local_buffer_via_lsp(
2637        &mut self,
2638        abs_path: lsp::Url,
2639        language_server_id: LanguageServerId,
2640        language_server_name: LanguageServerName,
2641        cx: &mut ModelContext<Self>,
2642    ) -> Task<Result<Model<Buffer>>> {
2643        self.lsp_store.update(cx, |lsp_store, cx| {
2644            lsp_store.open_local_buffer_via_lsp(
2645                abs_path,
2646                language_server_id,
2647                language_server_name,
2648                cx,
2649            )
2650        })
2651    }
2652
2653    pub fn signature_help<T: ToPointUtf16>(
2654        &self,
2655        buffer: &Model<Buffer>,
2656        position: T,
2657        cx: &mut ModelContext<Self>,
2658    ) -> Task<Vec<SignatureHelp>> {
2659        self.lsp_store.update(cx, |lsp_store, cx| {
2660            lsp_store.signature_help(buffer, position, cx)
2661        })
2662    }
2663
2664    pub fn hover<T: ToPointUtf16>(
2665        &self,
2666        buffer: &Model<Buffer>,
2667        position: T,
2668        cx: &mut ModelContext<Self>,
2669    ) -> Task<Vec<Hover>> {
2670        let position = position.to_point_utf16(buffer.read(cx));
2671        self.lsp_store
2672            .update(cx, |lsp_store, cx| lsp_store.hover(buffer, position, cx))
2673    }
2674
2675    pub fn linked_edit(
2676        &self,
2677        buffer: &Model<Buffer>,
2678        position: Anchor,
2679        cx: &mut ModelContext<Self>,
2680    ) -> Task<Result<Vec<Range<Anchor>>>> {
2681        self.lsp_store.update(cx, |lsp_store, cx| {
2682            lsp_store.linked_edit(buffer, position, cx)
2683        })
2684    }
2685
2686    pub fn completions<T: ToOffset + ToPointUtf16>(
2687        &self,
2688        buffer: &Model<Buffer>,
2689        position: T,
2690        context: CompletionContext,
2691        cx: &mut ModelContext<Self>,
2692    ) -> Task<Result<Vec<Completion>>> {
2693        let position = position.to_point_utf16(buffer.read(cx));
2694        self.lsp_store.update(cx, |lsp_store, cx| {
2695            lsp_store.completions(buffer, position, context, cx)
2696        })
2697    }
2698
2699    pub fn resolve_completions(
2700        &self,
2701        buffer: Model<Buffer>,
2702        completion_indices: Vec<usize>,
2703        completions: Arc<RwLock<Box<[Completion]>>>,
2704        cx: &mut ModelContext<Self>,
2705    ) -> Task<Result<bool>> {
2706        self.lsp_store.update(cx, |lsp_store, cx| {
2707            lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
2708        })
2709    }
2710
2711    pub fn apply_additional_edits_for_completion(
2712        &self,
2713        buffer_handle: Model<Buffer>,
2714        completion: Completion,
2715        push_to_history: bool,
2716        cx: &mut ModelContext<Self>,
2717    ) -> Task<Result<Option<Transaction>>> {
2718        self.lsp_store.update(cx, |lsp_store, cx| {
2719            lsp_store.apply_additional_edits_for_completion(
2720                buffer_handle,
2721                completion,
2722                push_to_history,
2723                cx,
2724            )
2725        })
2726    }
2727
2728    pub fn code_actions<T: Clone + ToOffset>(
2729        &mut self,
2730        buffer_handle: &Model<Buffer>,
2731        range: Range<T>,
2732        cx: &mut ModelContext<Self>,
2733    ) -> Task<Result<Vec<CodeAction>>> {
2734        let buffer = buffer_handle.read(cx);
2735        let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
2736        self.lsp_store.update(cx, |lsp_store, cx| {
2737            lsp_store.code_actions(buffer_handle, range, cx)
2738        })
2739    }
2740
2741    pub fn apply_code_action(
2742        &self,
2743        buffer_handle: Model<Buffer>,
2744        action: CodeAction,
2745        push_to_history: bool,
2746        cx: &mut ModelContext<Self>,
2747    ) -> Task<Result<ProjectTransaction>> {
2748        self.lsp_store.update(cx, |lsp_store, cx| {
2749            lsp_store.apply_code_action(buffer_handle, action, push_to_history, cx)
2750        })
2751    }
2752
2753    fn prepare_rename_impl(
2754        &mut self,
2755        buffer: Model<Buffer>,
2756        position: PointUtf16,
2757        cx: &mut ModelContext<Self>,
2758    ) -> Task<Result<Option<Range<Anchor>>>> {
2759        self.request_lsp(
2760            buffer,
2761            LanguageServerToQuery::Primary,
2762            PrepareRename { position },
2763            cx,
2764        )
2765    }
2766    pub fn prepare_rename<T: ToPointUtf16>(
2767        &mut self,
2768        buffer: Model<Buffer>,
2769        position: T,
2770        cx: &mut ModelContext<Self>,
2771    ) -> Task<Result<Option<Range<Anchor>>>> {
2772        let position = position.to_point_utf16(buffer.read(cx));
2773        self.prepare_rename_impl(buffer, position, cx)
2774    }
2775
2776    fn perform_rename_impl(
2777        &mut self,
2778        buffer: Model<Buffer>,
2779        position: PointUtf16,
2780        new_name: String,
2781        push_to_history: bool,
2782        cx: &mut ModelContext<Self>,
2783    ) -> Task<Result<ProjectTransaction>> {
2784        let position = position.to_point_utf16(buffer.read(cx));
2785        self.request_lsp(
2786            buffer,
2787            LanguageServerToQuery::Primary,
2788            PerformRename {
2789                position,
2790                new_name,
2791                push_to_history,
2792            },
2793            cx,
2794        )
2795    }
2796
2797    pub fn perform_rename<T: ToPointUtf16>(
2798        &mut self,
2799        buffer: Model<Buffer>,
2800        position: T,
2801        new_name: String,
2802        cx: &mut ModelContext<Self>,
2803    ) -> Task<Result<ProjectTransaction>> {
2804        let position = position.to_point_utf16(buffer.read(cx));
2805        self.perform_rename_impl(buffer, position, new_name, true, cx)
2806    }
2807
2808    pub fn on_type_format<T: ToPointUtf16>(
2809        &mut self,
2810        buffer: Model<Buffer>,
2811        position: T,
2812        trigger: String,
2813        push_to_history: bool,
2814        cx: &mut ModelContext<Self>,
2815    ) -> Task<Result<Option<Transaction>>> {
2816        self.lsp_store.update(cx, |lsp_store, cx| {
2817            lsp_store.on_type_format(buffer, position, trigger, push_to_history, cx)
2818        })
2819    }
2820
2821    pub fn inlay_hints<T: ToOffset>(
2822        &mut self,
2823        buffer_handle: Model<Buffer>,
2824        range: Range<T>,
2825        cx: &mut ModelContext<Self>,
2826    ) -> Task<anyhow::Result<Vec<InlayHint>>> {
2827        let buffer = buffer_handle.read(cx);
2828        let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
2829        self.lsp_store.update(cx, |lsp_store, cx| {
2830            lsp_store.inlay_hints(buffer_handle, range, cx)
2831        })
2832    }
2833
2834    pub fn resolve_inlay_hint(
2835        &self,
2836        hint: InlayHint,
2837        buffer_handle: Model<Buffer>,
2838        server_id: LanguageServerId,
2839        cx: &mut ModelContext<Self>,
2840    ) -> Task<anyhow::Result<InlayHint>> {
2841        self.lsp_store.update(cx, |lsp_store, cx| {
2842            lsp_store.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
2843        })
2844    }
2845
2846    pub fn search(
2847        &mut self,
2848        query: SearchQuery,
2849        cx: &mut ModelContext<Self>,
2850    ) -> Receiver<SearchResult> {
2851        let (result_tx, result_rx) = smol::channel::unbounded();
2852
2853        let matching_buffers_rx = if query.is_opened_only() {
2854            self.sort_search_candidates(&query, cx)
2855        } else {
2856            self.find_search_candidate_buffers(&query, MAX_SEARCH_RESULT_FILES + 1, cx)
2857        };
2858
2859        cx.spawn(|_, cx| async move {
2860            let mut range_count = 0;
2861            let mut buffer_count = 0;
2862            let mut limit_reached = false;
2863            let query = Arc::new(query);
2864            let mut chunks = matching_buffers_rx.ready_chunks(64);
2865
2866            // Now that we know what paths match the query, we will load at most
2867            // 64 buffers at a time to avoid overwhelming the main thread. For each
2868            // opened buffer, we will spawn a background task that retrieves all the
2869            // ranges in the buffer matched by the query.
2870            'outer: while let Some(matching_buffer_chunk) = chunks.next().await {
2871                let mut chunk_results = Vec::new();
2872                for buffer in matching_buffer_chunk {
2873                    let buffer = buffer.clone();
2874                    let query = query.clone();
2875                    let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot())?;
2876                    chunk_results.push(cx.background_executor().spawn(async move {
2877                        let ranges = query
2878                            .search(&snapshot, None)
2879                            .await
2880                            .iter()
2881                            .map(|range| {
2882                                snapshot.anchor_before(range.start)
2883                                    ..snapshot.anchor_after(range.end)
2884                            })
2885                            .collect::<Vec<_>>();
2886                        anyhow::Ok((buffer, ranges))
2887                    }));
2888                }
2889
2890                let chunk_results = futures::future::join_all(chunk_results).await;
2891                for result in chunk_results {
2892                    if let Some((buffer, ranges)) = result.log_err() {
2893                        range_count += ranges.len();
2894                        buffer_count += 1;
2895                        result_tx
2896                            .send(SearchResult::Buffer { buffer, ranges })
2897                            .await?;
2898                        if buffer_count > MAX_SEARCH_RESULT_FILES
2899                            || range_count > MAX_SEARCH_RESULT_RANGES
2900                        {
2901                            limit_reached = true;
2902                            break 'outer;
2903                        }
2904                    }
2905                }
2906            }
2907
2908            if limit_reached {
2909                result_tx.send(SearchResult::LimitReached).await?;
2910            }
2911
2912            anyhow::Ok(())
2913        })
2914        .detach();
2915
2916        result_rx
2917    }
2918
2919    fn find_search_candidate_buffers(
2920        &mut self,
2921        query: &SearchQuery,
2922        limit: usize,
2923        cx: &mut ModelContext<Project>,
2924    ) -> Receiver<Model<Buffer>> {
2925        if self.is_local() {
2926            let fs = self.fs.clone();
2927            self.buffer_store.update(cx, |buffer_store, cx| {
2928                buffer_store.find_search_candidates(query, limit, fs, cx)
2929            })
2930        } else {
2931            self.find_search_candidates_remote(query, limit, cx)
2932        }
2933    }
2934
2935    fn sort_search_candidates(
2936        &mut self,
2937        search_query: &SearchQuery,
2938        cx: &mut ModelContext<Project>,
2939    ) -> Receiver<Model<Buffer>> {
2940        let worktree_store = self.worktree_store.read(cx);
2941        let mut buffers = search_query
2942            .buffers()
2943            .into_iter()
2944            .flatten()
2945            .filter(|buffer| {
2946                let b = buffer.read(cx);
2947                if let Some(file) = b.file() {
2948                    if !search_query.file_matches(file.path()) {
2949                        return false;
2950                    }
2951                    if let Some(entry) = b
2952                        .entry_id(cx)
2953                        .and_then(|entry_id| worktree_store.entry_for_id(entry_id, cx))
2954                    {
2955                        if entry.is_ignored && !search_query.include_ignored() {
2956                            return false;
2957                        }
2958                    }
2959                }
2960                true
2961            })
2962            .collect::<Vec<_>>();
2963        let (tx, rx) = smol::channel::unbounded();
2964        buffers.sort_by(|a, b| match (a.read(cx).file(), b.read(cx).file()) {
2965            (None, None) => a.read(cx).remote_id().cmp(&b.read(cx).remote_id()),
2966            (None, Some(_)) => std::cmp::Ordering::Less,
2967            (Some(_), None) => std::cmp::Ordering::Greater,
2968            (Some(a), Some(b)) => compare_paths((a.path(), true), (b.path(), true)),
2969        });
2970        for buffer in buffers {
2971            tx.send_blocking(buffer.clone()).unwrap()
2972        }
2973
2974        rx
2975    }
2976
2977    fn find_search_candidates_remote(
2978        &mut self,
2979        query: &SearchQuery,
2980        limit: usize,
2981        cx: &mut ModelContext<Project>,
2982    ) -> Receiver<Model<Buffer>> {
2983        let (tx, rx) = smol::channel::unbounded();
2984
2985        let (client, remote_id): (AnyProtoClient, _) = if let Some(ssh_client) = &self.ssh_client {
2986            (ssh_client.read(cx).proto_client(), 0)
2987        } else if let Some(remote_id) = self.remote_id() {
2988            (self.client.clone().into(), remote_id)
2989        } else {
2990            return rx;
2991        };
2992
2993        let request = client.request(proto::FindSearchCandidates {
2994            project_id: remote_id,
2995            query: Some(query.to_proto()),
2996            limit: limit as _,
2997        });
2998        let guard = self.retain_remotely_created_models(cx);
2999
3000        cx.spawn(move |this, mut cx| async move {
3001            let response = request.await?;
3002            for buffer_id in response.buffer_ids {
3003                let buffer_id = BufferId::new(buffer_id)?;
3004                let buffer = this
3005                    .update(&mut cx, |this, cx| {
3006                        this.wait_for_remote_buffer(buffer_id, cx)
3007                    })?
3008                    .await?;
3009                let _ = tx.send(buffer).await;
3010            }
3011
3012            drop(guard);
3013            anyhow::Ok(())
3014        })
3015        .detach_and_log_err(cx);
3016        rx
3017    }
3018
3019    pub fn request_lsp<R: LspCommand>(
3020        &mut self,
3021        buffer_handle: Model<Buffer>,
3022        server: LanguageServerToQuery,
3023        request: R,
3024        cx: &mut ModelContext<Self>,
3025    ) -> Task<Result<R::Response>>
3026    where
3027        <R::LspRequest as lsp::request::Request>::Result: Send,
3028        <R::LspRequest as lsp::request::Request>::Params: Send,
3029    {
3030        let guard = self.retain_remotely_created_models(cx);
3031        let task = self.lsp_store.update(cx, |lsp_store, cx| {
3032            lsp_store.request_lsp(buffer_handle, server, request, cx)
3033        });
3034        cx.spawn(|_, _| async move {
3035            let result = task.await;
3036            drop(guard);
3037            result
3038        })
3039    }
3040
3041    /// Move a worktree to a new position in the worktree order.
3042    ///
3043    /// The worktree will moved to the opposite side of the destination worktree.
3044    ///
3045    /// # Example
3046    ///
3047    /// Given the worktree order `[11, 22, 33]` and a call to move worktree `22` to `33`,
3048    /// worktree_order will be updated to produce the indexes `[11, 33, 22]`.
3049    ///
3050    /// Given the worktree order `[11, 22, 33]` and a call to move worktree `22` to `11`,
3051    /// worktree_order will be updated to produce the indexes `[22, 11, 33]`.
3052    ///
3053    /// # Errors
3054    ///
3055    /// An error will be returned if the worktree or destination worktree are not found.
3056    pub fn move_worktree(
3057        &mut self,
3058        source: WorktreeId,
3059        destination: WorktreeId,
3060        cx: &mut ModelContext<'_, Self>,
3061    ) -> Result<()> {
3062        self.worktree_store.update(cx, |worktree_store, cx| {
3063            worktree_store.move_worktree(source, destination, cx)
3064        })
3065    }
3066
3067    pub fn find_or_create_worktree(
3068        &mut self,
3069        abs_path: impl AsRef<Path>,
3070        visible: bool,
3071        cx: &mut ModelContext<Self>,
3072    ) -> Task<Result<(Model<Worktree>, PathBuf)>> {
3073        self.worktree_store.update(cx, |worktree_store, cx| {
3074            worktree_store.find_or_create_worktree(abs_path, visible, cx)
3075        })
3076    }
3077
3078    pub fn find_worktree(
3079        &self,
3080        abs_path: &Path,
3081        cx: &AppContext,
3082    ) -> Option<(Model<Worktree>, PathBuf)> {
3083        self.worktree_store.read_with(cx, |worktree_store, cx| {
3084            worktree_store.find_worktree(abs_path, cx)
3085        })
3086    }
3087
3088    pub fn is_shared(&self) -> bool {
3089        match &self.client_state {
3090            ProjectClientState::Shared { .. } => true,
3091            ProjectClientState::Local => false,
3092            ProjectClientState::Remote { .. } => true,
3093        }
3094    }
3095
3096    /// Returns the resolved version of `path`, that was found in `buffer`, if it exists.
3097    pub fn resolve_path_in_buffer(
3098        &self,
3099        path: &str,
3100        buffer: &Model<Buffer>,
3101        cx: &mut ModelContext<Self>,
3102    ) -> Task<Option<ResolvedPath>> {
3103        let path_buf = PathBuf::from(path);
3104        if path_buf.is_absolute() || path.starts_with("~") {
3105            self.resolve_abs_path(path, cx)
3106        } else {
3107            self.resolve_path_in_worktrees(path_buf, buffer, cx)
3108        }
3109    }
3110
3111    pub fn resolve_abs_file_path(
3112        &self,
3113        path: &str,
3114        cx: &mut ModelContext<Self>,
3115    ) -> Task<Option<ResolvedPath>> {
3116        let resolve_task = self.resolve_abs_path(path, cx);
3117        cx.background_executor().spawn(async move {
3118            let resolved_path = resolve_task.await;
3119            resolved_path.filter(|path| path.is_file())
3120        })
3121    }
3122
3123    pub fn resolve_abs_path(
3124        &self,
3125        path: &str,
3126        cx: &mut ModelContext<Self>,
3127    ) -> Task<Option<ResolvedPath>> {
3128        if self.is_local() {
3129            let expanded = PathBuf::from(shellexpand::tilde(&path).into_owned());
3130            let fs = self.fs.clone();
3131            cx.background_executor().spawn(async move {
3132                let path = expanded.as_path();
3133                let metadata = fs.metadata(path).await.ok().flatten();
3134
3135                metadata.map(|metadata| ResolvedPath::AbsPath {
3136                    path: expanded,
3137                    is_dir: metadata.is_dir,
3138                })
3139            })
3140        } else if let Some(ssh_client) = self.ssh_client.as_ref() {
3141            let request = ssh_client
3142                .read(cx)
3143                .proto_client()
3144                .request(proto::GetPathMetadata {
3145                    project_id: SSH_PROJECT_ID,
3146                    path: path.to_string(),
3147                });
3148            cx.background_executor().spawn(async move {
3149                let response = request.await.log_err()?;
3150                if response.exists {
3151                    Some(ResolvedPath::AbsPath {
3152                        path: PathBuf::from(response.path),
3153                        is_dir: response.is_dir,
3154                    })
3155                } else {
3156                    None
3157                }
3158            })
3159        } else {
3160            return Task::ready(None);
3161        }
3162    }
3163
3164    fn resolve_path_in_worktrees(
3165        &self,
3166        path: PathBuf,
3167        buffer: &Model<Buffer>,
3168        cx: &mut ModelContext<Self>,
3169    ) -> Task<Option<ResolvedPath>> {
3170        let mut candidates = vec![path.clone()];
3171
3172        if let Some(file) = buffer.read(cx).file() {
3173            if let Some(dir) = file.path().parent() {
3174                let joined = dir.to_path_buf().join(path);
3175                candidates.push(joined);
3176            }
3177        }
3178
3179        let worktrees = self.worktrees(cx).collect::<Vec<_>>();
3180        cx.spawn(|_, mut cx| async move {
3181            for worktree in worktrees {
3182                for candidate in candidates.iter() {
3183                    let path = worktree
3184                        .update(&mut cx, |worktree, _| {
3185                            let root_entry_path = &worktree.root_entry()?.path;
3186
3187                            let resolved = resolve_path(root_entry_path, candidate);
3188
3189                            let stripped =
3190                                resolved.strip_prefix(root_entry_path).unwrap_or(&resolved);
3191
3192                            worktree.entry_for_path(stripped).map(|entry| {
3193                                let project_path = ProjectPath {
3194                                    worktree_id: worktree.id(),
3195                                    path: entry.path.clone(),
3196                                };
3197                                ResolvedPath::ProjectPath {
3198                                    project_path,
3199                                    is_dir: entry.is_dir(),
3200                                }
3201                            })
3202                        })
3203                        .ok()?;
3204
3205                    if path.is_some() {
3206                        return path;
3207                    }
3208                }
3209            }
3210            None
3211        })
3212    }
3213
3214    pub fn list_directory(
3215        &self,
3216        query: String,
3217        cx: &mut ModelContext<Self>,
3218    ) -> Task<Result<Vec<PathBuf>>> {
3219        if self.is_local() {
3220            DirectoryLister::Local(self.fs.clone()).list_directory(query, cx)
3221        } else if let Some(session) = self.ssh_client.as_ref() {
3222            let request = proto::ListRemoteDirectory {
3223                dev_server_id: SSH_PROJECT_ID,
3224                path: query,
3225            };
3226
3227            let response = session.read(cx).proto_client().request(request);
3228            cx.background_executor().spawn(async move {
3229                let response = response.await?;
3230                Ok(response.entries.into_iter().map(PathBuf::from).collect())
3231            })
3232        } else {
3233            Task::ready(Err(anyhow!("cannot list directory in remote project")))
3234        }
3235    }
3236
3237    pub fn create_worktree(
3238        &mut self,
3239        abs_path: impl AsRef<Path>,
3240        visible: bool,
3241        cx: &mut ModelContext<Self>,
3242    ) -> Task<Result<Model<Worktree>>> {
3243        self.worktree_store.update(cx, |worktree_store, cx| {
3244            worktree_store.create_worktree(abs_path, visible, cx)
3245        })
3246    }
3247
3248    pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
3249        self.worktree_store.update(cx, |worktree_store, cx| {
3250            worktree_store.remove_worktree(id_to_remove, cx);
3251        });
3252    }
3253
3254    fn add_worktree(&mut self, worktree: &Model<Worktree>, cx: &mut ModelContext<Self>) {
3255        self.worktree_store.update(cx, |worktree_store, cx| {
3256            worktree_store.add(worktree, cx);
3257        });
3258    }
3259
3260    pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
3261        let new_active_entry = entry.and_then(|project_path| {
3262            let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
3263            let entry = worktree.read(cx).entry_for_path(project_path.path)?;
3264            Some(entry.id)
3265        });
3266        if new_active_entry != self.active_entry {
3267            self.active_entry = new_active_entry;
3268            self.lsp_store.update(cx, |lsp_store, _| {
3269                lsp_store.set_active_entry(new_active_entry);
3270            });
3271            cx.emit(Event::ActiveEntryChanged(new_active_entry));
3272        }
3273    }
3274
3275    pub fn language_servers_running_disk_based_diagnostics<'a>(
3276        &'a self,
3277        cx: &'a AppContext,
3278    ) -> impl Iterator<Item = LanguageServerId> + 'a {
3279        self.lsp_store
3280            .read(cx)
3281            .language_servers_running_disk_based_diagnostics()
3282    }
3283
3284    pub fn diagnostic_summary(&self, include_ignored: bool, cx: &AppContext) -> DiagnosticSummary {
3285        let mut summary = DiagnosticSummary::default();
3286        for (_, _, path_summary) in self.diagnostic_summaries(include_ignored, cx) {
3287            summary.error_count += path_summary.error_count;
3288            summary.warning_count += path_summary.warning_count;
3289        }
3290        summary
3291    }
3292
3293    pub fn diagnostic_summaries<'a>(
3294        &'a self,
3295        include_ignored: bool,
3296        cx: &'a AppContext,
3297    ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
3298        self.lsp_store
3299            .read(cx)
3300            .diagnostic_summaries(include_ignored, cx)
3301    }
3302
3303    pub fn active_entry(&self) -> Option<ProjectEntryId> {
3304        self.active_entry
3305    }
3306
3307    pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Entry> {
3308        self.worktree_store.read(cx).entry_for_path(path, cx)
3309    }
3310
3311    pub fn path_for_entry(&self, entry_id: ProjectEntryId, cx: &AppContext) -> Option<ProjectPath> {
3312        let worktree = self.worktree_for_entry(entry_id, cx)?;
3313        let worktree = worktree.read(cx);
3314        let worktree_id = worktree.id();
3315        let path = worktree.entry_for_id(entry_id)?.path.clone();
3316        Some(ProjectPath { worktree_id, path })
3317    }
3318
3319    pub fn absolute_path(&self, project_path: &ProjectPath, cx: &AppContext) -> Option<PathBuf> {
3320        self.worktree_for_id(project_path.worktree_id, cx)?
3321            .read(cx)
3322            .absolutize(&project_path.path)
3323            .ok()
3324    }
3325
3326    /// Attempts to find a `ProjectPath` corresponding to the given path. If the path
3327    /// is a *full path*, meaning it starts with the root name of a worktree, we'll locate
3328    /// it in that worktree. Otherwise, we'll attempt to find it as a relative path in
3329    /// the first visible worktree that has an entry for that relative path.
3330    ///
3331    /// We use this to resolve edit steps, when there's a chance an LLM may omit the workree
3332    /// root name from paths.
3333    ///
3334    /// # Arguments
3335    ///
3336    /// * `path` - A full path that starts with a worktree root name, or alternatively a
3337    ///            relative path within a visible worktree.
3338    /// * `cx` - A reference to the `AppContext`.
3339    ///
3340    /// # Returns
3341    ///
3342    /// Returns `Some(ProjectPath)` if a matching worktree is found, otherwise `None`.
3343    pub fn find_project_path(&self, path: &Path, cx: &AppContext) -> Option<ProjectPath> {
3344        let worktree_store = self.worktree_store.read(cx);
3345
3346        for worktree in worktree_store.visible_worktrees(cx) {
3347            let worktree_root_name = worktree.read(cx).root_name();
3348            if let Ok(relative_path) = path.strip_prefix(worktree_root_name) {
3349                return Some(ProjectPath {
3350                    worktree_id: worktree.read(cx).id(),
3351                    path: relative_path.into(),
3352                });
3353            }
3354        }
3355
3356        for worktree in worktree_store.visible_worktrees(cx) {
3357            let worktree = worktree.read(cx);
3358            if let Some(entry) = worktree.entry_for_path(path) {
3359                return Some(ProjectPath {
3360                    worktree_id: worktree.id(),
3361                    path: entry.path.clone(),
3362                });
3363            }
3364        }
3365
3366        None
3367    }
3368
3369    pub fn get_workspace_root(
3370        &self,
3371        project_path: &ProjectPath,
3372        cx: &AppContext,
3373    ) -> Option<PathBuf> {
3374        Some(
3375            self.worktree_for_id(project_path.worktree_id, cx)?
3376                .read(cx)
3377                .abs_path()
3378                .to_path_buf(),
3379        )
3380    }
3381
3382    pub fn get_repo(
3383        &self,
3384        project_path: &ProjectPath,
3385        cx: &AppContext,
3386    ) -> Option<Arc<dyn GitRepository>> {
3387        self.worktree_for_id(project_path.worktree_id, cx)?
3388            .read(cx)
3389            .as_local()?
3390            .local_git_repo(&project_path.path)
3391    }
3392
3393    pub fn get_first_worktree_root_repo(&self, cx: &AppContext) -> Option<Arc<dyn GitRepository>> {
3394        let worktree = self.visible_worktrees(cx).next()?.read(cx).as_local()?;
3395        let root_entry = worktree.root_git_entry()?;
3396        worktree.get_local_repo(&root_entry)?.repo().clone().into()
3397    }
3398
3399    pub fn branches(
3400        &self,
3401        project_path: ProjectPath,
3402        cx: &AppContext,
3403    ) -> Task<Result<Vec<git::repository::Branch>>> {
3404        self.worktree_store().read(cx).branches(project_path, cx)
3405    }
3406
3407    pub fn update_or_create_branch(
3408        &self,
3409        repository: ProjectPath,
3410        new_branch: String,
3411        cx: &AppContext,
3412    ) -> Task<Result<()>> {
3413        self.worktree_store()
3414            .read(cx)
3415            .update_or_create_branch(repository, new_branch, cx)
3416    }
3417
3418    pub fn blame_buffer(
3419        &self,
3420        buffer: &Model<Buffer>,
3421        version: Option<clock::Global>,
3422        cx: &AppContext,
3423    ) -> Task<Result<Option<Blame>>> {
3424        self.buffer_store.read(cx).blame_buffer(buffer, version, cx)
3425    }
3426
3427    pub fn get_permalink_to_line(
3428        &self,
3429        buffer: &Model<Buffer>,
3430        selection: Range<u32>,
3431        cx: &AppContext,
3432    ) -> Task<Result<url::Url>> {
3433        self.buffer_store
3434            .read(cx)
3435            .get_permalink_to_line(buffer, selection, cx)
3436    }
3437
3438    // RPC message handlers
3439
3440    async fn handle_unshare_project(
3441        this: Model<Self>,
3442        _: TypedEnvelope<proto::UnshareProject>,
3443        mut cx: AsyncAppContext,
3444    ) -> Result<()> {
3445        this.update(&mut cx, |this, cx| {
3446            if this.is_local() || this.is_via_ssh() {
3447                this.unshare(cx)?;
3448            } else {
3449                this.disconnected_from_host(cx);
3450            }
3451            Ok(())
3452        })?
3453    }
3454
3455    async fn handle_add_collaborator(
3456        this: Model<Self>,
3457        mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
3458        mut cx: AsyncAppContext,
3459    ) -> Result<()> {
3460        let collaborator = envelope
3461            .payload
3462            .collaborator
3463            .take()
3464            .ok_or_else(|| anyhow!("empty collaborator"))?;
3465
3466        let collaborator = Collaborator::from_proto(collaborator)?;
3467        this.update(&mut cx, |this, cx| {
3468            this.buffer_store.update(cx, |buffer_store, _| {
3469                buffer_store.forget_shared_buffers_for(&collaborator.peer_id);
3470            });
3471            cx.emit(Event::CollaboratorJoined(collaborator.peer_id));
3472            this.collaborators
3473                .insert(collaborator.peer_id, collaborator);
3474            cx.notify();
3475        })?;
3476
3477        Ok(())
3478    }
3479
3480    async fn handle_update_project_collaborator(
3481        this: Model<Self>,
3482        envelope: TypedEnvelope<proto::UpdateProjectCollaborator>,
3483        mut cx: AsyncAppContext,
3484    ) -> Result<()> {
3485        let old_peer_id = envelope
3486            .payload
3487            .old_peer_id
3488            .ok_or_else(|| anyhow!("missing old peer id"))?;
3489        let new_peer_id = envelope
3490            .payload
3491            .new_peer_id
3492            .ok_or_else(|| anyhow!("missing new peer id"))?;
3493        this.update(&mut cx, |this, cx| {
3494            let collaborator = this
3495                .collaborators
3496                .remove(&old_peer_id)
3497                .ok_or_else(|| anyhow!("received UpdateProjectCollaborator for unknown peer"))?;
3498            let is_host = collaborator.is_host;
3499            this.collaborators.insert(new_peer_id, collaborator);
3500
3501            log::info!("peer {} became {}", old_peer_id, new_peer_id,);
3502            this.buffer_store.update(cx, |buffer_store, _| {
3503                buffer_store.update_peer_id(&old_peer_id, new_peer_id)
3504            });
3505
3506            if is_host {
3507                this.buffer_store
3508                    .update(cx, |buffer_store, _| buffer_store.discard_incomplete());
3509                this.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
3510                    .unwrap();
3511                cx.emit(Event::HostReshared);
3512            }
3513
3514            cx.emit(Event::CollaboratorUpdated {
3515                old_peer_id,
3516                new_peer_id,
3517            });
3518            cx.notify();
3519            Ok(())
3520        })?
3521    }
3522
3523    async fn handle_remove_collaborator(
3524        this: Model<Self>,
3525        envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
3526        mut cx: AsyncAppContext,
3527    ) -> Result<()> {
3528        this.update(&mut cx, |this, cx| {
3529            let peer_id = envelope
3530                .payload
3531                .peer_id
3532                .ok_or_else(|| anyhow!("invalid peer id"))?;
3533            let replica_id = this
3534                .collaborators
3535                .remove(&peer_id)
3536                .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
3537                .replica_id;
3538            this.buffer_store.update(cx, |buffer_store, cx| {
3539                buffer_store.forget_shared_buffers_for(&peer_id);
3540                for buffer in buffer_store.buffers() {
3541                    buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
3542                }
3543            });
3544
3545            cx.emit(Event::CollaboratorLeft(peer_id));
3546            cx.notify();
3547            Ok(())
3548        })?
3549    }
3550
3551    async fn handle_update_project(
3552        this: Model<Self>,
3553        envelope: TypedEnvelope<proto::UpdateProject>,
3554        mut cx: AsyncAppContext,
3555    ) -> Result<()> {
3556        this.update(&mut cx, |this, cx| {
3557            // Don't handle messages that were sent before the response to us joining the project
3558            if envelope.message_id > this.join_project_response_message_id {
3559                this.set_worktrees_from_proto(envelope.payload.worktrees, cx)?;
3560            }
3561            Ok(())
3562        })?
3563    }
3564
3565    async fn handle_toast(
3566        this: Model<Self>,
3567        envelope: TypedEnvelope<proto::Toast>,
3568        mut cx: AsyncAppContext,
3569    ) -> Result<()> {
3570        this.update(&mut cx, |_, cx| {
3571            cx.emit(Event::Toast {
3572                notification_id: envelope.payload.notification_id.into(),
3573                message: envelope.payload.message,
3574            });
3575            Ok(())
3576        })?
3577    }
3578
3579    async fn handle_language_server_prompt_request(
3580        this: Model<Self>,
3581        envelope: TypedEnvelope<proto::LanguageServerPromptRequest>,
3582        mut cx: AsyncAppContext,
3583    ) -> Result<proto::LanguageServerPromptResponse> {
3584        let (tx, mut rx) = smol::channel::bounded(1);
3585        let actions: Vec<_> = envelope
3586            .payload
3587            .actions
3588            .into_iter()
3589            .map(|action| MessageActionItem {
3590                title: action,
3591                properties: Default::default(),
3592            })
3593            .collect();
3594        this.update(&mut cx, |_, cx| {
3595            cx.emit(Event::LanguageServerPrompt(LanguageServerPromptRequest {
3596                level: proto_to_prompt(envelope.payload.level.context("Invalid prompt level")?),
3597                message: envelope.payload.message,
3598                actions: actions.clone(),
3599                lsp_name: envelope.payload.lsp_name,
3600                response_channel: tx,
3601            }));
3602
3603            anyhow::Ok(())
3604        })??;
3605
3606        // We drop `this` to avoid holding a reference in this future for too
3607        // long.
3608        // If we keep the reference, we might not drop the `Project` early
3609        // enough when closing a window and it will only get releases on the
3610        // next `flush_effects()` call.
3611        drop(this);
3612
3613        let answer = rx.next().await;
3614
3615        Ok(LanguageServerPromptResponse {
3616            action_response: answer.and_then(|answer| {
3617                actions
3618                    .iter()
3619                    .position(|action| *action == answer)
3620                    .map(|index| index as u64)
3621            }),
3622        })
3623    }
3624
3625    async fn handle_hide_toast(
3626        this: Model<Self>,
3627        envelope: TypedEnvelope<proto::HideToast>,
3628        mut cx: AsyncAppContext,
3629    ) -> Result<()> {
3630        this.update(&mut cx, |_, cx| {
3631            cx.emit(Event::HideToast {
3632                notification_id: envelope.payload.notification_id.into(),
3633            });
3634            Ok(())
3635        })?
3636    }
3637
3638    // Collab sends UpdateWorktree protos as messages
3639    async fn handle_update_worktree(
3640        this: Model<Self>,
3641        envelope: TypedEnvelope<proto::UpdateWorktree>,
3642        mut cx: AsyncAppContext,
3643    ) -> Result<()> {
3644        this.update(&mut cx, |this, cx| {
3645            let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
3646            if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
3647                worktree.update(cx, |worktree, _| {
3648                    let worktree = worktree.as_remote_mut().unwrap();
3649                    worktree.update_from_remote(envelope.payload);
3650                });
3651            }
3652            Ok(())
3653        })?
3654    }
3655
3656    async fn handle_update_buffer_from_ssh(
3657        this: Model<Self>,
3658        envelope: TypedEnvelope<proto::UpdateBuffer>,
3659        cx: AsyncAppContext,
3660    ) -> Result<proto::Ack> {
3661        let buffer_store = this.read_with(&cx, |this, cx| {
3662            if let Some(remote_id) = this.remote_id() {
3663                let mut payload = envelope.payload.clone();
3664                payload.project_id = remote_id;
3665                cx.background_executor()
3666                    .spawn(this.client.request(payload))
3667                    .detach_and_log_err(cx);
3668            }
3669            this.buffer_store.clone()
3670        })?;
3671        BufferStore::handle_update_buffer(buffer_store, envelope, cx).await
3672    }
3673
3674    async fn handle_update_buffer(
3675        this: Model<Self>,
3676        envelope: TypedEnvelope<proto::UpdateBuffer>,
3677        cx: AsyncAppContext,
3678    ) -> Result<proto::Ack> {
3679        let buffer_store = this.read_with(&cx, |this, cx| {
3680            if let Some(ssh) = &this.ssh_client {
3681                let mut payload = envelope.payload.clone();
3682                payload.project_id = SSH_PROJECT_ID;
3683                cx.background_executor()
3684                    .spawn(ssh.read(cx).proto_client().request(payload))
3685                    .detach_and_log_err(cx);
3686            }
3687            this.buffer_store.clone()
3688        })?;
3689        BufferStore::handle_update_buffer(buffer_store, envelope, cx).await
3690    }
3691
3692    fn retain_remotely_created_models(
3693        &mut self,
3694        cx: &mut ModelContext<Self>,
3695    ) -> RemotelyCreatedModelGuard {
3696        {
3697            let mut remotely_create_models = self.remotely_created_models.lock();
3698            if remotely_create_models.retain_count == 0 {
3699                remotely_create_models.buffers = self.buffer_store.read(cx).buffers().collect();
3700                remotely_create_models.worktrees =
3701                    self.worktree_store.read(cx).worktrees().collect();
3702            }
3703            remotely_create_models.retain_count += 1;
3704        }
3705        RemotelyCreatedModelGuard {
3706            remote_models: Arc::downgrade(&self.remotely_created_models),
3707        }
3708    }
3709
3710    async fn handle_create_buffer_for_peer(
3711        this: Model<Self>,
3712        envelope: TypedEnvelope<proto::CreateBufferForPeer>,
3713        mut cx: AsyncAppContext,
3714    ) -> Result<()> {
3715        this.update(&mut cx, |this, cx| {
3716            this.buffer_store.update(cx, |buffer_store, cx| {
3717                buffer_store.handle_create_buffer_for_peer(
3718                    envelope,
3719                    this.replica_id(),
3720                    this.capability(),
3721                    cx,
3722                )
3723            })
3724        })?
3725    }
3726
3727    async fn handle_synchronize_buffers(
3728        this: Model<Self>,
3729        envelope: TypedEnvelope<proto::SynchronizeBuffers>,
3730        mut cx: AsyncAppContext,
3731    ) -> Result<proto::SynchronizeBuffersResponse> {
3732        let response = this.update(&mut cx, |this, cx| {
3733            let client = this.client.clone();
3734            this.buffer_store.update(cx, |this, cx| {
3735                this.handle_synchronize_buffers(envelope, cx, client)
3736            })
3737        })??;
3738
3739        Ok(response)
3740    }
3741
3742    async fn handle_search_candidate_buffers(
3743        this: Model<Self>,
3744        envelope: TypedEnvelope<proto::FindSearchCandidates>,
3745        mut cx: AsyncAppContext,
3746    ) -> Result<proto::FindSearchCandidatesResponse> {
3747        let peer_id = envelope.original_sender_id()?;
3748        let message = envelope.payload;
3749        let query = SearchQuery::from_proto(
3750            message
3751                .query
3752                .ok_or_else(|| anyhow!("missing query field"))?,
3753        )?;
3754        let mut results = this.update(&mut cx, |this, cx| {
3755            this.find_search_candidate_buffers(&query, message.limit as _, cx)
3756        })?;
3757
3758        let mut response = proto::FindSearchCandidatesResponse {
3759            buffer_ids: Vec::new(),
3760        };
3761
3762        while let Some(buffer) = results.next().await {
3763            this.update(&mut cx, |this, cx| {
3764                let buffer_id = this.create_buffer_for_peer(&buffer, peer_id, cx);
3765                response.buffer_ids.push(buffer_id.to_proto());
3766            })?;
3767        }
3768
3769        Ok(response)
3770    }
3771
3772    async fn handle_open_buffer_by_id(
3773        this: Model<Self>,
3774        envelope: TypedEnvelope<proto::OpenBufferById>,
3775        mut cx: AsyncAppContext,
3776    ) -> Result<proto::OpenBufferResponse> {
3777        let peer_id = envelope.original_sender_id()?;
3778        let buffer_id = BufferId::new(envelope.payload.id)?;
3779        let buffer = this
3780            .update(&mut cx, |this, cx| this.open_buffer_by_id(buffer_id, cx))?
3781            .await?;
3782        Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
3783    }
3784
3785    async fn handle_open_buffer_by_path(
3786        this: Model<Self>,
3787        envelope: TypedEnvelope<proto::OpenBufferByPath>,
3788        mut cx: AsyncAppContext,
3789    ) -> Result<proto::OpenBufferResponse> {
3790        let peer_id = envelope.original_sender_id()?;
3791        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
3792        let open_buffer = this.update(&mut cx, |this, cx| {
3793            this.open_buffer(
3794                ProjectPath {
3795                    worktree_id,
3796                    path: PathBuf::from(envelope.payload.path).into(),
3797                },
3798                cx,
3799            )
3800        })?;
3801
3802        let buffer = open_buffer.await?;
3803        Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
3804    }
3805
3806    async fn handle_open_new_buffer(
3807        this: Model<Self>,
3808        envelope: TypedEnvelope<proto::OpenNewBuffer>,
3809        mut cx: AsyncAppContext,
3810    ) -> Result<proto::OpenBufferResponse> {
3811        let buffer = this
3812            .update(&mut cx, |this, cx| this.create_buffer(cx))?
3813            .await?;
3814        let peer_id = envelope.original_sender_id()?;
3815
3816        Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
3817    }
3818
3819    fn respond_to_open_buffer_request(
3820        this: Model<Self>,
3821        buffer: Model<Buffer>,
3822        peer_id: proto::PeerId,
3823        cx: &mut AsyncAppContext,
3824    ) -> Result<proto::OpenBufferResponse> {
3825        this.update(cx, |this, cx| {
3826            let is_private = buffer
3827                .read(cx)
3828                .file()
3829                .map(|f| f.is_private())
3830                .unwrap_or_default();
3831            if is_private {
3832                Err(anyhow!(ErrorCode::UnsharedItem))
3833            } else {
3834                Ok(proto::OpenBufferResponse {
3835                    buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx).into(),
3836                })
3837            }
3838        })?
3839    }
3840
3841    fn create_buffer_for_peer(
3842        &mut self,
3843        buffer: &Model<Buffer>,
3844        peer_id: proto::PeerId,
3845        cx: &mut AppContext,
3846    ) -> BufferId {
3847        self.buffer_store
3848            .update(cx, |buffer_store, cx| {
3849                buffer_store.create_buffer_for_peer(buffer, peer_id, cx)
3850            })
3851            .detach_and_log_err(cx);
3852        buffer.read(cx).remote_id()
3853    }
3854
3855    fn wait_for_remote_buffer(
3856        &mut self,
3857        id: BufferId,
3858        cx: &mut ModelContext<Self>,
3859    ) -> Task<Result<Model<Buffer>>> {
3860        self.buffer_store.update(cx, |buffer_store, cx| {
3861            buffer_store.wait_for_remote_buffer(id, cx)
3862        })
3863    }
3864
3865    fn synchronize_remote_buffers(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
3866        let project_id = match self.client_state {
3867            ProjectClientState::Remote {
3868                sharing_has_stopped,
3869                remote_id,
3870                ..
3871            } => {
3872                if sharing_has_stopped {
3873                    return Task::ready(Err(anyhow!(
3874                        "can't synchronize remote buffers on a readonly project"
3875                    )));
3876                } else {
3877                    remote_id
3878                }
3879            }
3880            ProjectClientState::Shared { .. } | ProjectClientState::Local => {
3881                return Task::ready(Err(anyhow!(
3882                    "can't synchronize remote buffers on a local project"
3883                )))
3884            }
3885        };
3886
3887        let client = self.client.clone();
3888        cx.spawn(move |this, mut cx| async move {
3889            let (buffers, incomplete_buffer_ids) = this.update(&mut cx, |this, cx| {
3890                this.buffer_store.read(cx).buffer_version_info(cx)
3891            })?;
3892            let response = client
3893                .request(proto::SynchronizeBuffers {
3894                    project_id,
3895                    buffers,
3896                })
3897                .await?;
3898
3899            let send_updates_for_buffers = this.update(&mut cx, |this, cx| {
3900                response
3901                    .buffers
3902                    .into_iter()
3903                    .map(|buffer| {
3904                        let client = client.clone();
3905                        let buffer_id = match BufferId::new(buffer.id) {
3906                            Ok(id) => id,
3907                            Err(e) => {
3908                                return Task::ready(Err(e));
3909                            }
3910                        };
3911                        let remote_version = language::proto::deserialize_version(&buffer.version);
3912                        if let Some(buffer) = this.buffer_for_id(buffer_id, cx) {
3913                            let operations =
3914                                buffer.read(cx).serialize_ops(Some(remote_version), cx);
3915                            cx.background_executor().spawn(async move {
3916                                let operations = operations.await;
3917                                for chunk in split_operations(operations) {
3918                                    client
3919                                        .request(proto::UpdateBuffer {
3920                                            project_id,
3921                                            buffer_id: buffer_id.into(),
3922                                            operations: chunk,
3923                                        })
3924                                        .await?;
3925                                }
3926                                anyhow::Ok(())
3927                            })
3928                        } else {
3929                            Task::ready(Ok(()))
3930                        }
3931                    })
3932                    .collect::<Vec<_>>()
3933            })?;
3934
3935            // Any incomplete buffers have open requests waiting. Request that the host sends
3936            // creates these buffers for us again to unblock any waiting futures.
3937            for id in incomplete_buffer_ids {
3938                cx.background_executor()
3939                    .spawn(client.request(proto::OpenBufferById {
3940                        project_id,
3941                        id: id.into(),
3942                    }))
3943                    .detach();
3944            }
3945
3946            futures::future::join_all(send_updates_for_buffers)
3947                .await
3948                .into_iter()
3949                .collect()
3950        })
3951    }
3952
3953    pub fn worktree_metadata_protos(&self, cx: &AppContext) -> Vec<proto::WorktreeMetadata> {
3954        self.worktree_store.read(cx).worktree_metadata_protos(cx)
3955    }
3956
3957    fn set_worktrees_from_proto(
3958        &mut self,
3959        worktrees: Vec<proto::WorktreeMetadata>,
3960        cx: &mut ModelContext<Project>,
3961    ) -> Result<()> {
3962        cx.notify();
3963        self.worktree_store.update(cx, |worktree_store, cx| {
3964            worktree_store.set_worktrees_from_proto(worktrees, self.replica_id(), cx)
3965        })
3966    }
3967
3968    fn set_collaborators_from_proto(
3969        &mut self,
3970        messages: Vec<proto::Collaborator>,
3971        cx: &mut ModelContext<Self>,
3972    ) -> Result<()> {
3973        let mut collaborators = HashMap::default();
3974        for message in messages {
3975            let collaborator = Collaborator::from_proto(message)?;
3976            collaborators.insert(collaborator.peer_id, collaborator);
3977        }
3978        for old_peer_id in self.collaborators.keys() {
3979            if !collaborators.contains_key(old_peer_id) {
3980                cx.emit(Event::CollaboratorLeft(*old_peer_id));
3981            }
3982        }
3983        self.collaborators = collaborators;
3984        Ok(())
3985    }
3986
3987    pub fn language_servers<'a>(
3988        &'a self,
3989        cx: &'a AppContext,
3990    ) -> impl 'a + Iterator<Item = (LanguageServerId, LanguageServerName, WorktreeId)> {
3991        self.lsp_store.read(cx).language_servers()
3992    }
3993
3994    pub fn supplementary_language_servers<'a>(
3995        &'a self,
3996        cx: &'a AppContext,
3997    ) -> impl 'a + Iterator<Item = (LanguageServerId, LanguageServerName)> {
3998        self.lsp_store.read(cx).supplementary_language_servers()
3999    }
4000
4001    pub fn language_server_for_id(
4002        &self,
4003        id: LanguageServerId,
4004        cx: &AppContext,
4005    ) -> Option<Arc<LanguageServer>> {
4006        self.lsp_store.read(cx).language_server_for_id(id)
4007    }
4008
4009    pub fn language_servers_for_buffer<'a>(
4010        &'a self,
4011        buffer: &'a Buffer,
4012        cx: &'a AppContext,
4013    ) -> impl Iterator<Item = (&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
4014        self.lsp_store
4015            .read(cx)
4016            .language_servers_for_buffer(buffer, cx)
4017    }
4018}
4019
4020fn deserialize_code_actions(code_actions: &HashMap<String, bool>) -> Vec<lsp::CodeActionKind> {
4021    code_actions
4022        .iter()
4023        .flat_map(|(kind, enabled)| {
4024            if *enabled {
4025                Some(kind.clone().into())
4026            } else {
4027                None
4028            }
4029        })
4030        .collect()
4031}
4032
4033pub struct PathMatchCandidateSet {
4034    pub snapshot: Snapshot,
4035    pub include_ignored: bool,
4036    pub include_root_name: bool,
4037    pub candidates: Candidates,
4038}
4039
4040pub enum Candidates {
4041    /// Only consider directories.
4042    Directories,
4043    /// Only consider files.
4044    Files,
4045    /// Consider directories and files.
4046    Entries,
4047}
4048
4049impl<'a> fuzzy::PathMatchCandidateSet<'a> for PathMatchCandidateSet {
4050    type Candidates = PathMatchCandidateSetIter<'a>;
4051
4052    fn id(&self) -> usize {
4053        self.snapshot.id().to_usize()
4054    }
4055
4056    fn len(&self) -> usize {
4057        match self.candidates {
4058            Candidates::Files => {
4059                if self.include_ignored {
4060                    self.snapshot.file_count()
4061                } else {
4062                    self.snapshot.visible_file_count()
4063                }
4064            }
4065
4066            Candidates::Directories => {
4067                if self.include_ignored {
4068                    self.snapshot.dir_count()
4069                } else {
4070                    self.snapshot.visible_dir_count()
4071                }
4072            }
4073
4074            Candidates::Entries => {
4075                if self.include_ignored {
4076                    self.snapshot.entry_count()
4077                } else {
4078                    self.snapshot.visible_entry_count()
4079                }
4080            }
4081        }
4082    }
4083
4084    fn prefix(&self) -> Arc<str> {
4085        if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
4086            self.snapshot.root_name().into()
4087        } else if self.include_root_name {
4088            format!("{}{}", self.snapshot.root_name(), std::path::MAIN_SEPARATOR).into()
4089        } else {
4090            Arc::default()
4091        }
4092    }
4093
4094    fn candidates(&'a self, start: usize) -> Self::Candidates {
4095        PathMatchCandidateSetIter {
4096            traversal: match self.candidates {
4097                Candidates::Directories => self.snapshot.directories(self.include_ignored, start),
4098                Candidates::Files => self.snapshot.files(self.include_ignored, start),
4099                Candidates::Entries => self.snapshot.entries(self.include_ignored, start),
4100            },
4101        }
4102    }
4103}
4104
4105pub struct PathMatchCandidateSetIter<'a> {
4106    traversal: Traversal<'a>,
4107}
4108
4109impl<'a> Iterator for PathMatchCandidateSetIter<'a> {
4110    type Item = fuzzy::PathMatchCandidate<'a>;
4111
4112    fn next(&mut self) -> Option<Self::Item> {
4113        self.traversal
4114            .next()
4115            .map(|entry| fuzzy::PathMatchCandidate {
4116                is_dir: entry.kind.is_dir(),
4117                path: &entry.path,
4118                char_bag: entry.char_bag,
4119            })
4120    }
4121}
4122
4123impl EventEmitter<Event> for Project {}
4124
4125impl<'a> From<&'a ProjectPath> for SettingsLocation<'a> {
4126    fn from(val: &'a ProjectPath) -> Self {
4127        SettingsLocation {
4128            worktree_id: val.worktree_id,
4129            path: val.path.as_ref(),
4130        }
4131    }
4132}
4133
4134impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
4135    fn from((worktree_id, path): (WorktreeId, P)) -> Self {
4136        Self {
4137            worktree_id,
4138            path: path.as_ref().into(),
4139        }
4140    }
4141}
4142
4143pub fn relativize_path(base: &Path, path: &Path) -> PathBuf {
4144    let mut path_components = path.components();
4145    let mut base_components = base.components();
4146    let mut components: Vec<Component> = Vec::new();
4147    loop {
4148        match (path_components.next(), base_components.next()) {
4149            (None, None) => break,
4150            (Some(a), None) => {
4151                components.push(a);
4152                components.extend(path_components.by_ref());
4153                break;
4154            }
4155            (None, _) => components.push(Component::ParentDir),
4156            (Some(a), Some(b)) if components.is_empty() && a == b => (),
4157            (Some(a), Some(Component::CurDir)) => components.push(a),
4158            (Some(a), Some(_)) => {
4159                components.push(Component::ParentDir);
4160                for _ in base_components {
4161                    components.push(Component::ParentDir);
4162                }
4163                components.push(a);
4164                components.extend(path_components.by_ref());
4165                break;
4166            }
4167        }
4168    }
4169    components.iter().map(|c| c.as_os_str()).collect()
4170}
4171
4172fn resolve_path(base: &Path, path: &Path) -> PathBuf {
4173    let mut result = base.to_path_buf();
4174    for component in path.components() {
4175        match component {
4176            Component::ParentDir => {
4177                result.pop();
4178            }
4179            Component::CurDir => (),
4180            _ => result.push(component),
4181        }
4182    }
4183    result
4184}
4185
4186/// ResolvedPath is a path that has been resolved to either a ProjectPath
4187/// or an AbsPath and that *exists*.
4188#[derive(Debug, Clone)]
4189pub enum ResolvedPath {
4190    ProjectPath {
4191        project_path: ProjectPath,
4192        is_dir: bool,
4193    },
4194    AbsPath {
4195        path: PathBuf,
4196        is_dir: bool,
4197    },
4198}
4199
4200impl ResolvedPath {
4201    pub fn abs_path(&self) -> Option<&Path> {
4202        match self {
4203            Self::AbsPath { path, .. } => Some(path.as_path()),
4204            _ => None,
4205        }
4206    }
4207
4208    pub fn project_path(&self) -> Option<&ProjectPath> {
4209        match self {
4210            Self::ProjectPath { project_path, .. } => Some(&project_path),
4211            _ => None,
4212        }
4213    }
4214
4215    pub fn is_file(&self) -> bool {
4216        !self.is_dir()
4217    }
4218
4219    pub fn is_dir(&self) -> bool {
4220        match self {
4221            Self::ProjectPath { is_dir, .. } => *is_dir,
4222            Self::AbsPath { is_dir, .. } => *is_dir,
4223        }
4224    }
4225}
4226
4227impl Item for Buffer {
4228    fn try_open(
4229        project: &Model<Project>,
4230        path: &ProjectPath,
4231        cx: &mut AppContext,
4232    ) -> Option<Task<Result<Model<Self>>>> {
4233        Some(project.update(cx, |project, cx| project.open_buffer(path.clone(), cx)))
4234    }
4235
4236    fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
4237        File::from_dyn(self.file()).and_then(|file| file.project_entry_id(cx))
4238    }
4239
4240    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
4241        File::from_dyn(self.file()).map(|file| ProjectPath {
4242            worktree_id: file.worktree_id(cx),
4243            path: file.path().clone(),
4244        })
4245    }
4246}
4247
4248impl Completion {
4249    /// A key that can be used to sort completions when displaying
4250    /// them to the user.
4251    pub fn sort_key(&self) -> (usize, &str) {
4252        let kind_key = match self.lsp_completion.kind {
4253            Some(lsp::CompletionItemKind::KEYWORD) => 0,
4254            Some(lsp::CompletionItemKind::VARIABLE) => 1,
4255            _ => 2,
4256        };
4257        (kind_key, &self.label.text[self.label.filter_range.clone()])
4258    }
4259
4260    /// Whether this completion is a snippet.
4261    pub fn is_snippet(&self) -> bool {
4262        self.lsp_completion.insert_text_format == Some(lsp::InsertTextFormat::SNIPPET)
4263    }
4264
4265    /// Returns the corresponding color for this completion.
4266    ///
4267    /// Will return `None` if this completion's kind is not [`CompletionItemKind::COLOR`].
4268    pub fn color(&self) -> Option<Hsla> {
4269        match self.lsp_completion.kind {
4270            Some(CompletionItemKind::COLOR) => color_extractor::extract_color(&self.lsp_completion),
4271            _ => None,
4272        }
4273    }
4274}
4275
4276pub fn sort_worktree_entries(entries: &mut [Entry]) {
4277    entries.sort_by(|entry_a, entry_b| {
4278        compare_paths(
4279            (&entry_a.path, entry_a.is_file()),
4280            (&entry_b.path, entry_b.is_file()),
4281        )
4282    });
4283}
4284
4285fn proto_to_prompt(level: proto::language_server_prompt_request::Level) -> gpui::PromptLevel {
4286    match level {
4287        proto::language_server_prompt_request::Level::Info(_) => gpui::PromptLevel::Info,
4288        proto::language_server_prompt_request::Level::Warning(_) => gpui::PromptLevel::Warning,
4289        proto::language_server_prompt_request::Level::Critical(_) => gpui::PromptLevel::Critical,
4290    }
4291}