worktree.rs

   1use crate::{copy_recursive, ProjectEntryId, RemoveOptions};
   2
   3use super::{
   4    fs::{self, Fs},
   5    ignore::IgnoreStack,
   6    DiagnosticSummary,
   7};
   8use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
   9use anyhow::{anyhow, Context, Result};
  10use client::{proto, Client};
  11use clock::ReplicaId;
  12use collections::{HashMap, VecDeque};
  13use futures::{
  14    channel::{
  15        mpsc::{self, UnboundedSender},
  16        oneshot,
  17    },
  18    Stream, StreamExt,
  19};
  20use fuzzy::CharBag;
  21use gpui::{
  22    executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext,
  23    Task,
  24};
  25use language::{
  26    proto::{deserialize_version, serialize_line_ending, serialize_version},
  27    Buffer, DiagnosticEntry, LineEnding, PointUtf16, Rope,
  28};
  29use lazy_static::lazy_static;
  30use parking_lot::Mutex;
  31use postage::{
  32    prelude::{Sink as _, Stream as _},
  33    watch,
  34};
  35use settings::Settings;
  36use smol::channel::{self, Sender};
  37use std::{
  38    any::Any,
  39    cmp::{self, Ordering},
  40    convert::TryFrom,
  41    ffi::{OsStr, OsString},
  42    fmt,
  43    future::Future,
  44    ops::{Deref, DerefMut},
  45    os::unix::prelude::{OsStrExt, OsStringExt},
  46    path::{Path, PathBuf},
  47    sync::{atomic::AtomicUsize, Arc},
  48    task::Poll,
  49    time::{Duration, SystemTime},
  50};
  51use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet};
  52use util::{ResultExt, TryFutureExt};
  53
  54lazy_static! {
  55    static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore");
  56}
  57
  58#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
  59pub struct WorktreeId(usize);
  60
  61#[allow(clippy::large_enum_variant)]
  62pub enum Worktree {
  63    Local(LocalWorktree),
  64    Remote(RemoteWorktree),
  65}
  66
  67pub struct LocalWorktree {
  68    snapshot: LocalSnapshot,
  69    background_snapshot: Arc<Mutex<LocalSnapshot>>,
  70    last_scan_state_rx: watch::Receiver<ScanState>,
  71    _background_scanner_task: Option<Task<()>>,
  72    poll_task: Option<Task<()>>,
  73    share: Option<ShareState>,
  74    diagnostics: HashMap<Arc<Path>, Vec<DiagnosticEntry<PointUtf16>>>,
  75    diagnostic_summaries: TreeMap<PathKey, DiagnosticSummary>,
  76    client: Arc<Client>,
  77    fs: Arc<dyn Fs>,
  78    visible: bool,
  79}
  80
  81pub struct RemoteWorktree {
  82    pub snapshot: Snapshot,
  83    pub(crate) background_snapshot: Arc<Mutex<Snapshot>>,
  84    project_id: u64,
  85    client: Arc<Client>,
  86    updates_tx: Option<UnboundedSender<proto::UpdateWorktree>>,
  87    snapshot_subscriptions: VecDeque<(usize, oneshot::Sender<()>)>,
  88    replica_id: ReplicaId,
  89    diagnostic_summaries: TreeMap<PathKey, DiagnosticSummary>,
  90    visible: bool,
  91}
  92
  93#[derive(Clone)]
  94pub struct Snapshot {
  95    id: WorktreeId,
  96    root_name: String,
  97    root_char_bag: CharBag,
  98    entries_by_path: SumTree<Entry>,
  99    entries_by_id: SumTree<PathEntry>,
 100    scan_id: usize,
 101    is_complete: bool,
 102}
 103
 104#[derive(Clone)]
 105pub struct LocalSnapshot {
 106    abs_path: Arc<Path>,
 107    ignores_by_parent_abs_path: HashMap<Arc<Path>, (Arc<Gitignore>, usize)>,
 108    removed_entry_ids: HashMap<u64, ProjectEntryId>,
 109    next_entry_id: Arc<AtomicUsize>,
 110    snapshot: Snapshot,
 111    extension_counts: HashMap<OsString, usize>,
 112}
 113
 114impl Deref for LocalSnapshot {
 115    type Target = Snapshot;
 116
 117    fn deref(&self) -> &Self::Target {
 118        &self.snapshot
 119    }
 120}
 121
 122impl DerefMut for LocalSnapshot {
 123    fn deref_mut(&mut self) -> &mut Self::Target {
 124        &mut self.snapshot
 125    }
 126}
 127
 128#[derive(Clone, Debug)]
 129enum ScanState {
 130    Idle,
 131    /// The worktree is performing its initial scan of the filesystem.
 132    Initializing,
 133    /// The worktree is updating in response to filesystem events.
 134    Updating,
 135    Err(Arc<anyhow::Error>),
 136}
 137
 138struct ShareState {
 139    project_id: u64,
 140    snapshots_tx: watch::Sender<LocalSnapshot>,
 141    _maintain_remote_snapshot: Option<Task<Option<()>>>,
 142}
 143
 144pub enum Event {
 145    UpdatedEntries,
 146}
 147
 148impl Entity for Worktree {
 149    type Event = Event;
 150}
 151
 152impl Worktree {
 153    pub async fn local(
 154        client: Arc<Client>,
 155        path: impl Into<Arc<Path>>,
 156        visible: bool,
 157        fs: Arc<dyn Fs>,
 158        next_entry_id: Arc<AtomicUsize>,
 159        cx: &mut AsyncAppContext,
 160    ) -> Result<ModelHandle<Self>> {
 161        let (tree, scan_states_tx) =
 162            LocalWorktree::create(client, path, visible, fs.clone(), next_entry_id, cx).await?;
 163        tree.update(cx, |tree, cx| {
 164            let tree = tree.as_local_mut().unwrap();
 165            let abs_path = tree.abs_path().clone();
 166            let background_snapshot = tree.background_snapshot.clone();
 167            let background = cx.background().clone();
 168            tree._background_scanner_task = Some(cx.background().spawn(async move {
 169                let events = fs.watch(&abs_path, Duration::from_millis(100)).await;
 170                let scanner =
 171                    BackgroundScanner::new(background_snapshot, scan_states_tx, fs, background);
 172                scanner.run(events).await;
 173            }));
 174        });
 175        Ok(tree)
 176    }
 177
 178    pub fn remote(
 179        project_remote_id: u64,
 180        replica_id: ReplicaId,
 181        worktree: proto::WorktreeMetadata,
 182        client: Arc<Client>,
 183        cx: &mut MutableAppContext,
 184    ) -> ModelHandle<Self> {
 185        let remote_id = worktree.id;
 186        let root_char_bag: CharBag = worktree
 187            .root_name
 188            .chars()
 189            .map(|c| c.to_ascii_lowercase())
 190            .collect();
 191        let root_name = worktree.root_name.clone();
 192        let visible = worktree.visible;
 193        let snapshot = Snapshot {
 194            id: WorktreeId(remote_id as usize),
 195            root_name,
 196            root_char_bag,
 197            entries_by_path: Default::default(),
 198            entries_by_id: Default::default(),
 199            scan_id: 0,
 200            is_complete: false,
 201        };
 202
 203        let (updates_tx, mut updates_rx) = mpsc::unbounded();
 204        let background_snapshot = Arc::new(Mutex::new(snapshot.clone()));
 205        let (mut snapshot_updated_tx, mut snapshot_updated_rx) = watch::channel();
 206        let worktree_handle = cx.add_model(|_: &mut ModelContext<Worktree>| {
 207            Worktree::Remote(RemoteWorktree {
 208                project_id: project_remote_id,
 209                replica_id,
 210                snapshot: snapshot.clone(),
 211                background_snapshot: background_snapshot.clone(),
 212                updates_tx: Some(updates_tx),
 213                snapshot_subscriptions: Default::default(),
 214                client: client.clone(),
 215                diagnostic_summaries: Default::default(),
 216                visible,
 217            })
 218        });
 219
 220        cx.background()
 221            .spawn(async move {
 222                while let Some(update) = updates_rx.next().await {
 223                    if let Err(error) = background_snapshot.lock().apply_remote_update(update) {
 224                        log::error!("error applying worktree update: {}", error);
 225                    }
 226                    snapshot_updated_tx.send(()).await.ok();
 227                }
 228            })
 229            .detach();
 230
 231        cx.spawn(|mut cx| {
 232            let this = worktree_handle.downgrade();
 233            async move {
 234                while (snapshot_updated_rx.recv().await).is_some() {
 235                    if let Some(this) = this.upgrade(&cx) {
 236                        this.update(&mut cx, |this, cx| {
 237                            this.poll_snapshot(cx);
 238                            let this = this.as_remote_mut().unwrap();
 239                            while let Some((scan_id, _)) = this.snapshot_subscriptions.front() {
 240                                if this.observed_snapshot(*scan_id) {
 241                                    let (_, tx) = this.snapshot_subscriptions.pop_front().unwrap();
 242                                    let _ = tx.send(());
 243                                } else {
 244                                    break;
 245                                }
 246                            }
 247                        });
 248                    } else {
 249                        break;
 250                    }
 251                }
 252            }
 253        })
 254        .detach();
 255
 256        worktree_handle
 257    }
 258
 259    pub fn as_local(&self) -> Option<&LocalWorktree> {
 260        if let Worktree::Local(worktree) = self {
 261            Some(worktree)
 262        } else {
 263            None
 264        }
 265    }
 266
 267    pub fn as_remote(&self) -> Option<&RemoteWorktree> {
 268        if let Worktree::Remote(worktree) = self {
 269            Some(worktree)
 270        } else {
 271            None
 272        }
 273    }
 274
 275    pub fn as_local_mut(&mut self) -> Option<&mut LocalWorktree> {
 276        if let Worktree::Local(worktree) = self {
 277            Some(worktree)
 278        } else {
 279            None
 280        }
 281    }
 282
 283    pub fn as_remote_mut(&mut self) -> Option<&mut RemoteWorktree> {
 284        if let Worktree::Remote(worktree) = self {
 285            Some(worktree)
 286        } else {
 287            None
 288        }
 289    }
 290
 291    pub fn is_local(&self) -> bool {
 292        matches!(self, Worktree::Local(_))
 293    }
 294
 295    pub fn is_remote(&self) -> bool {
 296        !self.is_local()
 297    }
 298
 299    pub fn snapshot(&self) -> Snapshot {
 300        match self {
 301            Worktree::Local(worktree) => worktree.snapshot().snapshot,
 302            Worktree::Remote(worktree) => worktree.snapshot(),
 303        }
 304    }
 305
 306    pub fn scan_id(&self) -> usize {
 307        match self {
 308            Worktree::Local(worktree) => worktree.snapshot.scan_id,
 309            Worktree::Remote(worktree) => worktree.snapshot.scan_id,
 310        }
 311    }
 312
 313    pub fn is_visible(&self) -> bool {
 314        match self {
 315            Worktree::Local(worktree) => worktree.visible,
 316            Worktree::Remote(worktree) => worktree.visible,
 317        }
 318    }
 319
 320    pub fn replica_id(&self) -> ReplicaId {
 321        match self {
 322            Worktree::Local(_) => 0,
 323            Worktree::Remote(worktree) => worktree.replica_id,
 324        }
 325    }
 326
 327    pub fn diagnostic_summaries(
 328        &self,
 329    ) -> impl Iterator<Item = (Arc<Path>, DiagnosticSummary)> + '_ {
 330        match self {
 331            Worktree::Local(worktree) => &worktree.diagnostic_summaries,
 332            Worktree::Remote(worktree) => &worktree.diagnostic_summaries,
 333        }
 334        .iter()
 335        .map(|(path, summary)| (path.0.clone(), *summary))
 336    }
 337
 338    fn poll_snapshot(&mut self, cx: &mut ModelContext<Self>) {
 339        match self {
 340            Self::Local(worktree) => worktree.poll_snapshot(false, cx),
 341            Self::Remote(worktree) => worktree.poll_snapshot(cx),
 342        };
 343    }
 344}
 345
 346impl LocalWorktree {
 347    async fn create(
 348        client: Arc<Client>,
 349        path: impl Into<Arc<Path>>,
 350        visible: bool,
 351        fs: Arc<dyn Fs>,
 352        next_entry_id: Arc<AtomicUsize>,
 353        cx: &mut AsyncAppContext,
 354    ) -> Result<(ModelHandle<Worktree>, UnboundedSender<ScanState>)> {
 355        let abs_path = path.into();
 356        let path: Arc<Path> = Arc::from(Path::new(""));
 357
 358        // After determining whether the root entry is a file or a directory, populate the
 359        // snapshot's "root name", which will be used for the purpose of fuzzy matching.
 360        let root_name = abs_path
 361            .file_name()
 362            .map_or(String::new(), |f| f.to_string_lossy().to_string());
 363        let root_char_bag = root_name.chars().map(|c| c.to_ascii_lowercase()).collect();
 364        let metadata = fs
 365            .metadata(&abs_path)
 366            .await
 367            .context("failed to stat worktree path")?;
 368
 369        let (scan_states_tx, mut scan_states_rx) = mpsc::unbounded();
 370        let (mut last_scan_state_tx, last_scan_state_rx) =
 371            watch::channel_with(ScanState::Initializing);
 372        let tree = cx.add_model(move |cx: &mut ModelContext<Worktree>| {
 373            let mut snapshot = LocalSnapshot {
 374                abs_path,
 375                ignores_by_parent_abs_path: Default::default(),
 376                removed_entry_ids: Default::default(),
 377                next_entry_id,
 378                snapshot: Snapshot {
 379                    id: WorktreeId::from_usize(cx.model_id()),
 380                    root_name: root_name.clone(),
 381                    root_char_bag,
 382                    entries_by_path: Default::default(),
 383                    entries_by_id: Default::default(),
 384                    scan_id: 0,
 385                    is_complete: true,
 386                },
 387                extension_counts: Default::default(),
 388            };
 389            if let Some(metadata) = metadata {
 390                let entry = Entry::new(
 391                    path,
 392                    &metadata,
 393                    &snapshot.next_entry_id,
 394                    snapshot.root_char_bag,
 395                );
 396                snapshot.insert_entry(entry, fs.as_ref());
 397            }
 398
 399            let tree = Self {
 400                snapshot: snapshot.clone(),
 401                background_snapshot: Arc::new(Mutex::new(snapshot)),
 402                last_scan_state_rx,
 403                _background_scanner_task: None,
 404                share: None,
 405                poll_task: None,
 406                diagnostics: Default::default(),
 407                diagnostic_summaries: Default::default(),
 408                client,
 409                fs,
 410                visible,
 411            };
 412
 413            cx.spawn_weak(|this, mut cx| async move {
 414                while let Some(scan_state) = scan_states_rx.next().await {
 415                    if let Some(this) = this.upgrade(&cx) {
 416                        last_scan_state_tx.blocking_send(scan_state).ok();
 417                        this.update(&mut cx, |this, cx| this.poll_snapshot(cx));
 418                    } else {
 419                        break;
 420                    }
 421                }
 422            })
 423            .detach();
 424
 425            Worktree::Local(tree)
 426        });
 427
 428        Ok((tree, scan_states_tx))
 429    }
 430
 431    pub fn contains_abs_path(&self, path: &Path) -> bool {
 432        path.starts_with(&self.abs_path)
 433    }
 434
 435    fn absolutize(&self, path: &Path) -> PathBuf {
 436        if path.file_name().is_some() {
 437            self.abs_path.join(path)
 438        } else {
 439            self.abs_path.to_path_buf()
 440        }
 441    }
 442
 443    pub(crate) fn load_buffer(
 444        &mut self,
 445        path: &Path,
 446        cx: &mut ModelContext<Worktree>,
 447    ) -> Task<Result<ModelHandle<Buffer>>> {
 448        let path = Arc::from(path);
 449        cx.spawn(move |this, mut cx| async move {
 450            let (file, contents, head_text) = this
 451                .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx))
 452                .await?;
 453            Ok(cx.add_model(|cx| {
 454                let mut buffer = Buffer::from_file(0, contents, head_text, Arc::new(file), cx);
 455                buffer.update_git(cx);
 456                buffer
 457            }))
 458        })
 459    }
 460
 461    pub fn diagnostics_for_path(&self, path: &Path) -> Option<Vec<DiagnosticEntry<PointUtf16>>> {
 462        self.diagnostics.get(path).cloned()
 463    }
 464
 465    pub fn update_diagnostics(
 466        &mut self,
 467        language_server_id: usize,
 468        worktree_path: Arc<Path>,
 469        diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
 470        _: &mut ModelContext<Worktree>,
 471    ) -> Result<bool> {
 472        self.diagnostics.remove(&worktree_path);
 473        let old_summary = self
 474            .diagnostic_summaries
 475            .remove(&PathKey(worktree_path.clone()))
 476            .unwrap_or_default();
 477        let new_summary = DiagnosticSummary::new(language_server_id, &diagnostics);
 478        if !new_summary.is_empty() {
 479            self.diagnostic_summaries
 480                .insert(PathKey(worktree_path.clone()), new_summary);
 481            self.diagnostics.insert(worktree_path.clone(), diagnostics);
 482        }
 483
 484        let updated = !old_summary.is_empty() || !new_summary.is_empty();
 485        if updated {
 486            if let Some(share) = self.share.as_ref() {
 487                self.client
 488                    .send(proto::UpdateDiagnosticSummary {
 489                        project_id: share.project_id,
 490                        worktree_id: self.id().to_proto(),
 491                        summary: Some(proto::DiagnosticSummary {
 492                            path: worktree_path.to_string_lossy().to_string(),
 493                            language_server_id: language_server_id as u64,
 494                            error_count: new_summary.error_count as u32,
 495                            warning_count: new_summary.warning_count as u32,
 496                        }),
 497                    })
 498                    .log_err();
 499            }
 500        }
 501
 502        Ok(updated)
 503    }
 504
 505    fn poll_snapshot(&mut self, force: bool, cx: &mut ModelContext<Worktree>) {
 506        self.poll_task.take();
 507        match self.scan_state() {
 508            ScanState::Idle => {
 509                self.snapshot = self.background_snapshot.lock().clone();
 510                if let Some(share) = self.share.as_mut() {
 511                    *share.snapshots_tx.borrow_mut() = self.snapshot.clone();
 512                }
 513                cx.emit(Event::UpdatedEntries);
 514            }
 515            ScanState::Initializing => {
 516                let is_fake_fs = self.fs.is_fake();
 517                self.snapshot = self.background_snapshot.lock().clone();
 518                self.poll_task = Some(cx.spawn_weak(|this, mut cx| async move {
 519                    if is_fake_fs {
 520                        #[cfg(any(test, feature = "test-support"))]
 521                        cx.background().simulate_random_delay().await;
 522                    } else {
 523                        smol::Timer::after(Duration::from_millis(100)).await;
 524                    }
 525                    if let Some(this) = this.upgrade(&cx) {
 526                        this.update(&mut cx, |this, cx| this.poll_snapshot(cx));
 527                    }
 528                }));
 529                cx.emit(Event::UpdatedEntries);
 530            }
 531            _ => {
 532                if force {
 533                    self.snapshot = self.background_snapshot.lock().clone();
 534                }
 535            }
 536        }
 537        cx.notify();
 538    }
 539
 540    pub fn scan_complete(&self) -> impl Future<Output = ()> {
 541        let mut scan_state_rx = self.last_scan_state_rx.clone();
 542        async move {
 543            let mut scan_state = Some(scan_state_rx.borrow().clone());
 544            while let Some(ScanState::Initializing | ScanState::Updating) = scan_state {
 545                scan_state = scan_state_rx.recv().await;
 546            }
 547        }
 548    }
 549
 550    fn scan_state(&self) -> ScanState {
 551        self.last_scan_state_rx.borrow().clone()
 552    }
 553
 554    pub fn snapshot(&self) -> LocalSnapshot {
 555        self.snapshot.clone()
 556    }
 557
 558    pub fn metadata_proto(&self) -> proto::WorktreeMetadata {
 559        proto::WorktreeMetadata {
 560            id: self.id().to_proto(),
 561            root_name: self.root_name().to_string(),
 562            visible: self.visible,
 563        }
 564    }
 565
 566    fn load(
 567        &self,
 568        path: &Path,
 569        cx: &mut ModelContext<Worktree>,
 570    ) -> Task<Result<(File, String, Option<String>)>> {
 571        let handle = cx.handle();
 572        let path = Arc::from(path);
 573        let abs_path = self.absolutize(&path);
 574        let fs = self.fs.clone();
 575
 576        let files_included = cx
 577            .global::<Settings>()
 578            .editor_overrides
 579            .git_gutter
 580            .unwrap_or_default()
 581            .files_included;
 582
 583        cx.spawn(|this, mut cx| async move {
 584            let text = fs.load(&abs_path).await?;
 585
 586            let head_text = if matches!(
 587                files_included,
 588                settings::GitFilesIncluded::All | settings::GitFilesIncluded::OnlyTracked
 589            ) {
 590                let fs = fs.clone();
 591                let abs_path = abs_path.clone();
 592                let task = async move { fs.load_head_text(&abs_path).await };
 593                let results = cx.background().spawn(task).await;
 594
 595                if files_included == settings::GitFilesIncluded::All {
 596                    results.or_else(|| Some(text.clone()))
 597                } else {
 598                    results
 599                }
 600            } else {
 601                None
 602            };
 603
 604            // Eagerly populate the snapshot with an updated entry for the loaded file
 605            let entry = this
 606                .update(&mut cx, |this, cx| {
 607                    this.as_local()
 608                        .unwrap()
 609                        .refresh_entry(path, abs_path, None, cx)
 610                })
 611                .await?;
 612
 613            Ok((
 614                File {
 615                    entry_id: Some(entry.id),
 616                    worktree: handle,
 617                    path: entry.path,
 618                    mtime: entry.mtime,
 619                    is_local: true,
 620                },
 621                text,
 622                head_text,
 623            ))
 624        })
 625    }
 626
 627    pub fn save_buffer_as(
 628        &self,
 629        buffer_handle: ModelHandle<Buffer>,
 630        path: impl Into<Arc<Path>>,
 631        cx: &mut ModelContext<Worktree>,
 632    ) -> Task<Result<()>> {
 633        let buffer = buffer_handle.read(cx);
 634        let text = buffer.as_rope().clone();
 635        let fingerprint = text.fingerprint();
 636        let version = buffer.version();
 637        let save = self.write_file(path, text, buffer.line_ending(), cx);
 638        let handle = cx.handle();
 639        cx.as_mut().spawn(|mut cx| async move {
 640            let entry = save.await?;
 641            let file = File {
 642                entry_id: Some(entry.id),
 643                worktree: handle,
 644                path: entry.path,
 645                mtime: entry.mtime,
 646                is_local: true,
 647            };
 648
 649            buffer_handle.update(&mut cx, |buffer, cx| {
 650                buffer.did_save(version, fingerprint, file.mtime, Some(Arc::new(file)), cx);
 651            });
 652
 653            Ok(())
 654        })
 655    }
 656
 657    pub fn create_entry(
 658        &self,
 659        path: impl Into<Arc<Path>>,
 660        is_dir: bool,
 661        cx: &mut ModelContext<Worktree>,
 662    ) -> Task<Result<Entry>> {
 663        self.write_entry_internal(
 664            path,
 665            if is_dir {
 666                None
 667            } else {
 668                Some(Default::default())
 669            },
 670            cx,
 671        )
 672    }
 673
 674    pub fn write_file(
 675        &self,
 676        path: impl Into<Arc<Path>>,
 677        text: Rope,
 678        line_ending: LineEnding,
 679        cx: &mut ModelContext<Worktree>,
 680    ) -> Task<Result<Entry>> {
 681        self.write_entry_internal(path, Some((text, line_ending)), cx)
 682    }
 683
 684    pub fn delete_entry(
 685        &self,
 686        entry_id: ProjectEntryId,
 687        cx: &mut ModelContext<Worktree>,
 688    ) -> Option<Task<Result<()>>> {
 689        let entry = self.entry_for_id(entry_id)?.clone();
 690        let abs_path = self.absolutize(&entry.path);
 691        let delete = cx.background().spawn({
 692            let fs = self.fs.clone();
 693            let abs_path = abs_path;
 694            async move {
 695                if entry.is_file() {
 696                    fs.remove_file(&abs_path, Default::default()).await
 697                } else {
 698                    fs.remove_dir(
 699                        &abs_path,
 700                        RemoveOptions {
 701                            recursive: true,
 702                            ignore_if_not_exists: false,
 703                        },
 704                    )
 705                    .await
 706                }
 707            }
 708        });
 709
 710        Some(cx.spawn(|this, mut cx| async move {
 711            delete.await?;
 712            this.update(&mut cx, |this, cx| {
 713                let this = this.as_local_mut().unwrap();
 714                {
 715                    let mut snapshot = this.background_snapshot.lock();
 716                    snapshot.delete_entry(entry_id);
 717                }
 718                this.poll_snapshot(true, cx);
 719            });
 720            Ok(())
 721        }))
 722    }
 723
 724    pub fn rename_entry(
 725        &self,
 726        entry_id: ProjectEntryId,
 727        new_path: impl Into<Arc<Path>>,
 728        cx: &mut ModelContext<Worktree>,
 729    ) -> Option<Task<Result<Entry>>> {
 730        let old_path = self.entry_for_id(entry_id)?.path.clone();
 731        let new_path = new_path.into();
 732        let abs_old_path = self.absolutize(&old_path);
 733        let abs_new_path = self.absolutize(&new_path);
 734        let rename = cx.background().spawn({
 735            let fs = self.fs.clone();
 736            let abs_new_path = abs_new_path.clone();
 737            async move {
 738                fs.rename(&abs_old_path, &abs_new_path, Default::default())
 739                    .await
 740            }
 741        });
 742
 743        Some(cx.spawn(|this, mut cx| async move {
 744            rename.await?;
 745            let entry = this
 746                .update(&mut cx, |this, cx| {
 747                    this.as_local_mut().unwrap().refresh_entry(
 748                        new_path.clone(),
 749                        abs_new_path,
 750                        Some(old_path),
 751                        cx,
 752                    )
 753                })
 754                .await?;
 755            Ok(entry)
 756        }))
 757    }
 758
 759    pub fn copy_entry(
 760        &self,
 761        entry_id: ProjectEntryId,
 762        new_path: impl Into<Arc<Path>>,
 763        cx: &mut ModelContext<Worktree>,
 764    ) -> Option<Task<Result<Entry>>> {
 765        let old_path = self.entry_for_id(entry_id)?.path.clone();
 766        let new_path = new_path.into();
 767        let abs_old_path = self.absolutize(&old_path);
 768        let abs_new_path = self.absolutize(&new_path);
 769        let copy = cx.background().spawn({
 770            let fs = self.fs.clone();
 771            let abs_new_path = abs_new_path.clone();
 772            async move {
 773                copy_recursive(
 774                    fs.as_ref(),
 775                    &abs_old_path,
 776                    &abs_new_path,
 777                    Default::default(),
 778                )
 779                .await
 780            }
 781        });
 782
 783        Some(cx.spawn(|this, mut cx| async move {
 784            copy.await?;
 785            let entry = this
 786                .update(&mut cx, |this, cx| {
 787                    this.as_local_mut().unwrap().refresh_entry(
 788                        new_path.clone(),
 789                        abs_new_path,
 790                        None,
 791                        cx,
 792                    )
 793                })
 794                .await?;
 795            Ok(entry)
 796        }))
 797    }
 798
 799    fn write_entry_internal(
 800        &self,
 801        path: impl Into<Arc<Path>>,
 802        text_if_file: Option<(Rope, LineEnding)>,
 803        cx: &mut ModelContext<Worktree>,
 804    ) -> Task<Result<Entry>> {
 805        let path = path.into();
 806        let abs_path = self.absolutize(&path);
 807        let write = cx.background().spawn({
 808            let fs = self.fs.clone();
 809            let abs_path = abs_path.clone();
 810            async move {
 811                if let Some((text, line_ending)) = text_if_file {
 812                    fs.save(&abs_path, &text, line_ending).await
 813                } else {
 814                    fs.create_dir(&abs_path).await
 815                }
 816            }
 817        });
 818
 819        cx.spawn(|this, mut cx| async move {
 820            write.await?;
 821            let entry = this
 822                .update(&mut cx, |this, cx| {
 823                    this.as_local_mut()
 824                        .unwrap()
 825                        .refresh_entry(path, abs_path, None, cx)
 826                })
 827                .await?;
 828            Ok(entry)
 829        })
 830    }
 831
 832    fn refresh_entry(
 833        &self,
 834        path: Arc<Path>,
 835        abs_path: PathBuf,
 836        old_path: Option<Arc<Path>>,
 837        cx: &mut ModelContext<Worktree>,
 838    ) -> Task<Result<Entry>> {
 839        let fs = self.fs.clone();
 840        let root_char_bag;
 841        let next_entry_id;
 842        {
 843            let snapshot = self.background_snapshot.lock();
 844            root_char_bag = snapshot.root_char_bag;
 845            next_entry_id = snapshot.next_entry_id.clone();
 846        }
 847        cx.spawn_weak(|this, mut cx| async move {
 848            let metadata = fs
 849                .metadata(&abs_path)
 850                .await?
 851                .ok_or_else(|| anyhow!("could not read saved file metadata"))?;
 852            let this = this
 853                .upgrade(&cx)
 854                .ok_or_else(|| anyhow!("worktree was dropped"))?;
 855            this.update(&mut cx, |this, cx| {
 856                let this = this.as_local_mut().unwrap();
 857                let inserted_entry;
 858                {
 859                    let mut snapshot = this.background_snapshot.lock();
 860                    let mut entry = Entry::new(path, &metadata, &next_entry_id, root_char_bag);
 861                    entry.is_ignored = snapshot
 862                        .ignore_stack_for_abs_path(&abs_path, entry.is_dir())
 863                        .is_abs_path_ignored(&abs_path, entry.is_dir());
 864                    if let Some(old_path) = old_path {
 865                        snapshot.remove_path(&old_path);
 866                    }
 867                    inserted_entry = snapshot.insert_entry(entry, fs.as_ref());
 868                    snapshot.scan_id += 1;
 869                }
 870                this.poll_snapshot(true, cx);
 871                Ok(inserted_entry)
 872            })
 873        })
 874    }
 875
 876    pub fn share(&mut self, project_id: u64, cx: &mut ModelContext<Worktree>) -> Task<Result<()>> {
 877        let (share_tx, share_rx) = oneshot::channel();
 878
 879        if self.share.is_some() {
 880            let _ = share_tx.send(Ok(()));
 881        } else {
 882            let (snapshots_tx, mut snapshots_rx) = watch::channel_with(self.snapshot());
 883            let rpc = self.client.clone();
 884            let worktree_id = cx.model_id() as u64;
 885            let maintain_remote_snapshot = cx.background().spawn({
 886                let rpc = rpc;
 887                let diagnostic_summaries = self.diagnostic_summaries.clone();
 888                async move {
 889                    let mut prev_snapshot = match snapshots_rx.recv().await {
 890                        Some(snapshot) => {
 891                            let update = proto::UpdateWorktree {
 892                                project_id,
 893                                worktree_id,
 894                                root_name: snapshot.root_name().to_string(),
 895                                updated_entries: snapshot
 896                                    .entries_by_path
 897                                    .iter()
 898                                    .map(Into::into)
 899                                    .collect(),
 900                                removed_entries: Default::default(),
 901                                scan_id: snapshot.scan_id as u64,
 902                                is_last_update: true,
 903                            };
 904                            if let Err(error) = send_worktree_update(&rpc, update).await {
 905                                let _ = share_tx.send(Err(error));
 906                                return Err(anyhow!("failed to send initial update worktree"));
 907                            } else {
 908                                let _ = share_tx.send(Ok(()));
 909                                snapshot
 910                            }
 911                        }
 912                        None => {
 913                            share_tx
 914                                .send(Err(anyhow!("worktree dropped before share completed")))
 915                                .ok();
 916                            return Err(anyhow!("failed to send initial update worktree"));
 917                        }
 918                    };
 919
 920                    for (path, summary) in diagnostic_summaries.iter() {
 921                        rpc.send(proto::UpdateDiagnosticSummary {
 922                            project_id,
 923                            worktree_id,
 924                            summary: Some(summary.to_proto(&path.0)),
 925                        })?;
 926                    }
 927
 928                    while let Some(snapshot) = snapshots_rx.recv().await {
 929                        send_worktree_update(
 930                            &rpc,
 931                            snapshot.build_update(&prev_snapshot, project_id, worktree_id, true),
 932                        )
 933                        .await?;
 934                        prev_snapshot = snapshot;
 935                    }
 936
 937                    Ok::<_, anyhow::Error>(())
 938                }
 939                .log_err()
 940            });
 941            self.share = Some(ShareState {
 942                project_id,
 943                snapshots_tx,
 944                _maintain_remote_snapshot: Some(maintain_remote_snapshot),
 945            });
 946        }
 947
 948        cx.foreground().spawn(async move {
 949            share_rx
 950                .await
 951                .unwrap_or_else(|_| Err(anyhow!("share ended")))
 952        })
 953    }
 954
 955    pub fn unshare(&mut self) {
 956        self.share.take();
 957    }
 958
 959    pub fn is_shared(&self) -> bool {
 960        self.share.is_some()
 961    }
 962
 963    pub fn send_extension_counts(&self, project_id: u64) {
 964        let mut extensions = Vec::new();
 965        let mut counts = Vec::new();
 966
 967        for (extension, count) in self.extension_counts() {
 968            extensions.push(extension.to_string_lossy().to_string());
 969            counts.push(*count as u32);
 970        }
 971
 972        self.client
 973            .send(proto::UpdateWorktreeExtensions {
 974                project_id,
 975                worktree_id: self.id().to_proto(),
 976                extensions,
 977                counts,
 978            })
 979            .log_err();
 980    }
 981}
 982
 983impl RemoteWorktree {
 984    fn snapshot(&self) -> Snapshot {
 985        self.snapshot.clone()
 986    }
 987
 988    fn poll_snapshot(&mut self, cx: &mut ModelContext<Worktree>) {
 989        self.snapshot = self.background_snapshot.lock().clone();
 990        cx.emit(Event::UpdatedEntries);
 991        cx.notify();
 992    }
 993
 994    pub fn disconnected_from_host(&mut self) {
 995        self.updates_tx.take();
 996        self.snapshot_subscriptions.clear();
 997    }
 998
 999    pub fn update_from_remote(&mut self, update: proto::UpdateWorktree) {
1000        if let Some(updates_tx) = &self.updates_tx {
1001            updates_tx
1002                .unbounded_send(update)
1003                .expect("consumer runs to completion");
1004        }
1005    }
1006
1007    fn observed_snapshot(&self, scan_id: usize) -> bool {
1008        self.scan_id > scan_id || (self.scan_id == scan_id && self.is_complete)
1009    }
1010
1011    fn wait_for_snapshot(&mut self, scan_id: usize) -> impl Future<Output = ()> {
1012        let (tx, rx) = oneshot::channel();
1013        if self.observed_snapshot(scan_id) {
1014            let _ = tx.send(());
1015        } else {
1016            match self
1017                .snapshot_subscriptions
1018                .binary_search_by_key(&scan_id, |probe| probe.0)
1019            {
1020                Ok(ix) | Err(ix) => self.snapshot_subscriptions.insert(ix, (scan_id, tx)),
1021            }
1022        }
1023
1024        async move {
1025            let _ = rx.await;
1026        }
1027    }
1028
1029    pub fn update_diagnostic_summary(
1030        &mut self,
1031        path: Arc<Path>,
1032        summary: &proto::DiagnosticSummary,
1033    ) {
1034        let summary = DiagnosticSummary {
1035            language_server_id: summary.language_server_id as usize,
1036            error_count: summary.error_count as usize,
1037            warning_count: summary.warning_count as usize,
1038        };
1039        if summary.is_empty() {
1040            self.diagnostic_summaries.remove(&PathKey(path));
1041        } else {
1042            self.diagnostic_summaries.insert(PathKey(path), summary);
1043        }
1044    }
1045
1046    pub fn insert_entry(
1047        &mut self,
1048        entry: proto::Entry,
1049        scan_id: usize,
1050        cx: &mut ModelContext<Worktree>,
1051    ) -> Task<Result<Entry>> {
1052        let wait_for_snapshot = self.wait_for_snapshot(scan_id);
1053        cx.spawn(|this, mut cx| async move {
1054            wait_for_snapshot.await;
1055            this.update(&mut cx, |worktree, _| {
1056                let worktree = worktree.as_remote_mut().unwrap();
1057                let mut snapshot = worktree.background_snapshot.lock();
1058                let entry = snapshot.insert_entry(entry);
1059                worktree.snapshot = snapshot.clone();
1060                entry
1061            })
1062        })
1063    }
1064
1065    pub(crate) fn delete_entry(
1066        &mut self,
1067        id: ProjectEntryId,
1068        scan_id: usize,
1069        cx: &mut ModelContext<Worktree>,
1070    ) -> Task<Result<()>> {
1071        let wait_for_snapshot = self.wait_for_snapshot(scan_id);
1072        cx.spawn(|this, mut cx| async move {
1073            wait_for_snapshot.await;
1074            this.update(&mut cx, |worktree, _| {
1075                let worktree = worktree.as_remote_mut().unwrap();
1076                let mut snapshot = worktree.background_snapshot.lock();
1077                snapshot.delete_entry(id);
1078                worktree.snapshot = snapshot.clone();
1079            });
1080            Ok(())
1081        })
1082    }
1083}
1084
1085impl Snapshot {
1086    pub fn id(&self) -> WorktreeId {
1087        self.id
1088    }
1089
1090    pub fn contains_entry(&self, entry_id: ProjectEntryId) -> bool {
1091        self.entries_by_id.get(&entry_id, &()).is_some()
1092    }
1093
1094    pub(crate) fn insert_entry(&mut self, entry: proto::Entry) -> Result<Entry> {
1095        let entry = Entry::try_from((&self.root_char_bag, entry))?;
1096        let old_entry = self.entries_by_id.insert_or_replace(
1097            PathEntry {
1098                id: entry.id,
1099                path: entry.path.clone(),
1100                is_ignored: entry.is_ignored,
1101                scan_id: 0,
1102            },
1103            &(),
1104        );
1105        if let Some(old_entry) = old_entry {
1106            self.entries_by_path.remove(&PathKey(old_entry.path), &());
1107        }
1108        self.entries_by_path.insert_or_replace(entry.clone(), &());
1109        Ok(entry)
1110    }
1111
1112    fn delete_entry(&mut self, entry_id: ProjectEntryId) -> bool {
1113        if let Some(removed_entry) = self.entries_by_id.remove(&entry_id, &()) {
1114            self.entries_by_path = {
1115                let mut cursor = self.entries_by_path.cursor();
1116                let mut new_entries_by_path =
1117                    cursor.slice(&TraversalTarget::Path(&removed_entry.path), Bias::Left, &());
1118                while let Some(entry) = cursor.item() {
1119                    if entry.path.starts_with(&removed_entry.path) {
1120                        self.entries_by_id.remove(&entry.id, &());
1121                        cursor.next(&());
1122                    } else {
1123                        break;
1124                    }
1125                }
1126                new_entries_by_path.push_tree(cursor.suffix(&()), &());
1127                new_entries_by_path
1128            };
1129
1130            true
1131        } else {
1132            false
1133        }
1134    }
1135
1136    pub(crate) fn apply_remote_update(&mut self, update: proto::UpdateWorktree) -> Result<()> {
1137        let mut entries_by_path_edits = Vec::new();
1138        let mut entries_by_id_edits = Vec::new();
1139        for entry_id in update.removed_entries {
1140            let entry = self
1141                .entry_for_id(ProjectEntryId::from_proto(entry_id))
1142                .ok_or_else(|| anyhow!("unknown entry {}", entry_id))?;
1143            entries_by_path_edits.push(Edit::Remove(PathKey(entry.path.clone())));
1144            entries_by_id_edits.push(Edit::Remove(entry.id));
1145        }
1146
1147        for entry in update.updated_entries {
1148            let entry = Entry::try_from((&self.root_char_bag, entry))?;
1149            if let Some(PathEntry { path, .. }) = self.entries_by_id.get(&entry.id, &()) {
1150                entries_by_path_edits.push(Edit::Remove(PathKey(path.clone())));
1151            }
1152            entries_by_id_edits.push(Edit::Insert(PathEntry {
1153                id: entry.id,
1154                path: entry.path.clone(),
1155                is_ignored: entry.is_ignored,
1156                scan_id: 0,
1157            }));
1158            entries_by_path_edits.push(Edit::Insert(entry));
1159        }
1160
1161        self.entries_by_path.edit(entries_by_path_edits, &());
1162        self.entries_by_id.edit(entries_by_id_edits, &());
1163        self.scan_id = update.scan_id as usize;
1164        self.is_complete = update.is_last_update;
1165
1166        Ok(())
1167    }
1168
1169    pub fn file_count(&self) -> usize {
1170        self.entries_by_path.summary().file_count
1171    }
1172
1173    pub fn visible_file_count(&self) -> usize {
1174        self.entries_by_path.summary().visible_file_count
1175    }
1176
1177    fn traverse_from_offset(
1178        &self,
1179        include_dirs: bool,
1180        include_ignored: bool,
1181        start_offset: usize,
1182    ) -> Traversal {
1183        let mut cursor = self.entries_by_path.cursor();
1184        cursor.seek(
1185            &TraversalTarget::Count {
1186                count: start_offset,
1187                include_dirs,
1188                include_ignored,
1189            },
1190            Bias::Right,
1191            &(),
1192        );
1193        Traversal {
1194            cursor,
1195            include_dirs,
1196            include_ignored,
1197        }
1198    }
1199
1200    fn traverse_from_path(
1201        &self,
1202        include_dirs: bool,
1203        include_ignored: bool,
1204        path: &Path,
1205    ) -> Traversal {
1206        let mut cursor = self.entries_by_path.cursor();
1207        cursor.seek(&TraversalTarget::Path(path), Bias::Left, &());
1208        Traversal {
1209            cursor,
1210            include_dirs,
1211            include_ignored,
1212        }
1213    }
1214
1215    pub fn files(&self, include_ignored: bool, start: usize) -> Traversal {
1216        self.traverse_from_offset(false, include_ignored, start)
1217    }
1218
1219    pub fn entries(&self, include_ignored: bool) -> Traversal {
1220        self.traverse_from_offset(true, include_ignored, 0)
1221    }
1222
1223    pub fn paths(&self) -> impl Iterator<Item = &Arc<Path>> {
1224        let empty_path = Path::new("");
1225        self.entries_by_path
1226            .cursor::<()>()
1227            .filter(move |entry| entry.path.as_ref() != empty_path)
1228            .map(|entry| &entry.path)
1229    }
1230
1231    fn child_entries<'a>(&'a self, parent_path: &'a Path) -> ChildEntriesIter<'a> {
1232        let mut cursor = self.entries_by_path.cursor();
1233        cursor.seek(&TraversalTarget::Path(parent_path), Bias::Right, &());
1234        let traversal = Traversal {
1235            cursor,
1236            include_dirs: true,
1237            include_ignored: true,
1238        };
1239        ChildEntriesIter {
1240            traversal,
1241            parent_path,
1242        }
1243    }
1244
1245    pub fn root_entry(&self) -> Option<&Entry> {
1246        self.entry_for_path("")
1247    }
1248
1249    pub fn root_name(&self) -> &str {
1250        &self.root_name
1251    }
1252
1253    pub fn scan_id(&self) -> usize {
1254        self.scan_id
1255    }
1256
1257    pub fn entry_for_path(&self, path: impl AsRef<Path>) -> Option<&Entry> {
1258        let path = path.as_ref();
1259        self.traverse_from_path(true, true, path)
1260            .entry()
1261            .and_then(|entry| {
1262                if entry.path.as_ref() == path {
1263                    Some(entry)
1264                } else {
1265                    None
1266                }
1267            })
1268    }
1269
1270    pub fn entry_for_id(&self, id: ProjectEntryId) -> Option<&Entry> {
1271        let entry = self.entries_by_id.get(&id, &())?;
1272        self.entry_for_path(&entry.path)
1273    }
1274
1275    pub fn inode_for_path(&self, path: impl AsRef<Path>) -> Option<u64> {
1276        self.entry_for_path(path.as_ref()).map(|e| e.inode)
1277    }
1278}
1279
1280impl LocalSnapshot {
1281    pub fn abs_path(&self) -> &Arc<Path> {
1282        &self.abs_path
1283    }
1284
1285    pub fn extension_counts(&self) -> &HashMap<OsString, usize> {
1286        &self.extension_counts
1287    }
1288
1289    #[cfg(test)]
1290    pub(crate) fn build_initial_update(&self, project_id: u64) -> proto::UpdateWorktree {
1291        let root_name = self.root_name.clone();
1292        proto::UpdateWorktree {
1293            project_id,
1294            worktree_id: self.id().to_proto(),
1295            root_name,
1296            updated_entries: self.entries_by_path.iter().map(Into::into).collect(),
1297            removed_entries: Default::default(),
1298            scan_id: self.scan_id as u64,
1299            is_last_update: true,
1300        }
1301    }
1302
1303    pub(crate) fn build_update(
1304        &self,
1305        other: &Self,
1306        project_id: u64,
1307        worktree_id: u64,
1308        include_ignored: bool,
1309    ) -> proto::UpdateWorktree {
1310        let mut updated_entries = Vec::new();
1311        let mut removed_entries = Vec::new();
1312        let mut self_entries = self
1313            .entries_by_id
1314            .cursor::<()>()
1315            .filter(|e| include_ignored || !e.is_ignored)
1316            .peekable();
1317        let mut other_entries = other
1318            .entries_by_id
1319            .cursor::<()>()
1320            .filter(|e| include_ignored || !e.is_ignored)
1321            .peekable();
1322        loop {
1323            match (self_entries.peek(), other_entries.peek()) {
1324                (Some(self_entry), Some(other_entry)) => {
1325                    match Ord::cmp(&self_entry.id, &other_entry.id) {
1326                        Ordering::Less => {
1327                            let entry = self.entry_for_id(self_entry.id).unwrap().into();
1328                            updated_entries.push(entry);
1329                            self_entries.next();
1330                        }
1331                        Ordering::Equal => {
1332                            if self_entry.scan_id != other_entry.scan_id {
1333                                let entry = self.entry_for_id(self_entry.id).unwrap().into();
1334                                updated_entries.push(entry);
1335                            }
1336
1337                            self_entries.next();
1338                            other_entries.next();
1339                        }
1340                        Ordering::Greater => {
1341                            removed_entries.push(other_entry.id.to_proto());
1342                            other_entries.next();
1343                        }
1344                    }
1345                }
1346                (Some(self_entry), None) => {
1347                    let entry = self.entry_for_id(self_entry.id).unwrap().into();
1348                    updated_entries.push(entry);
1349                    self_entries.next();
1350                }
1351                (None, Some(other_entry)) => {
1352                    removed_entries.push(other_entry.id.to_proto());
1353                    other_entries.next();
1354                }
1355                (None, None) => break,
1356            }
1357        }
1358
1359        proto::UpdateWorktree {
1360            project_id,
1361            worktree_id,
1362            root_name: self.root_name().to_string(),
1363            updated_entries,
1364            removed_entries,
1365            scan_id: self.scan_id as u64,
1366            is_last_update: true,
1367        }
1368    }
1369
1370    fn insert_entry(&mut self, mut entry: Entry, fs: &dyn Fs) -> Entry {
1371        if !entry.is_dir() && entry.path.file_name() == Some(&GITIGNORE) {
1372            let abs_path = self.abs_path.join(&entry.path);
1373            match smol::block_on(build_gitignore(&abs_path, fs)) {
1374                Ok(ignore) => {
1375                    self.ignores_by_parent_abs_path.insert(
1376                        abs_path.parent().unwrap().into(),
1377                        (Arc::new(ignore), self.scan_id),
1378                    );
1379                }
1380                Err(error) => {
1381                    log::error!(
1382                        "error loading .gitignore file {:?} - {:?}",
1383                        &entry.path,
1384                        error
1385                    );
1386                }
1387            }
1388        }
1389
1390        self.reuse_entry_id(&mut entry);
1391
1392        if entry.kind == EntryKind::PendingDir {
1393            if let Some(existing_entry) =
1394                self.entries_by_path.get(&PathKey(entry.path.clone()), &())
1395            {
1396                entry.kind = existing_entry.kind;
1397            }
1398        }
1399
1400        self.entries_by_path.insert_or_replace(entry.clone(), &());
1401        let scan_id = self.scan_id;
1402        let removed_entry = self.entries_by_id.insert_or_replace(
1403            PathEntry {
1404                id: entry.id,
1405                path: entry.path.clone(),
1406                is_ignored: entry.is_ignored,
1407                scan_id,
1408            },
1409            &(),
1410        );
1411
1412        if let Some(removed_entry) = removed_entry {
1413            self.dec_extension_count(&removed_entry.path, removed_entry.is_ignored);
1414        }
1415        self.inc_extension_count(&entry.path, entry.is_ignored);
1416
1417        entry
1418    }
1419
1420    fn populate_dir(
1421        &mut self,
1422        parent_path: Arc<Path>,
1423        entries: impl IntoIterator<Item = Entry>,
1424        ignore: Option<Arc<Gitignore>>,
1425    ) {
1426        let mut parent_entry = if let Some(parent_entry) =
1427            self.entries_by_path.get(&PathKey(parent_path.clone()), &())
1428        {
1429            parent_entry.clone()
1430        } else {
1431            log::warn!(
1432                "populating a directory {:?} that has been removed",
1433                parent_path
1434            );
1435            return;
1436        };
1437
1438        if let Some(ignore) = ignore {
1439            self.ignores_by_parent_abs_path.insert(
1440                self.abs_path.join(&parent_path).into(),
1441                (ignore, self.scan_id),
1442            );
1443        }
1444        if matches!(parent_entry.kind, EntryKind::PendingDir) {
1445            parent_entry.kind = EntryKind::Dir;
1446        } else {
1447            unreachable!();
1448        }
1449
1450        let mut entries_by_path_edits = vec![Edit::Insert(parent_entry)];
1451        let mut entries_by_id_edits = Vec::new();
1452
1453        for mut entry in entries {
1454            self.reuse_entry_id(&mut entry);
1455            self.inc_extension_count(&entry.path, entry.is_ignored);
1456            entries_by_id_edits.push(Edit::Insert(PathEntry {
1457                id: entry.id,
1458                path: entry.path.clone(),
1459                is_ignored: entry.is_ignored,
1460                scan_id: self.scan_id,
1461            }));
1462            entries_by_path_edits.push(Edit::Insert(entry));
1463        }
1464
1465        self.entries_by_path.edit(entries_by_path_edits, &());
1466        let removed_entries = self.entries_by_id.edit(entries_by_id_edits, &());
1467
1468        for removed_entry in removed_entries {
1469            self.dec_extension_count(&removed_entry.path, removed_entry.is_ignored);
1470        }
1471    }
1472
1473    fn inc_extension_count(&mut self, path: &Path, ignored: bool) {
1474        if !ignored {
1475            if let Some(extension) = path.extension() {
1476                if let Some(count) = self.extension_counts.get_mut(extension) {
1477                    *count += 1;
1478                } else {
1479                    self.extension_counts.insert(extension.into(), 1);
1480                }
1481            }
1482        }
1483    }
1484
1485    fn dec_extension_count(&mut self, path: &Path, ignored: bool) {
1486        if !ignored {
1487            if let Some(extension) = path.extension() {
1488                if let Some(count) = self.extension_counts.get_mut(extension) {
1489                    *count -= 1;
1490                }
1491            }
1492        }
1493    }
1494
1495    fn reuse_entry_id(&mut self, entry: &mut Entry) {
1496        if let Some(removed_entry_id) = self.removed_entry_ids.remove(&entry.inode) {
1497            entry.id = removed_entry_id;
1498        } else if let Some(existing_entry) = self.entry_for_path(&entry.path) {
1499            entry.id = existing_entry.id;
1500        }
1501    }
1502
1503    fn remove_path(&mut self, path: &Path) {
1504        let mut new_entries;
1505        let removed_entries;
1506        {
1507            let mut cursor = self.entries_by_path.cursor::<TraversalProgress>();
1508            new_entries = cursor.slice(&TraversalTarget::Path(path), Bias::Left, &());
1509            removed_entries = cursor.slice(&TraversalTarget::PathSuccessor(path), Bias::Left, &());
1510            new_entries.push_tree(cursor.suffix(&()), &());
1511        }
1512        self.entries_by_path = new_entries;
1513
1514        let mut entries_by_id_edits = Vec::new();
1515        for entry in removed_entries.cursor::<()>() {
1516            let removed_entry_id = self
1517                .removed_entry_ids
1518                .entry(entry.inode)
1519                .or_insert(entry.id);
1520            *removed_entry_id = cmp::max(*removed_entry_id, entry.id);
1521            entries_by_id_edits.push(Edit::Remove(entry.id));
1522            self.dec_extension_count(&entry.path, entry.is_ignored);
1523        }
1524        self.entries_by_id.edit(entries_by_id_edits, &());
1525
1526        if path.file_name() == Some(&GITIGNORE) {
1527            let abs_parent_path = self.abs_path.join(path.parent().unwrap());
1528            if let Some((_, scan_id)) = self
1529                .ignores_by_parent_abs_path
1530                .get_mut(abs_parent_path.as_path())
1531            {
1532                *scan_id = self.snapshot.scan_id;
1533            }
1534        }
1535    }
1536
1537    fn ancestor_inodes_for_path(&self, path: &Path) -> TreeSet<u64> {
1538        let mut inodes = TreeSet::default();
1539        for ancestor in path.ancestors().skip(1) {
1540            if let Some(entry) = self.entry_for_path(ancestor) {
1541                inodes.insert(entry.inode);
1542            }
1543        }
1544        inodes
1545    }
1546
1547    fn ignore_stack_for_abs_path(&self, abs_path: &Path, is_dir: bool) -> Arc<IgnoreStack> {
1548        let mut new_ignores = Vec::new();
1549        for ancestor in abs_path.ancestors().skip(1) {
1550            if let Some((ignore, _)) = self.ignores_by_parent_abs_path.get(ancestor) {
1551                new_ignores.push((ancestor, Some(ignore.clone())));
1552            } else {
1553                new_ignores.push((ancestor, None));
1554            }
1555        }
1556
1557        let mut ignore_stack = IgnoreStack::none();
1558        for (parent_abs_path, ignore) in new_ignores.into_iter().rev() {
1559            if ignore_stack.is_abs_path_ignored(parent_abs_path, true) {
1560                ignore_stack = IgnoreStack::all();
1561                break;
1562            } else if let Some(ignore) = ignore {
1563                ignore_stack = ignore_stack.append(parent_abs_path.into(), ignore);
1564            }
1565        }
1566
1567        if ignore_stack.is_abs_path_ignored(abs_path, is_dir) {
1568            ignore_stack = IgnoreStack::all();
1569        }
1570
1571        ignore_stack
1572    }
1573}
1574
1575async fn build_gitignore(abs_path: &Path, fs: &dyn Fs) -> Result<Gitignore> {
1576    let contents = fs.load(abs_path).await?;
1577    let parent = abs_path.parent().unwrap_or_else(|| Path::new("/"));
1578    let mut builder = GitignoreBuilder::new(parent);
1579    for line in contents.lines() {
1580        builder.add_line(Some(abs_path.into()), line)?;
1581    }
1582    Ok(builder.build()?)
1583}
1584
1585impl WorktreeId {
1586    pub fn from_usize(handle_id: usize) -> Self {
1587        Self(handle_id)
1588    }
1589
1590    pub(crate) fn from_proto(id: u64) -> Self {
1591        Self(id as usize)
1592    }
1593
1594    pub fn to_proto(&self) -> u64 {
1595        self.0 as u64
1596    }
1597
1598    pub fn to_usize(&self) -> usize {
1599        self.0
1600    }
1601}
1602
1603impl fmt::Display for WorktreeId {
1604    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1605        self.0.fmt(f)
1606    }
1607}
1608
1609impl Deref for Worktree {
1610    type Target = Snapshot;
1611
1612    fn deref(&self) -> &Self::Target {
1613        match self {
1614            Worktree::Local(worktree) => &worktree.snapshot,
1615            Worktree::Remote(worktree) => &worktree.snapshot,
1616        }
1617    }
1618}
1619
1620impl Deref for LocalWorktree {
1621    type Target = LocalSnapshot;
1622
1623    fn deref(&self) -> &Self::Target {
1624        &self.snapshot
1625    }
1626}
1627
1628impl Deref for RemoteWorktree {
1629    type Target = Snapshot;
1630
1631    fn deref(&self) -> &Self::Target {
1632        &self.snapshot
1633    }
1634}
1635
1636impl fmt::Debug for LocalWorktree {
1637    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1638        self.snapshot.fmt(f)
1639    }
1640}
1641
1642impl fmt::Debug for Snapshot {
1643    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1644        struct EntriesById<'a>(&'a SumTree<PathEntry>);
1645        struct EntriesByPath<'a>(&'a SumTree<Entry>);
1646
1647        impl<'a> fmt::Debug for EntriesByPath<'a> {
1648            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1649                f.debug_map()
1650                    .entries(self.0.iter().map(|entry| (&entry.path, entry.id)))
1651                    .finish()
1652            }
1653        }
1654
1655        impl<'a> fmt::Debug for EntriesById<'a> {
1656            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1657                f.debug_list().entries(self.0.iter()).finish()
1658            }
1659        }
1660
1661        f.debug_struct("Snapshot")
1662            .field("id", &self.id)
1663            .field("root_name", &self.root_name)
1664            .field("entries_by_path", &EntriesByPath(&self.entries_by_path))
1665            .field("entries_by_id", &EntriesById(&self.entries_by_id))
1666            .finish()
1667    }
1668}
1669
1670#[derive(Clone, PartialEq)]
1671pub struct File {
1672    pub worktree: ModelHandle<Worktree>,
1673    pub path: Arc<Path>,
1674    pub mtime: SystemTime,
1675    pub(crate) entry_id: Option<ProjectEntryId>,
1676    pub(crate) is_local: bool,
1677}
1678
1679impl language::File for File {
1680    fn as_local(&self) -> Option<&dyn language::LocalFile> {
1681        if self.is_local {
1682            Some(self)
1683        } else {
1684            None
1685        }
1686    }
1687
1688    fn mtime(&self) -> SystemTime {
1689        self.mtime
1690    }
1691
1692    fn path(&self) -> &Arc<Path> {
1693        &self.path
1694    }
1695
1696    fn full_path(&self, cx: &AppContext) -> PathBuf {
1697        let mut full_path = PathBuf::new();
1698        full_path.push(self.worktree.read(cx).root_name());
1699        if self.path.components().next().is_some() {
1700            full_path.push(&self.path);
1701        }
1702        full_path
1703    }
1704
1705    /// Returns the last component of this handle's absolute path. If this handle refers to the root
1706    /// of its worktree, then this method will return the name of the worktree itself.
1707    fn file_name<'a>(&'a self, cx: &'a AppContext) -> &'a OsStr {
1708        self.path
1709            .file_name()
1710            .unwrap_or_else(|| OsStr::new(&self.worktree.read(cx).root_name))
1711    }
1712
1713    fn is_deleted(&self) -> bool {
1714        self.entry_id.is_none()
1715    }
1716
1717    fn save(
1718        &self,
1719        buffer_id: u64,
1720        text: Rope,
1721        version: clock::Global,
1722        line_ending: LineEnding,
1723        cx: &mut MutableAppContext,
1724    ) -> Task<Result<(clock::Global, String, SystemTime)>> {
1725        self.worktree.update(cx, |worktree, cx| match worktree {
1726            Worktree::Local(worktree) => {
1727                let rpc = worktree.client.clone();
1728                let project_id = worktree.share.as_ref().map(|share| share.project_id);
1729                let fingerprint = text.fingerprint();
1730                let save = worktree.write_file(self.path.clone(), text, line_ending, cx);
1731                cx.background().spawn(async move {
1732                    let entry = save.await?;
1733                    if let Some(project_id) = project_id {
1734                        rpc.send(proto::BufferSaved {
1735                            project_id,
1736                            buffer_id,
1737                            version: serialize_version(&version),
1738                            mtime: Some(entry.mtime.into()),
1739                            fingerprint: fingerprint.clone(),
1740                        })?;
1741                    }
1742                    Ok((version, fingerprint, entry.mtime))
1743                })
1744            }
1745            Worktree::Remote(worktree) => {
1746                let rpc = worktree.client.clone();
1747                let project_id = worktree.project_id;
1748                cx.foreground().spawn(async move {
1749                    let response = rpc
1750                        .request(proto::SaveBuffer {
1751                            project_id,
1752                            buffer_id,
1753                            version: serialize_version(&version),
1754                        })
1755                        .await?;
1756                    let version = deserialize_version(response.version);
1757                    let mtime = response
1758                        .mtime
1759                        .ok_or_else(|| anyhow!("missing mtime"))?
1760                        .into();
1761                    Ok((version, response.fingerprint, mtime))
1762                })
1763            }
1764        })
1765    }
1766
1767    fn as_any(&self) -> &dyn Any {
1768        self
1769    }
1770
1771    fn to_proto(&self) -> rpc::proto::File {
1772        rpc::proto::File {
1773            worktree_id: self.worktree.id() as u64,
1774            entry_id: self.entry_id.map(|entry_id| entry_id.to_proto()),
1775            path: self.path.to_string_lossy().into(),
1776            mtime: Some(self.mtime.into()),
1777        }
1778    }
1779}
1780
1781impl language::LocalFile for File {
1782    fn abs_path(&self, cx: &AppContext) -> PathBuf {
1783        self.worktree
1784            .read(cx)
1785            .as_local()
1786            .unwrap()
1787            .abs_path
1788            .join(&self.path)
1789    }
1790
1791    fn load(&self, cx: &AppContext) -> Task<Result<String>> {
1792        let worktree = self.worktree.read(cx).as_local().unwrap();
1793        let abs_path = worktree.absolutize(&self.path);
1794        let fs = worktree.fs.clone();
1795        cx.background()
1796            .spawn(async move { fs.load(&abs_path).await })
1797    }
1798
1799    fn buffer_reloaded(
1800        &self,
1801        buffer_id: u64,
1802        version: &clock::Global,
1803        fingerprint: String,
1804        line_ending: LineEnding,
1805        mtime: SystemTime,
1806        cx: &mut MutableAppContext,
1807    ) {
1808        let worktree = self.worktree.read(cx).as_local().unwrap();
1809        if let Some(project_id) = worktree.share.as_ref().map(|share| share.project_id) {
1810            worktree
1811                .client
1812                .send(proto::BufferReloaded {
1813                    project_id,
1814                    buffer_id,
1815                    version: serialize_version(version),
1816                    mtime: Some(mtime.into()),
1817                    fingerprint,
1818                    line_ending: serialize_line_ending(line_ending) as i32,
1819                })
1820                .log_err();
1821        }
1822    }
1823}
1824
1825impl File {
1826    pub fn from_proto(
1827        proto: rpc::proto::File,
1828        worktree: ModelHandle<Worktree>,
1829        cx: &AppContext,
1830    ) -> Result<Self> {
1831        let worktree_id = worktree
1832            .read(cx)
1833            .as_remote()
1834            .ok_or_else(|| anyhow!("not remote"))?
1835            .id();
1836
1837        if worktree_id.to_proto() != proto.worktree_id {
1838            return Err(anyhow!("worktree id does not match file"));
1839        }
1840
1841        Ok(Self {
1842            worktree,
1843            path: Path::new(&proto.path).into(),
1844            mtime: proto.mtime.ok_or_else(|| anyhow!("no timestamp"))?.into(),
1845            entry_id: proto.entry_id.map(ProjectEntryId::from_proto),
1846            is_local: false,
1847        })
1848    }
1849
1850    pub fn from_dyn(file: Option<&dyn language::File>) -> Option<&Self> {
1851        file.and_then(|f| f.as_any().downcast_ref())
1852    }
1853
1854    pub fn worktree_id(&self, cx: &AppContext) -> WorktreeId {
1855        self.worktree.read(cx).id()
1856    }
1857
1858    pub fn project_entry_id(&self, _: &AppContext) -> Option<ProjectEntryId> {
1859        self.entry_id
1860    }
1861}
1862
1863#[derive(Clone, Debug, PartialEq, Eq)]
1864pub struct Entry {
1865    pub id: ProjectEntryId,
1866    pub kind: EntryKind,
1867    pub path: Arc<Path>,
1868    pub inode: u64,
1869    pub mtime: SystemTime,
1870    pub is_symlink: bool,
1871    pub is_ignored: bool,
1872}
1873
1874#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1875pub enum EntryKind {
1876    PendingDir,
1877    Dir,
1878    File(CharBag),
1879}
1880
1881impl Entry {
1882    fn new(
1883        path: Arc<Path>,
1884        metadata: &fs::Metadata,
1885        next_entry_id: &AtomicUsize,
1886        root_char_bag: CharBag,
1887    ) -> Self {
1888        Self {
1889            id: ProjectEntryId::new(next_entry_id),
1890            kind: if metadata.is_dir {
1891                EntryKind::PendingDir
1892            } else {
1893                EntryKind::File(char_bag_for_path(root_char_bag, &path))
1894            },
1895            path,
1896            inode: metadata.inode,
1897            mtime: metadata.mtime,
1898            is_symlink: metadata.is_symlink,
1899            is_ignored: false,
1900        }
1901    }
1902
1903    pub fn is_dir(&self) -> bool {
1904        matches!(self.kind, EntryKind::Dir | EntryKind::PendingDir)
1905    }
1906
1907    pub fn is_file(&self) -> bool {
1908        matches!(self.kind, EntryKind::File(_))
1909    }
1910}
1911
1912impl sum_tree::Item for Entry {
1913    type Summary = EntrySummary;
1914
1915    fn summary(&self) -> Self::Summary {
1916        let visible_count = if self.is_ignored { 0 } else { 1 };
1917        let file_count;
1918        let visible_file_count;
1919        if self.is_file() {
1920            file_count = 1;
1921            visible_file_count = visible_count;
1922        } else {
1923            file_count = 0;
1924            visible_file_count = 0;
1925        }
1926
1927        EntrySummary {
1928            max_path: self.path.clone(),
1929            count: 1,
1930            visible_count,
1931            file_count,
1932            visible_file_count,
1933        }
1934    }
1935}
1936
1937impl sum_tree::KeyedItem for Entry {
1938    type Key = PathKey;
1939
1940    fn key(&self) -> Self::Key {
1941        PathKey(self.path.clone())
1942    }
1943}
1944
1945#[derive(Clone, Debug)]
1946pub struct EntrySummary {
1947    max_path: Arc<Path>,
1948    count: usize,
1949    visible_count: usize,
1950    file_count: usize,
1951    visible_file_count: usize,
1952}
1953
1954impl Default for EntrySummary {
1955    fn default() -> Self {
1956        Self {
1957            max_path: Arc::from(Path::new("")),
1958            count: 0,
1959            visible_count: 0,
1960            file_count: 0,
1961            visible_file_count: 0,
1962        }
1963    }
1964}
1965
1966impl sum_tree::Summary for EntrySummary {
1967    type Context = ();
1968
1969    fn add_summary(&mut self, rhs: &Self, _: &()) {
1970        self.max_path = rhs.max_path.clone();
1971        self.count += rhs.count;
1972        self.visible_count += rhs.visible_count;
1973        self.file_count += rhs.file_count;
1974        self.visible_file_count += rhs.visible_file_count;
1975    }
1976}
1977
1978#[derive(Clone, Debug)]
1979struct PathEntry {
1980    id: ProjectEntryId,
1981    path: Arc<Path>,
1982    is_ignored: bool,
1983    scan_id: usize,
1984}
1985
1986impl sum_tree::Item for PathEntry {
1987    type Summary = PathEntrySummary;
1988
1989    fn summary(&self) -> Self::Summary {
1990        PathEntrySummary { max_id: self.id }
1991    }
1992}
1993
1994impl sum_tree::KeyedItem for PathEntry {
1995    type Key = ProjectEntryId;
1996
1997    fn key(&self) -> Self::Key {
1998        self.id
1999    }
2000}
2001
2002#[derive(Clone, Debug, Default)]
2003struct PathEntrySummary {
2004    max_id: ProjectEntryId,
2005}
2006
2007impl sum_tree::Summary for PathEntrySummary {
2008    type Context = ();
2009
2010    fn add_summary(&mut self, summary: &Self, _: &Self::Context) {
2011        self.max_id = summary.max_id;
2012    }
2013}
2014
2015impl<'a> sum_tree::Dimension<'a, PathEntrySummary> for ProjectEntryId {
2016    fn add_summary(&mut self, summary: &'a PathEntrySummary, _: &()) {
2017        *self = summary.max_id;
2018    }
2019}
2020
2021#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
2022pub struct PathKey(Arc<Path>);
2023
2024impl Default for PathKey {
2025    fn default() -> Self {
2026        Self(Path::new("").into())
2027    }
2028}
2029
2030impl<'a> sum_tree::Dimension<'a, EntrySummary> for PathKey {
2031    fn add_summary(&mut self, summary: &'a EntrySummary, _: &()) {
2032        self.0 = summary.max_path.clone();
2033    }
2034}
2035
2036struct BackgroundScanner {
2037    fs: Arc<dyn Fs>,
2038    snapshot: Arc<Mutex<LocalSnapshot>>,
2039    notify: UnboundedSender<ScanState>,
2040    executor: Arc<executor::Background>,
2041}
2042
2043impl BackgroundScanner {
2044    fn new(
2045        snapshot: Arc<Mutex<LocalSnapshot>>,
2046        notify: UnboundedSender<ScanState>,
2047        fs: Arc<dyn Fs>,
2048        executor: Arc<executor::Background>,
2049    ) -> Self {
2050        Self {
2051            fs,
2052            snapshot,
2053            notify,
2054            executor,
2055        }
2056    }
2057
2058    fn abs_path(&self) -> Arc<Path> {
2059        self.snapshot.lock().abs_path.clone()
2060    }
2061
2062    fn snapshot(&self) -> LocalSnapshot {
2063        self.snapshot.lock().clone()
2064    }
2065
2066    async fn run(mut self, events_rx: impl Stream<Item = Vec<fsevent::Event>>) {
2067        if self.notify.unbounded_send(ScanState::Initializing).is_err() {
2068            return;
2069        }
2070
2071        if let Err(err) = self.scan_dirs().await {
2072            if self
2073                .notify
2074                .unbounded_send(ScanState::Err(Arc::new(err)))
2075                .is_err()
2076            {
2077                return;
2078            }
2079        }
2080
2081        if self.notify.unbounded_send(ScanState::Idle).is_err() {
2082            return;
2083        }
2084
2085        futures::pin_mut!(events_rx);
2086
2087        while let Some(mut events) = events_rx.next().await {
2088            while let Poll::Ready(Some(additional_events)) = futures::poll!(events_rx.next()) {
2089                events.extend(additional_events);
2090            }
2091
2092            if self.notify.unbounded_send(ScanState::Updating).is_err() {
2093                break;
2094            }
2095
2096            if !self.process_events(events).await {
2097                break;
2098            }
2099
2100            if self.notify.unbounded_send(ScanState::Idle).is_err() {
2101                break;
2102            }
2103        }
2104    }
2105
2106    async fn scan_dirs(&mut self) -> Result<()> {
2107        let root_char_bag;
2108        let root_abs_path;
2109        let root_inode;
2110        let is_dir;
2111        let next_entry_id;
2112        {
2113            let snapshot = self.snapshot.lock();
2114            root_char_bag = snapshot.root_char_bag;
2115            root_abs_path = snapshot.abs_path.clone();
2116            root_inode = snapshot.root_entry().map(|e| e.inode);
2117            is_dir = snapshot.root_entry().map_or(false, |e| e.is_dir());
2118            next_entry_id = snapshot.next_entry_id.clone();
2119        };
2120
2121        // Populate ignores above the root.
2122        for ancestor in root_abs_path.ancestors().skip(1) {
2123            if let Ok(ignore) = build_gitignore(&ancestor.join(&*GITIGNORE), self.fs.as_ref()).await
2124            {
2125                self.snapshot
2126                    .lock()
2127                    .ignores_by_parent_abs_path
2128                    .insert(ancestor.into(), (ignore.into(), 0));
2129            }
2130        }
2131
2132        let ignore_stack = {
2133            let mut snapshot = self.snapshot.lock();
2134            let ignore_stack = snapshot.ignore_stack_for_abs_path(&root_abs_path, true);
2135            if ignore_stack.is_all() {
2136                if let Some(mut root_entry) = snapshot.root_entry().cloned() {
2137                    root_entry.is_ignored = true;
2138                    snapshot.insert_entry(root_entry, self.fs.as_ref());
2139                }
2140            }
2141            ignore_stack
2142        };
2143
2144        if is_dir {
2145            let path: Arc<Path> = Arc::from(Path::new(""));
2146            let mut ancestor_inodes = TreeSet::default();
2147            if let Some(root_inode) = root_inode {
2148                ancestor_inodes.insert(root_inode);
2149            }
2150
2151            let (tx, rx) = channel::unbounded();
2152            self.executor
2153                .block(tx.send(ScanJob {
2154                    abs_path: root_abs_path.to_path_buf(),
2155                    path,
2156                    ignore_stack,
2157                    ancestor_inodes,
2158                    scan_queue: tx.clone(),
2159                }))
2160                .unwrap();
2161            drop(tx);
2162
2163            self.executor
2164                .scoped(|scope| {
2165                    for _ in 0..self.executor.num_cpus() {
2166                        scope.spawn(async {
2167                            while let Ok(job) = rx.recv().await {
2168                                if let Err(err) = self
2169                                    .scan_dir(root_char_bag, next_entry_id.clone(), &job)
2170                                    .await
2171                                {
2172                                    log::error!("error scanning {:?}: {}", job.abs_path, err);
2173                                }
2174                            }
2175                        });
2176                    }
2177                })
2178                .await;
2179        }
2180
2181        Ok(())
2182    }
2183
2184    async fn scan_dir(
2185        &self,
2186        root_char_bag: CharBag,
2187        next_entry_id: Arc<AtomicUsize>,
2188        job: &ScanJob,
2189    ) -> Result<()> {
2190        let mut new_entries: Vec<Entry> = Vec::new();
2191        let mut new_jobs: Vec<ScanJob> = Vec::new();
2192        let mut ignore_stack = job.ignore_stack.clone();
2193        let mut new_ignore = None;
2194
2195        let mut child_paths = self.fs.read_dir(&job.abs_path).await?;
2196        while let Some(child_abs_path) = child_paths.next().await {
2197            let child_abs_path = match child_abs_path {
2198                Ok(child_abs_path) => child_abs_path,
2199                Err(error) => {
2200                    log::error!("error processing entry {:?}", error);
2201                    continue;
2202                }
2203            };
2204            let child_name = child_abs_path.file_name().unwrap();
2205            let child_path: Arc<Path> = job.path.join(child_name).into();
2206            let child_metadata = match self.fs.metadata(&child_abs_path).await {
2207                Ok(Some(metadata)) => metadata,
2208                Ok(None) => continue,
2209                Err(err) => {
2210                    log::error!("error processing {:?}: {:?}", child_abs_path, err);
2211                    continue;
2212                }
2213            };
2214
2215            // If we find a .gitignore, add it to the stack of ignores used to determine which paths are ignored
2216            if child_name == *GITIGNORE {
2217                match build_gitignore(&child_abs_path, self.fs.as_ref()).await {
2218                    Ok(ignore) => {
2219                        let ignore = Arc::new(ignore);
2220                        ignore_stack =
2221                            ignore_stack.append(job.abs_path.as_path().into(), ignore.clone());
2222                        new_ignore = Some(ignore);
2223                    }
2224                    Err(error) => {
2225                        log::error!(
2226                            "error loading .gitignore file {:?} - {:?}",
2227                            child_name,
2228                            error
2229                        );
2230                    }
2231                }
2232
2233                // Update ignore status of any child entries we've already processed to reflect the
2234                // ignore file in the current directory. Because `.gitignore` starts with a `.`,
2235                // there should rarely be too numerous. Update the ignore stack associated with any
2236                // new jobs as well.
2237                let mut new_jobs = new_jobs.iter_mut();
2238                for entry in &mut new_entries {
2239                    let entry_abs_path = self.abs_path().join(&entry.path);
2240                    entry.is_ignored =
2241                        ignore_stack.is_abs_path_ignored(&entry_abs_path, entry.is_dir());
2242                    if entry.is_dir() {
2243                        new_jobs.next().unwrap().ignore_stack = if entry.is_ignored {
2244                            IgnoreStack::all()
2245                        } else {
2246                            ignore_stack.clone()
2247                        };
2248                    }
2249                }
2250            }
2251
2252            let mut child_entry = Entry::new(
2253                child_path.clone(),
2254                &child_metadata,
2255                &next_entry_id,
2256                root_char_bag,
2257            );
2258
2259            if child_entry.is_dir() {
2260                let is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, true);
2261                child_entry.is_ignored = is_ignored;
2262
2263                if !job.ancestor_inodes.contains(&child_entry.inode) {
2264                    let mut ancestor_inodes = job.ancestor_inodes.clone();
2265                    ancestor_inodes.insert(child_entry.inode);
2266                    new_jobs.push(ScanJob {
2267                        abs_path: child_abs_path,
2268                        path: child_path,
2269                        ignore_stack: if is_ignored {
2270                            IgnoreStack::all()
2271                        } else {
2272                            ignore_stack.clone()
2273                        },
2274                        ancestor_inodes,
2275                        scan_queue: job.scan_queue.clone(),
2276                    });
2277                }
2278            } else {
2279                child_entry.is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, false);
2280            }
2281
2282            new_entries.push(child_entry);
2283        }
2284
2285        self.snapshot
2286            .lock()
2287            .populate_dir(job.path.clone(), new_entries, new_ignore);
2288        for new_job in new_jobs {
2289            job.scan_queue.send(new_job).await.unwrap();
2290        }
2291
2292        Ok(())
2293    }
2294
2295    async fn process_events(&mut self, mut events: Vec<fsevent::Event>) -> bool {
2296        events.sort_unstable_by(|a, b| a.path.cmp(&b.path));
2297        events.dedup_by(|a, b| a.path.starts_with(&b.path));
2298
2299        let root_char_bag;
2300        let root_abs_path;
2301        let next_entry_id;
2302        {
2303            let snapshot = self.snapshot.lock();
2304            root_char_bag = snapshot.root_char_bag;
2305            root_abs_path = snapshot.abs_path.clone();
2306            next_entry_id = snapshot.next_entry_id.clone();
2307        }
2308
2309        let root_canonical_path = if let Ok(path) = self.fs.canonicalize(&root_abs_path).await {
2310            path
2311        } else {
2312            return false;
2313        };
2314        let metadata = futures::future::join_all(
2315            events
2316                .iter()
2317                .map(|event| self.fs.metadata(&event.path))
2318                .collect::<Vec<_>>(),
2319        )
2320        .await;
2321
2322        // Hold the snapshot lock while clearing and re-inserting the root entries
2323        // for each event. This way, the snapshot is not observable to the foreground
2324        // thread while this operation is in-progress.
2325        let (scan_queue_tx, scan_queue_rx) = channel::unbounded();
2326        {
2327            let mut snapshot = self.snapshot.lock();
2328            snapshot.scan_id += 1;
2329            for event in &events {
2330                if let Ok(path) = event.path.strip_prefix(&root_canonical_path) {
2331                    snapshot.remove_path(path);
2332                }
2333            }
2334
2335            for (event, metadata) in events.into_iter().zip(metadata.into_iter()) {
2336                let path: Arc<Path> = match event.path.strip_prefix(&root_canonical_path) {
2337                    Ok(path) => Arc::from(path.to_path_buf()),
2338                    Err(_) => {
2339                        log::error!(
2340                            "unexpected event {:?} for root path {:?}",
2341                            event.path,
2342                            root_canonical_path
2343                        );
2344                        continue;
2345                    }
2346                };
2347                let abs_path = root_abs_path.join(&path);
2348
2349                match metadata {
2350                    Ok(Some(metadata)) => {
2351                        let ignore_stack =
2352                            snapshot.ignore_stack_for_abs_path(&abs_path, metadata.is_dir);
2353                        let mut fs_entry = Entry::new(
2354                            path.clone(),
2355                            &metadata,
2356                            snapshot.next_entry_id.as_ref(),
2357                            snapshot.root_char_bag,
2358                        );
2359                        fs_entry.is_ignored = ignore_stack.is_all();
2360                        snapshot.insert_entry(fs_entry, self.fs.as_ref());
2361
2362                        let mut ancestor_inodes = snapshot.ancestor_inodes_for_path(&path);
2363                        if metadata.is_dir && !ancestor_inodes.contains(&metadata.inode) {
2364                            ancestor_inodes.insert(metadata.inode);
2365                            self.executor
2366                                .block(scan_queue_tx.send(ScanJob {
2367                                    abs_path,
2368                                    path,
2369                                    ignore_stack,
2370                                    ancestor_inodes,
2371                                    scan_queue: scan_queue_tx.clone(),
2372                                }))
2373                                .unwrap();
2374                        }
2375                    }
2376                    Ok(None) => {}
2377                    Err(err) => {
2378                        // TODO - create a special 'error' entry in the entries tree to mark this
2379                        log::error!("error reading file on event {:?}", err);
2380                    }
2381                }
2382            }
2383            drop(scan_queue_tx);
2384        }
2385
2386        // Scan any directories that were created as part of this event batch.
2387        self.executor
2388            .scoped(|scope| {
2389                for _ in 0..self.executor.num_cpus() {
2390                    scope.spawn(async {
2391                        while let Ok(job) = scan_queue_rx.recv().await {
2392                            if let Err(err) = self
2393                                .scan_dir(root_char_bag, next_entry_id.clone(), &job)
2394                                .await
2395                            {
2396                                log::error!("error scanning {:?}: {}", job.abs_path, err);
2397                            }
2398                        }
2399                    });
2400                }
2401            })
2402            .await;
2403
2404        // Attempt to detect renames only over a single batch of file-system events.
2405        self.snapshot.lock().removed_entry_ids.clear();
2406
2407        self.update_ignore_statuses().await;
2408        true
2409    }
2410
2411    async fn update_ignore_statuses(&self) {
2412        let mut snapshot = self.snapshot();
2413
2414        let mut ignores_to_update = Vec::new();
2415        let mut ignores_to_delete = Vec::new();
2416        for (parent_abs_path, (_, scan_id)) in &snapshot.ignores_by_parent_abs_path {
2417            if let Ok(parent_path) = parent_abs_path.strip_prefix(&snapshot.abs_path) {
2418                if *scan_id == snapshot.scan_id && snapshot.entry_for_path(parent_path).is_some() {
2419                    ignores_to_update.push(parent_abs_path.clone());
2420                }
2421
2422                let ignore_path = parent_path.join(&*GITIGNORE);
2423                if snapshot.entry_for_path(ignore_path).is_none() {
2424                    ignores_to_delete.push(parent_abs_path.clone());
2425                }
2426            }
2427        }
2428
2429        for parent_abs_path in ignores_to_delete {
2430            snapshot.ignores_by_parent_abs_path.remove(&parent_abs_path);
2431            self.snapshot
2432                .lock()
2433                .ignores_by_parent_abs_path
2434                .remove(&parent_abs_path);
2435        }
2436
2437        let (ignore_queue_tx, ignore_queue_rx) = channel::unbounded();
2438        ignores_to_update.sort_unstable();
2439        let mut ignores_to_update = ignores_to_update.into_iter().peekable();
2440        while let Some(parent_abs_path) = ignores_to_update.next() {
2441            while ignores_to_update
2442                .peek()
2443                .map_or(false, |p| p.starts_with(&parent_abs_path))
2444            {
2445                ignores_to_update.next().unwrap();
2446            }
2447
2448            let ignore_stack = snapshot.ignore_stack_for_abs_path(&parent_abs_path, true);
2449            ignore_queue_tx
2450                .send(UpdateIgnoreStatusJob {
2451                    abs_path: parent_abs_path,
2452                    ignore_stack,
2453                    ignore_queue: ignore_queue_tx.clone(),
2454                })
2455                .await
2456                .unwrap();
2457        }
2458        drop(ignore_queue_tx);
2459
2460        self.executor
2461            .scoped(|scope| {
2462                for _ in 0..self.executor.num_cpus() {
2463                    scope.spawn(async {
2464                        while let Ok(job) = ignore_queue_rx.recv().await {
2465                            self.update_ignore_status(job, &snapshot).await;
2466                        }
2467                    });
2468                }
2469            })
2470            .await;
2471    }
2472
2473    async fn update_ignore_status(&self, job: UpdateIgnoreStatusJob, snapshot: &LocalSnapshot) {
2474        let mut ignore_stack = job.ignore_stack;
2475        if let Some((ignore, _)) = snapshot.ignores_by_parent_abs_path.get(&job.abs_path) {
2476            ignore_stack = ignore_stack.append(job.abs_path.clone(), ignore.clone());
2477        }
2478
2479        let mut entries_by_id_edits = Vec::new();
2480        let mut entries_by_path_edits = Vec::new();
2481        let path = job.abs_path.strip_prefix(&snapshot.abs_path).unwrap();
2482        for mut entry in snapshot.child_entries(path).cloned() {
2483            let was_ignored = entry.is_ignored;
2484            let abs_path = self.abs_path().join(&entry.path);
2485            entry.is_ignored = ignore_stack.is_abs_path_ignored(&abs_path, entry.is_dir());
2486            if entry.is_dir() {
2487                let child_ignore_stack = if entry.is_ignored {
2488                    IgnoreStack::all()
2489                } else {
2490                    ignore_stack.clone()
2491                };
2492                job.ignore_queue
2493                    .send(UpdateIgnoreStatusJob {
2494                        abs_path: abs_path.into(),
2495                        ignore_stack: child_ignore_stack,
2496                        ignore_queue: job.ignore_queue.clone(),
2497                    })
2498                    .await
2499                    .unwrap();
2500            }
2501
2502            if entry.is_ignored != was_ignored {
2503                let mut path_entry = snapshot.entries_by_id.get(&entry.id, &()).unwrap().clone();
2504                path_entry.scan_id = snapshot.scan_id;
2505                path_entry.is_ignored = entry.is_ignored;
2506                entries_by_id_edits.push(Edit::Insert(path_entry));
2507                entries_by_path_edits.push(Edit::Insert(entry));
2508            }
2509        }
2510
2511        let mut snapshot = self.snapshot.lock();
2512        snapshot.entries_by_path.edit(entries_by_path_edits, &());
2513        snapshot.entries_by_id.edit(entries_by_id_edits, &());
2514    }
2515}
2516
2517fn char_bag_for_path(root_char_bag: CharBag, path: &Path) -> CharBag {
2518    let mut result = root_char_bag;
2519    result.extend(
2520        path.to_string_lossy()
2521            .chars()
2522            .map(|c| c.to_ascii_lowercase()),
2523    );
2524    result
2525}
2526
2527struct ScanJob {
2528    abs_path: PathBuf,
2529    path: Arc<Path>,
2530    ignore_stack: Arc<IgnoreStack>,
2531    scan_queue: Sender<ScanJob>,
2532    ancestor_inodes: TreeSet<u64>,
2533}
2534
2535struct UpdateIgnoreStatusJob {
2536    abs_path: Arc<Path>,
2537    ignore_stack: Arc<IgnoreStack>,
2538    ignore_queue: Sender<UpdateIgnoreStatusJob>,
2539}
2540
2541pub trait WorktreeHandle {
2542    #[cfg(any(test, feature = "test-support"))]
2543    fn flush_fs_events<'a>(
2544        &self,
2545        cx: &'a gpui::TestAppContext,
2546    ) -> futures::future::LocalBoxFuture<'a, ()>;
2547}
2548
2549impl WorktreeHandle for ModelHandle<Worktree> {
2550    // When the worktree's FS event stream sometimes delivers "redundant" events for FS changes that
2551    // occurred before the worktree was constructed. These events can cause the worktree to perfrom
2552    // extra directory scans, and emit extra scan-state notifications.
2553    //
2554    // This function mutates the worktree's directory and waits for those mutations to be picked up,
2555    // to ensure that all redundant FS events have already been processed.
2556    #[cfg(any(test, feature = "test-support"))]
2557    fn flush_fs_events<'a>(
2558        &self,
2559        cx: &'a gpui::TestAppContext,
2560    ) -> futures::future::LocalBoxFuture<'a, ()> {
2561        use smol::future::FutureExt;
2562
2563        let filename = "fs-event-sentinel";
2564        let tree = self.clone();
2565        let (fs, root_path) = self.read_with(cx, |tree, _| {
2566            let tree = tree.as_local().unwrap();
2567            (tree.fs.clone(), tree.abs_path().clone())
2568        });
2569
2570        async move {
2571            fs.create_file(&root_path.join(filename), Default::default())
2572                .await
2573                .unwrap();
2574            tree.condition(cx, |tree, _| tree.entry_for_path(filename).is_some())
2575                .await;
2576
2577            fs.remove_file(&root_path.join(filename), Default::default())
2578                .await
2579                .unwrap();
2580            tree.condition(cx, |tree, _| tree.entry_for_path(filename).is_none())
2581                .await;
2582
2583            cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2584                .await;
2585        }
2586        .boxed_local()
2587    }
2588}
2589
2590#[derive(Clone, Debug)]
2591struct TraversalProgress<'a> {
2592    max_path: &'a Path,
2593    count: usize,
2594    visible_count: usize,
2595    file_count: usize,
2596    visible_file_count: usize,
2597}
2598
2599impl<'a> TraversalProgress<'a> {
2600    fn count(&self, include_dirs: bool, include_ignored: bool) -> usize {
2601        match (include_ignored, include_dirs) {
2602            (true, true) => self.count,
2603            (true, false) => self.file_count,
2604            (false, true) => self.visible_count,
2605            (false, false) => self.visible_file_count,
2606        }
2607    }
2608}
2609
2610impl<'a> sum_tree::Dimension<'a, EntrySummary> for TraversalProgress<'a> {
2611    fn add_summary(&mut self, summary: &'a EntrySummary, _: &()) {
2612        self.max_path = summary.max_path.as_ref();
2613        self.count += summary.count;
2614        self.visible_count += summary.visible_count;
2615        self.file_count += summary.file_count;
2616        self.visible_file_count += summary.visible_file_count;
2617    }
2618}
2619
2620impl<'a> Default for TraversalProgress<'a> {
2621    fn default() -> Self {
2622        Self {
2623            max_path: Path::new(""),
2624            count: 0,
2625            visible_count: 0,
2626            file_count: 0,
2627            visible_file_count: 0,
2628        }
2629    }
2630}
2631
2632pub struct Traversal<'a> {
2633    cursor: sum_tree::Cursor<'a, Entry, TraversalProgress<'a>>,
2634    include_ignored: bool,
2635    include_dirs: bool,
2636}
2637
2638impl<'a> Traversal<'a> {
2639    pub fn advance(&mut self) -> bool {
2640        self.advance_to_offset(self.offset() + 1)
2641    }
2642
2643    pub fn advance_to_offset(&mut self, offset: usize) -> bool {
2644        self.cursor.seek_forward(
2645            &TraversalTarget::Count {
2646                count: offset,
2647                include_dirs: self.include_dirs,
2648                include_ignored: self.include_ignored,
2649            },
2650            Bias::Right,
2651            &(),
2652        )
2653    }
2654
2655    pub fn advance_to_sibling(&mut self) -> bool {
2656        while let Some(entry) = self.cursor.item() {
2657            self.cursor.seek_forward(
2658                &TraversalTarget::PathSuccessor(&entry.path),
2659                Bias::Left,
2660                &(),
2661            );
2662            if let Some(entry) = self.cursor.item() {
2663                if (self.include_dirs || !entry.is_dir())
2664                    && (self.include_ignored || !entry.is_ignored)
2665                {
2666                    return true;
2667                }
2668            }
2669        }
2670        false
2671    }
2672
2673    pub fn entry(&self) -> Option<&'a Entry> {
2674        self.cursor.item()
2675    }
2676
2677    pub fn offset(&self) -> usize {
2678        self.cursor
2679            .start()
2680            .count(self.include_dirs, self.include_ignored)
2681    }
2682}
2683
2684impl<'a> Iterator for Traversal<'a> {
2685    type Item = &'a Entry;
2686
2687    fn next(&mut self) -> Option<Self::Item> {
2688        if let Some(item) = self.entry() {
2689            self.advance();
2690            Some(item)
2691        } else {
2692            None
2693        }
2694    }
2695}
2696
2697#[derive(Debug)]
2698enum TraversalTarget<'a> {
2699    Path(&'a Path),
2700    PathSuccessor(&'a Path),
2701    Count {
2702        count: usize,
2703        include_ignored: bool,
2704        include_dirs: bool,
2705    },
2706}
2707
2708impl<'a, 'b> SeekTarget<'a, EntrySummary, TraversalProgress<'a>> for TraversalTarget<'b> {
2709    fn cmp(&self, cursor_location: &TraversalProgress<'a>, _: &()) -> Ordering {
2710        match self {
2711            TraversalTarget::Path(path) => path.cmp(&cursor_location.max_path),
2712            TraversalTarget::PathSuccessor(path) => {
2713                if !cursor_location.max_path.starts_with(path) {
2714                    Ordering::Equal
2715                } else {
2716                    Ordering::Greater
2717                }
2718            }
2719            TraversalTarget::Count {
2720                count,
2721                include_dirs,
2722                include_ignored,
2723            } => Ord::cmp(
2724                count,
2725                &cursor_location.count(*include_dirs, *include_ignored),
2726            ),
2727        }
2728    }
2729}
2730
2731struct ChildEntriesIter<'a> {
2732    parent_path: &'a Path,
2733    traversal: Traversal<'a>,
2734}
2735
2736impl<'a> Iterator for ChildEntriesIter<'a> {
2737    type Item = &'a Entry;
2738
2739    fn next(&mut self) -> Option<Self::Item> {
2740        if let Some(item) = self.traversal.entry() {
2741            if item.path.starts_with(&self.parent_path) {
2742                self.traversal.advance_to_sibling();
2743                return Some(item);
2744            }
2745        }
2746        None
2747    }
2748}
2749
2750impl<'a> From<&'a Entry> for proto::Entry {
2751    fn from(entry: &'a Entry) -> Self {
2752        Self {
2753            id: entry.id.to_proto(),
2754            is_dir: entry.is_dir(),
2755            path: entry.path.as_os_str().as_bytes().to_vec(),
2756            inode: entry.inode,
2757            mtime: Some(entry.mtime.into()),
2758            is_symlink: entry.is_symlink,
2759            is_ignored: entry.is_ignored,
2760        }
2761    }
2762}
2763
2764impl<'a> TryFrom<(&'a CharBag, proto::Entry)> for Entry {
2765    type Error = anyhow::Error;
2766
2767    fn try_from((root_char_bag, entry): (&'a CharBag, proto::Entry)) -> Result<Self> {
2768        if let Some(mtime) = entry.mtime {
2769            let kind = if entry.is_dir {
2770                EntryKind::Dir
2771            } else {
2772                let mut char_bag = *root_char_bag;
2773                char_bag.extend(
2774                    String::from_utf8_lossy(&entry.path)
2775                        .chars()
2776                        .map(|c| c.to_ascii_lowercase()),
2777                );
2778                EntryKind::File(char_bag)
2779            };
2780            let path: Arc<Path> = PathBuf::from(OsString::from_vec(entry.path)).into();
2781            Ok(Entry {
2782                id: ProjectEntryId::from_proto(entry.id),
2783                kind,
2784                path,
2785                inode: entry.inode,
2786                mtime: mtime.into(),
2787                is_symlink: entry.is_symlink,
2788                is_ignored: entry.is_ignored,
2789            })
2790        } else {
2791            Err(anyhow!(
2792                "missing mtime in remote worktree entry {:?}",
2793                entry.path
2794            ))
2795        }
2796    }
2797}
2798
2799async fn send_worktree_update(client: &Arc<Client>, update: proto::UpdateWorktree) -> Result<()> {
2800    #[cfg(any(test, feature = "test-support"))]
2801    const MAX_CHUNK_SIZE: usize = 2;
2802    #[cfg(not(any(test, feature = "test-support")))]
2803    const MAX_CHUNK_SIZE: usize = 256;
2804
2805    for update in proto::split_worktree_update(update, MAX_CHUNK_SIZE) {
2806        client.request(update).await?;
2807    }
2808
2809    Ok(())
2810}
2811
2812#[cfg(test)]
2813mod tests {
2814    use super::*;
2815    use crate::fs::FakeFs;
2816    use anyhow::Result;
2817    use client::test::FakeHttpClient;
2818    use fs::RealFs;
2819    use gpui::{executor::Deterministic, TestAppContext};
2820    use rand::prelude::*;
2821    use serde_json::json;
2822    use std::{
2823        env,
2824        fmt::Write,
2825        time::{SystemTime, UNIX_EPOCH},
2826    };
2827    use util::test::temp_tree;
2828
2829    #[gpui::test]
2830    async fn test_traversal(cx: &mut TestAppContext) {
2831        let fs = FakeFs::new(cx.background());
2832        fs.insert_tree(
2833            "/root",
2834            json!({
2835               ".gitignore": "a/b\n",
2836               "a": {
2837                   "b": "",
2838                   "c": "",
2839               }
2840            }),
2841        )
2842        .await;
2843
2844        let http_client = FakeHttpClient::with_404_response();
2845        let client = cx.read(|cx| Client::new(http_client, cx));
2846
2847        let tree = Worktree::local(
2848            client,
2849            Arc::from(Path::new("/root")),
2850            true,
2851            fs,
2852            Default::default(),
2853            &mut cx.to_async(),
2854        )
2855        .await
2856        .unwrap();
2857        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2858            .await;
2859
2860        tree.read_with(cx, |tree, _| {
2861            assert_eq!(
2862                tree.entries(false)
2863                    .map(|entry| entry.path.as_ref())
2864                    .collect::<Vec<_>>(),
2865                vec![
2866                    Path::new(""),
2867                    Path::new(".gitignore"),
2868                    Path::new("a"),
2869                    Path::new("a/c"),
2870                ]
2871            );
2872            assert_eq!(
2873                tree.entries(true)
2874                    .map(|entry| entry.path.as_ref())
2875                    .collect::<Vec<_>>(),
2876                vec![
2877                    Path::new(""),
2878                    Path::new(".gitignore"),
2879                    Path::new("a"),
2880                    Path::new("a/b"),
2881                    Path::new("a/c"),
2882                ]
2883            );
2884        })
2885    }
2886
2887    #[gpui::test(iterations = 10)]
2888    async fn test_circular_symlinks(executor: Arc<Deterministic>, cx: &mut TestAppContext) {
2889        let fs = FakeFs::new(cx.background());
2890        fs.insert_tree(
2891            "/root",
2892            json!({
2893                "lib": {
2894                    "a": {
2895                        "a.txt": ""
2896                    },
2897                    "b": {
2898                        "b.txt": ""
2899                    }
2900                }
2901            }),
2902        )
2903        .await;
2904        fs.insert_symlink("/root/lib/a/lib", "..".into()).await;
2905        fs.insert_symlink("/root/lib/b/lib", "..".into()).await;
2906
2907        let client = cx.read(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
2908        let tree = Worktree::local(
2909            client,
2910            Arc::from(Path::new("/root")),
2911            true,
2912            fs.clone(),
2913            Default::default(),
2914            &mut cx.to_async(),
2915        )
2916        .await
2917        .unwrap();
2918
2919        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2920            .await;
2921
2922        tree.read_with(cx, |tree, _| {
2923            assert_eq!(
2924                tree.entries(false)
2925                    .map(|entry| entry.path.as_ref())
2926                    .collect::<Vec<_>>(),
2927                vec![
2928                    Path::new(""),
2929                    Path::new("lib"),
2930                    Path::new("lib/a"),
2931                    Path::new("lib/a/a.txt"),
2932                    Path::new("lib/a/lib"),
2933                    Path::new("lib/b"),
2934                    Path::new("lib/b/b.txt"),
2935                    Path::new("lib/b/lib"),
2936                ]
2937            );
2938        });
2939
2940        fs.rename(
2941            Path::new("/root/lib/a/lib"),
2942            Path::new("/root/lib/a/lib-2"),
2943            Default::default(),
2944        )
2945        .await
2946        .unwrap();
2947        executor.run_until_parked();
2948        tree.read_with(cx, |tree, _| {
2949            assert_eq!(
2950                tree.entries(false)
2951                    .map(|entry| entry.path.as_ref())
2952                    .collect::<Vec<_>>(),
2953                vec![
2954                    Path::new(""),
2955                    Path::new("lib"),
2956                    Path::new("lib/a"),
2957                    Path::new("lib/a/a.txt"),
2958                    Path::new("lib/a/lib-2"),
2959                    Path::new("lib/b"),
2960                    Path::new("lib/b/b.txt"),
2961                    Path::new("lib/b/lib"),
2962                ]
2963            );
2964        });
2965    }
2966
2967    #[gpui::test]
2968    async fn test_rescan_with_gitignore(cx: &mut TestAppContext) {
2969        let parent_dir = temp_tree(json!({
2970            ".gitignore": "ancestor-ignored-file1\nancestor-ignored-file2\n",
2971            "tree": {
2972                ".git": {},
2973                ".gitignore": "ignored-dir\n",
2974                "tracked-dir": {
2975                    "tracked-file1": "",
2976                    "ancestor-ignored-file1": "",
2977                },
2978                "ignored-dir": {
2979                    "ignored-file1": ""
2980                }
2981            }
2982        }));
2983        let dir = parent_dir.path().join("tree");
2984
2985        let client = cx.read(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
2986
2987        let tree = Worktree::local(
2988            client,
2989            dir.as_path(),
2990            true,
2991            Arc::new(RealFs),
2992            Default::default(),
2993            &mut cx.to_async(),
2994        )
2995        .await
2996        .unwrap();
2997        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2998            .await;
2999        tree.flush_fs_events(cx).await;
3000        cx.read(|cx| {
3001            let tree = tree.read(cx);
3002            assert!(
3003                !tree
3004                    .entry_for_path("tracked-dir/tracked-file1")
3005                    .unwrap()
3006                    .is_ignored
3007            );
3008            assert!(
3009                tree.entry_for_path("tracked-dir/ancestor-ignored-file1")
3010                    .unwrap()
3011                    .is_ignored
3012            );
3013            assert!(
3014                tree.entry_for_path("ignored-dir/ignored-file1")
3015                    .unwrap()
3016                    .is_ignored
3017            );
3018        });
3019
3020        std::fs::write(dir.join("tracked-dir/tracked-file2"), "").unwrap();
3021        std::fs::write(dir.join("tracked-dir/ancestor-ignored-file2"), "").unwrap();
3022        std::fs::write(dir.join("ignored-dir/ignored-file2"), "").unwrap();
3023        tree.flush_fs_events(cx).await;
3024        cx.read(|cx| {
3025            let tree = tree.read(cx);
3026            assert!(
3027                !tree
3028                    .entry_for_path("tracked-dir/tracked-file2")
3029                    .unwrap()
3030                    .is_ignored
3031            );
3032            assert!(
3033                tree.entry_for_path("tracked-dir/ancestor-ignored-file2")
3034                    .unwrap()
3035                    .is_ignored
3036            );
3037            assert!(
3038                tree.entry_for_path("ignored-dir/ignored-file2")
3039                    .unwrap()
3040                    .is_ignored
3041            );
3042            assert!(tree.entry_for_path(".git").unwrap().is_ignored);
3043        });
3044    }
3045
3046    #[gpui::test]
3047    async fn test_write_file(cx: &mut TestAppContext) {
3048        let dir = temp_tree(json!({
3049            ".git": {},
3050            ".gitignore": "ignored-dir\n",
3051            "tracked-dir": {},
3052            "ignored-dir": {}
3053        }));
3054
3055        let client = cx.read(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
3056
3057        let tree = Worktree::local(
3058            client,
3059            dir.path(),
3060            true,
3061            Arc::new(RealFs),
3062            Default::default(),
3063            &mut cx.to_async(),
3064        )
3065        .await
3066        .unwrap();
3067        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
3068            .await;
3069        tree.flush_fs_events(cx).await;
3070
3071        tree.update(cx, |tree, cx| {
3072            tree.as_local().unwrap().write_file(
3073                Path::new("tracked-dir/file.txt"),
3074                "hello".into(),
3075                Default::default(),
3076                cx,
3077            )
3078        })
3079        .await
3080        .unwrap();
3081        tree.update(cx, |tree, cx| {
3082            tree.as_local().unwrap().write_file(
3083                Path::new("ignored-dir/file.txt"),
3084                "world".into(),
3085                Default::default(),
3086                cx,
3087            )
3088        })
3089        .await
3090        .unwrap();
3091
3092        tree.read_with(cx, |tree, _| {
3093            let tracked = tree.entry_for_path("tracked-dir/file.txt").unwrap();
3094            let ignored = tree.entry_for_path("ignored-dir/file.txt").unwrap();
3095            assert!(!tracked.is_ignored);
3096            assert!(ignored.is_ignored);
3097        });
3098    }
3099
3100    #[gpui::test(iterations = 30)]
3101    async fn test_create_directory(cx: &mut TestAppContext) {
3102        let client = cx.read(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
3103
3104        let fs = FakeFs::new(cx.background());
3105        fs.insert_tree(
3106            "/a",
3107            json!({
3108                "b": {},
3109                "c": {},
3110                "d": {},
3111            }),
3112        )
3113        .await;
3114
3115        let tree = Worktree::local(
3116            client,
3117            "/a".as_ref(),
3118            true,
3119            fs,
3120            Default::default(),
3121            &mut cx.to_async(),
3122        )
3123        .await
3124        .unwrap();
3125
3126        let entry = tree
3127            .update(cx, |tree, cx| {
3128                tree.as_local_mut()
3129                    .unwrap()
3130                    .create_entry("a/e".as_ref(), true, cx)
3131            })
3132            .await
3133            .unwrap();
3134        assert!(entry.is_dir());
3135
3136        cx.foreground().run_until_parked();
3137        tree.read_with(cx, |tree, _| {
3138            assert_eq!(tree.entry_for_path("a/e").unwrap().kind, EntryKind::Dir);
3139        });
3140    }
3141
3142    #[gpui::test(iterations = 100)]
3143    fn test_random(mut rng: StdRng) {
3144        let operations = env::var("OPERATIONS")
3145            .map(|o| o.parse().unwrap())
3146            .unwrap_or(40);
3147        let initial_entries = env::var("INITIAL_ENTRIES")
3148            .map(|o| o.parse().unwrap())
3149            .unwrap_or(20);
3150
3151        let root_dir = tempdir::TempDir::new("worktree-test").unwrap();
3152        for _ in 0..initial_entries {
3153            randomly_mutate_tree(root_dir.path(), 1.0, &mut rng).unwrap();
3154        }
3155        log::info!("Generated initial tree");
3156
3157        let (notify_tx, _notify_rx) = mpsc::unbounded();
3158        let fs = Arc::new(RealFs);
3159        let next_entry_id = Arc::new(AtomicUsize::new(0));
3160        let mut initial_snapshot = LocalSnapshot {
3161            abs_path: root_dir.path().into(),
3162            removed_entry_ids: Default::default(),
3163            ignores_by_parent_abs_path: Default::default(),
3164            next_entry_id: next_entry_id.clone(),
3165            snapshot: Snapshot {
3166                id: WorktreeId::from_usize(0),
3167                entries_by_path: Default::default(),
3168                entries_by_id: Default::default(),
3169                root_name: Default::default(),
3170                root_char_bag: Default::default(),
3171                scan_id: 0,
3172                is_complete: true,
3173            },
3174            extension_counts: Default::default(),
3175        };
3176        initial_snapshot.insert_entry(
3177            Entry::new(
3178                Path::new("").into(),
3179                &smol::block_on(fs.metadata(root_dir.path()))
3180                    .unwrap()
3181                    .unwrap(),
3182                &next_entry_id,
3183                Default::default(),
3184            ),
3185            fs.as_ref(),
3186        );
3187        let mut scanner = BackgroundScanner::new(
3188            Arc::new(Mutex::new(initial_snapshot.clone())),
3189            notify_tx,
3190            fs.clone(),
3191            Arc::new(gpui::executor::Background::new()),
3192        );
3193        smol::block_on(scanner.scan_dirs()).unwrap();
3194        scanner.snapshot().check_invariants();
3195
3196        let mut events = Vec::new();
3197        let mut snapshots = Vec::new();
3198        let mut mutations_len = operations;
3199        while mutations_len > 1 {
3200            if !events.is_empty() && rng.gen_bool(0.4) {
3201                let len = rng.gen_range(0..=events.len());
3202                let to_deliver = events.drain(0..len).collect::<Vec<_>>();
3203                log::info!("Delivering events: {:#?}", to_deliver);
3204                smol::block_on(scanner.process_events(to_deliver));
3205                scanner.snapshot().check_invariants();
3206            } else {
3207                events.extend(randomly_mutate_tree(root_dir.path(), 0.6, &mut rng).unwrap());
3208                mutations_len -= 1;
3209            }
3210
3211            if rng.gen_bool(0.2) {
3212                snapshots.push(scanner.snapshot());
3213            }
3214        }
3215        log::info!("Quiescing: {:#?}", events);
3216        smol::block_on(scanner.process_events(events));
3217        scanner.snapshot().check_invariants();
3218
3219        let (notify_tx, _notify_rx) = mpsc::unbounded();
3220        let mut new_scanner = BackgroundScanner::new(
3221            Arc::new(Mutex::new(initial_snapshot)),
3222            notify_tx,
3223            scanner.fs.clone(),
3224            scanner.executor.clone(),
3225        );
3226        smol::block_on(new_scanner.scan_dirs()).unwrap();
3227        assert_eq!(
3228            scanner.snapshot().to_vec(true),
3229            new_scanner.snapshot().to_vec(true)
3230        );
3231
3232        for mut prev_snapshot in snapshots {
3233            let include_ignored = rng.gen::<bool>();
3234            if !include_ignored {
3235                let mut entries_by_path_edits = Vec::new();
3236                let mut entries_by_id_edits = Vec::new();
3237                for entry in prev_snapshot
3238                    .entries_by_id
3239                    .cursor::<()>()
3240                    .filter(|e| e.is_ignored)
3241                {
3242                    entries_by_path_edits.push(Edit::Remove(PathKey(entry.path.clone())));
3243                    entries_by_id_edits.push(Edit::Remove(entry.id));
3244                }
3245
3246                prev_snapshot
3247                    .entries_by_path
3248                    .edit(entries_by_path_edits, &());
3249                prev_snapshot.entries_by_id.edit(entries_by_id_edits, &());
3250            }
3251
3252            let update = scanner
3253                .snapshot()
3254                .build_update(&prev_snapshot, 0, 0, include_ignored);
3255            prev_snapshot.apply_remote_update(update).unwrap();
3256            assert_eq!(
3257                prev_snapshot.to_vec(true),
3258                scanner.snapshot().to_vec(include_ignored)
3259            );
3260        }
3261    }
3262
3263    fn randomly_mutate_tree(
3264        root_path: &Path,
3265        insertion_probability: f64,
3266        rng: &mut impl Rng,
3267    ) -> Result<Vec<fsevent::Event>> {
3268        let root_path = root_path.canonicalize().unwrap();
3269        let (dirs, files) = read_dir_recursive(root_path.clone());
3270
3271        let mut events = Vec::new();
3272        let mut record_event = |path: PathBuf| {
3273            events.push(fsevent::Event {
3274                event_id: SystemTime::now()
3275                    .duration_since(UNIX_EPOCH)
3276                    .unwrap()
3277                    .as_secs(),
3278                flags: fsevent::StreamFlags::empty(),
3279                path,
3280            });
3281        };
3282
3283        if (files.is_empty() && dirs.len() == 1) || rng.gen_bool(insertion_probability) {
3284            let path = dirs.choose(rng).unwrap();
3285            let new_path = path.join(gen_name(rng));
3286
3287            if rng.gen() {
3288                log::info!("Creating dir {:?}", new_path.strip_prefix(root_path)?);
3289                std::fs::create_dir(&new_path)?;
3290            } else {
3291                log::info!("Creating file {:?}", new_path.strip_prefix(root_path)?);
3292                std::fs::write(&new_path, "")?;
3293            }
3294            record_event(new_path);
3295        } else if rng.gen_bool(0.05) {
3296            let ignore_dir_path = dirs.choose(rng).unwrap();
3297            let ignore_path = ignore_dir_path.join(&*GITIGNORE);
3298
3299            let (subdirs, subfiles) = read_dir_recursive(ignore_dir_path.clone());
3300            let files_to_ignore = {
3301                let len = rng.gen_range(0..=subfiles.len());
3302                subfiles.choose_multiple(rng, len)
3303            };
3304            let dirs_to_ignore = {
3305                let len = rng.gen_range(0..subdirs.len());
3306                subdirs.choose_multiple(rng, len)
3307            };
3308
3309            let mut ignore_contents = String::new();
3310            for path_to_ignore in files_to_ignore.chain(dirs_to_ignore) {
3311                writeln!(
3312                    ignore_contents,
3313                    "{}",
3314                    path_to_ignore
3315                        .strip_prefix(&ignore_dir_path)?
3316                        .to_str()
3317                        .unwrap()
3318                )
3319                .unwrap();
3320            }
3321            log::info!(
3322                "Creating {:?} with contents:\n{}",
3323                ignore_path.strip_prefix(&root_path)?,
3324                ignore_contents
3325            );
3326            std::fs::write(&ignore_path, ignore_contents).unwrap();
3327            record_event(ignore_path);
3328        } else {
3329            let old_path = {
3330                let file_path = files.choose(rng);
3331                let dir_path = dirs[1..].choose(rng);
3332                file_path.into_iter().chain(dir_path).choose(rng).unwrap()
3333            };
3334
3335            let is_rename = rng.gen();
3336            if is_rename {
3337                let new_path_parent = dirs
3338                    .iter()
3339                    .filter(|d| !d.starts_with(old_path))
3340                    .choose(rng)
3341                    .unwrap();
3342
3343                let overwrite_existing_dir =
3344                    !old_path.starts_with(&new_path_parent) && rng.gen_bool(0.3);
3345                let new_path = if overwrite_existing_dir {
3346                    std::fs::remove_dir_all(&new_path_parent).ok();
3347                    new_path_parent.to_path_buf()
3348                } else {
3349                    new_path_parent.join(gen_name(rng))
3350                };
3351
3352                log::info!(
3353                    "Renaming {:?} to {}{:?}",
3354                    old_path.strip_prefix(&root_path)?,
3355                    if overwrite_existing_dir {
3356                        "overwrite "
3357                    } else {
3358                        ""
3359                    },
3360                    new_path.strip_prefix(&root_path)?
3361                );
3362                std::fs::rename(&old_path, &new_path)?;
3363                record_event(old_path.clone());
3364                record_event(new_path);
3365            } else if old_path.is_dir() {
3366                let (dirs, files) = read_dir_recursive(old_path.clone());
3367
3368                log::info!("Deleting dir {:?}", old_path.strip_prefix(&root_path)?);
3369                std::fs::remove_dir_all(&old_path).unwrap();
3370                for file in files {
3371                    record_event(file);
3372                }
3373                for dir in dirs {
3374                    record_event(dir);
3375                }
3376            } else {
3377                log::info!("Deleting file {:?}", old_path.strip_prefix(&root_path)?);
3378                std::fs::remove_file(old_path).unwrap();
3379                record_event(old_path.clone());
3380            }
3381        }
3382
3383        Ok(events)
3384    }
3385
3386    fn read_dir_recursive(path: PathBuf) -> (Vec<PathBuf>, Vec<PathBuf>) {
3387        let child_entries = std::fs::read_dir(&path).unwrap();
3388        let mut dirs = vec![path];
3389        let mut files = Vec::new();
3390        for child_entry in child_entries {
3391            let child_path = child_entry.unwrap().path();
3392            if child_path.is_dir() {
3393                let (child_dirs, child_files) = read_dir_recursive(child_path);
3394                dirs.extend(child_dirs);
3395                files.extend(child_files);
3396            } else {
3397                files.push(child_path);
3398            }
3399        }
3400        (dirs, files)
3401    }
3402
3403    fn gen_name(rng: &mut impl Rng) -> String {
3404        (0..6)
3405            .map(|_| rng.sample(rand::distributions::Alphanumeric))
3406            .map(char::from)
3407            .collect()
3408    }
3409
3410    impl LocalSnapshot {
3411        fn check_invariants(&self) {
3412            let mut files = self.files(true, 0);
3413            let mut visible_files = self.files(false, 0);
3414            for entry in self.entries_by_path.cursor::<()>() {
3415                if entry.is_file() {
3416                    assert_eq!(files.next().unwrap().inode, entry.inode);
3417                    if !entry.is_ignored {
3418                        assert_eq!(visible_files.next().unwrap().inode, entry.inode);
3419                    }
3420                }
3421            }
3422            assert!(files.next().is_none());
3423            assert!(visible_files.next().is_none());
3424
3425            let mut bfs_paths = Vec::new();
3426            let mut stack = vec![Path::new("")];
3427            while let Some(path) = stack.pop() {
3428                bfs_paths.push(path);
3429                let ix = stack.len();
3430                for child_entry in self.child_entries(path) {
3431                    stack.insert(ix, &child_entry.path);
3432                }
3433            }
3434
3435            let dfs_paths_via_iter = self
3436                .entries_by_path
3437                .cursor::<()>()
3438                .map(|e| e.path.as_ref())
3439                .collect::<Vec<_>>();
3440            assert_eq!(bfs_paths, dfs_paths_via_iter);
3441
3442            let dfs_paths_via_traversal = self
3443                .entries(true)
3444                .map(|e| e.path.as_ref())
3445                .collect::<Vec<_>>();
3446            assert_eq!(dfs_paths_via_traversal, dfs_paths_via_iter);
3447
3448            for ignore_parent_abs_path in self.ignores_by_parent_abs_path.keys() {
3449                let ignore_parent_path =
3450                    ignore_parent_abs_path.strip_prefix(&self.abs_path).unwrap();
3451                assert!(self.entry_for_path(&ignore_parent_path).is_some());
3452                assert!(self
3453                    .entry_for_path(ignore_parent_path.join(&*GITIGNORE))
3454                    .is_some());
3455            }
3456
3457            // Ensure extension counts are correct.
3458            let mut expected_extension_counts = HashMap::default();
3459            for extension in self.entries(false).filter_map(|e| e.path.extension()) {
3460                *expected_extension_counts
3461                    .entry(extension.into())
3462                    .or_insert(0) += 1;
3463            }
3464            assert_eq!(self.extension_counts, expected_extension_counts);
3465        }
3466
3467        fn to_vec(&self, include_ignored: bool) -> Vec<(&Path, u64, bool)> {
3468            let mut paths = Vec::new();
3469            for entry in self.entries_by_path.cursor::<()>() {
3470                if include_ignored || !entry.is_ignored {
3471                    paths.push((entry.path.as_ref(), entry.inode, entry.is_ignored));
3472                }
3473            }
3474            paths.sort_by(|a, b| a.0.cmp(b.0));
3475            paths
3476        }
3477    }
3478}