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