worktree.rs

   1mod char_bag;
   2mod fuzzy;
   3
   4use crate::{
   5    editor::{History, Snapshot as BufferSnapshot},
   6    sum_tree::{self, Edit, SumTree},
   7};
   8use anyhow::{anyhow, Result};
   9pub use fuzzy::match_paths;
  10use fuzzy::PathEntry;
  11use gpui::{scoped_pool, AppContext, Entity, ModelContext, ModelHandle, Task};
  12use ignore::dir::{Ignore, IgnoreBuilder};
  13use parking_lot::Mutex;
  14use smol::{channel::Sender, Timer};
  15use std::future::Future;
  16use std::{
  17    ffi::OsStr,
  18    fmt, fs,
  19    io::{self, Read, Write},
  20    ops::{AddAssign, Deref},
  21    os::unix::fs::MetadataExt,
  22    path::{Path, PathBuf},
  23    sync::Arc,
  24    time::Duration,
  25};
  26
  27pub use fuzzy::PathMatch;
  28
  29#[derive(Debug)]
  30enum ScanState {
  31    Idle,
  32    Scanning,
  33    Err(io::Error),
  34}
  35
  36pub struct Worktree {
  37    snapshot: Snapshot,
  38    scanner: Arc<BackgroundScanner>,
  39    scan_state: ScanState,
  40    poll_scheduled: bool,
  41}
  42
  43#[derive(Clone)]
  44pub struct FileHandle {
  45    worktree: ModelHandle<Worktree>,
  46    inode: u64,
  47}
  48
  49impl Worktree {
  50    pub fn new(path: impl Into<Arc<Path>>, ctx: &mut ModelContext<Self>) -> Self {
  51        let scan_state = smol::channel::unbounded();
  52        let snapshot = Snapshot {
  53            id: ctx.model_id(),
  54            path: path.into(),
  55            root_inode: None,
  56            entries: Default::default(),
  57        };
  58        let scanner = Arc::new(BackgroundScanner::new(snapshot.clone(), scan_state.0));
  59        let tree = Self {
  60            snapshot,
  61            scanner,
  62            scan_state: ScanState::Idle,
  63            poll_scheduled: false,
  64        };
  65
  66        let scanner = tree.scanner.clone();
  67        std::thread::spawn(move || scanner.run());
  68
  69        ctx.spawn_stream(scan_state.1, Self::observe_scan_state, |_, _| {})
  70            .detach();
  71
  72        tree
  73    }
  74
  75    fn observe_scan_state(&mut self, scan_state: ScanState, ctx: &mut ModelContext<Self>) {
  76        self.scan_state = scan_state;
  77        self.poll_entries(ctx);
  78    }
  79
  80    fn poll_entries(&mut self, ctx: &mut ModelContext<Self>) {
  81        self.snapshot = self.scanner.snapshot();
  82        ctx.notify();
  83
  84        if self.is_scanning() && !self.poll_scheduled {
  85            ctx.spawn(Timer::after(Duration::from_millis(100)), |this, _, ctx| {
  86                this.poll_scheduled = false;
  87                this.poll_entries(ctx);
  88            })
  89            .detach();
  90            self.poll_scheduled = true;
  91        }
  92    }
  93
  94    fn is_scanning(&self) -> bool {
  95        if let ScanState::Scanning = self.scan_state {
  96            true
  97        } else {
  98            false
  99        }
 100    }
 101
 102    pub fn snapshot(&self) -> Snapshot {
 103        self.snapshot.clone()
 104    }
 105
 106    pub fn contains_path(&self, path: &Path) -> bool {
 107        path.starts_with(&self.snapshot.path)
 108    }
 109
 110    pub fn has_inode(&self, inode: u64) -> bool {
 111        self.snapshot.entries.get(&inode).is_some()
 112    }
 113
 114    pub fn file_count(&self) -> usize {
 115        self.snapshot.entries.summary().file_count
 116    }
 117
 118    pub fn abs_path_for_inode(&self, ino: u64) -> Result<PathBuf> {
 119        let mut result = self.snapshot.path.to_path_buf();
 120        result.push(self.path_for_inode(ino, false)?);
 121        Ok(result)
 122    }
 123
 124    pub fn load_history(
 125        &self,
 126        ino: u64,
 127        ctx: &AppContext,
 128    ) -> impl Future<Output = Result<History>> {
 129        let path = self.abs_path_for_inode(ino);
 130        ctx.background_executor().spawn(async move {
 131            let mut file = std::fs::File::open(&path?)?;
 132            let mut base_text = String::new();
 133            file.read_to_string(&mut base_text)?;
 134            Ok(History::new(Arc::from(base_text)))
 135        })
 136    }
 137
 138    pub fn save<'a>(
 139        &self,
 140        ino: u64,
 141        content: BufferSnapshot,
 142        ctx: &AppContext,
 143    ) -> Task<Result<()>> {
 144        let path = self.abs_path_for_inode(ino);
 145        ctx.background_executor().spawn(async move {
 146            let buffer_size = content.text_summary().bytes.min(10 * 1024);
 147            let file = std::fs::File::create(&path?)?;
 148            let mut writer = std::io::BufWriter::with_capacity(buffer_size, file);
 149            for chunk in content.fragments() {
 150                writer.write(chunk.as_bytes())?;
 151            }
 152            writer.flush()?;
 153            Ok(())
 154        })
 155    }
 156
 157    #[cfg(test)]
 158    pub fn files<'a>(&'a self) -> impl Iterator<Item = u64> + 'a {
 159        self.snapshot
 160            .entries
 161            .cursor::<(), ()>()
 162            .filter_map(|entry| {
 163                if let Entry::File { inode, .. } = entry {
 164                    Some(*inode)
 165                } else {
 166                    None
 167                }
 168            })
 169    }
 170}
 171
 172impl Entity for Worktree {
 173    type Event = ();
 174}
 175
 176impl Deref for Worktree {
 177    type Target = Snapshot;
 178
 179    fn deref(&self) -> &Self::Target {
 180        &self.snapshot
 181    }
 182}
 183
 184impl fmt::Debug for Worktree {
 185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 186        self.snapshot.fmt(f)
 187    }
 188}
 189
 190#[derive(Clone)]
 191pub struct Snapshot {
 192    id: usize,
 193    path: Arc<Path>,
 194    root_inode: Option<u64>,
 195    entries: SumTree<Entry>,
 196}
 197
 198impl Snapshot {
 199    pub fn file_count(&self) -> usize {
 200        self.entries.summary().file_count
 201    }
 202
 203    pub fn root_entry(&self) -> Option<&Entry> {
 204        self.root_inode.and_then(|inode| self.entries.get(&inode))
 205    }
 206
 207    pub fn root_name(&self) -> Option<&OsStr> {
 208        self.path.file_name()
 209    }
 210
 211    fn entry_for_path(&self, path: impl AsRef<Path>) -> Option<&Entry> {
 212        self.inode_for_path(path)
 213            .and_then(|inode| self.entries.get(&inode))
 214    }
 215
 216    fn inode_for_path(&self, path: impl AsRef<Path>) -> Option<u64> {
 217        let path = path.as_ref();
 218        self.root_inode.and_then(|mut inode| {
 219            'components: for path_component in path {
 220                if let Some(Entry::Dir { children, .. }) = &self.entries.get(&inode) {
 221                    for (child_inode, name) in children.as_ref() {
 222                        if name.as_ref() == path_component {
 223                            inode = *child_inode;
 224                            continue 'components;
 225                        }
 226                    }
 227                }
 228                return None;
 229            }
 230            Some(inode)
 231        })
 232    }
 233
 234    pub fn path_for_inode(&self, mut inode: u64, include_root: bool) -> Result<PathBuf> {
 235        let mut components = Vec::new();
 236        let mut entry = self
 237            .entries
 238            .get(&inode)
 239            .ok_or_else(|| anyhow!("entry does not exist in worktree"))?;
 240        while let Some(parent) = entry.parent() {
 241            entry = self.entries.get(&parent).unwrap();
 242            if let Entry::Dir { children, .. } = entry {
 243                let (_, child_name) = children
 244                    .iter()
 245                    .find(|(child_inode, _)| *child_inode == inode)
 246                    .unwrap();
 247                components.push(child_name.as_ref());
 248                inode = parent;
 249            } else {
 250                unreachable!();
 251            }
 252        }
 253        if include_root {
 254            components.push(self.root_name().unwrap());
 255        }
 256        Ok(components.into_iter().rev().collect())
 257    }
 258
 259    fn insert_entry(&mut self, path: &Path, entry: Entry) {
 260        let mut edits = Vec::new();
 261        edits.push(Edit::Insert(entry.clone()));
 262        if let Some(parent) = entry.parent() {
 263            let mut parent_entry = self.entries.get(&parent).unwrap().clone();
 264            if let Entry::Dir { children, .. } = &mut parent_entry {
 265                let name = Arc::from(path.file_name().unwrap());
 266                *children = children
 267                    .into_iter()
 268                    .cloned()
 269                    .chain(Some((entry.inode(), name)))
 270                    .collect::<Vec<_>>()
 271                    .into();
 272                edits.push(Edit::Insert(parent_entry));
 273            } else {
 274                unreachable!();
 275            }
 276        }
 277        self.entries.edit(edits);
 278    }
 279
 280    fn remove_entry(&mut self, path: &Path) {
 281        let mut parent_entry = match path.parent().and_then(|p| self.entry_for_path(p).cloned()) {
 282            Some(e) => e,
 283            None => return,
 284        };
 285
 286        let mut edits = Vec::new();
 287        let parent_inode = parent_entry.inode();
 288        let mut entry_inode = None;
 289        if let Entry::Dir { children, .. } = &mut parent_entry {
 290            let mut new_children = Vec::new();
 291            for (child_inode, child_name) in children.as_ref() {
 292                if Some(child_name.as_ref()) == path.file_name() {
 293                    entry_inode = Some(*child_inode);
 294                } else {
 295                    new_children.push((*child_inode, child_name.clone()));
 296                }
 297            }
 298
 299            if new_children.iter().any(|c| Some(c.0) == entry_inode) {
 300                entry_inode = None;
 301            }
 302
 303            *children = new_children.into();
 304            edits.push(Edit::Insert(parent_entry));
 305        }
 306
 307        if let Some(entry_inode) = entry_inode {
 308            let mut descendant_stack = Vec::new();
 309            descendant_stack.push((entry_inode, parent_inode));
 310            while let Some((inode, parent_inode)) = descendant_stack.pop() {
 311                if let Some(entry) = self.entries.get(&inode) {
 312                    if entry.parent() == Some(parent_inode) {
 313                        edits.push(Edit::Remove(inode));
 314                        if let Entry::Dir { children, .. } = entry {
 315                            descendant_stack.extend(children.iter().map(|c| (c.0, entry.inode())));
 316                        }
 317                    }
 318                }
 319            }
 320        }
 321
 322        self.entries.edit(edits);
 323    }
 324
 325    fn fmt_entry(
 326        &self,
 327        f: &mut fmt::Formatter<'_>,
 328        ino: u64,
 329        name: &OsStr,
 330        indent: usize,
 331    ) -> fmt::Result {
 332        match self.entries.get(&ino).unwrap() {
 333            Entry::Dir { children, .. } => {
 334                write!(
 335                    f,
 336                    "{}{}/ ({})\n",
 337                    " ".repeat(indent),
 338                    name.to_string_lossy(),
 339                    ino
 340                )?;
 341                for (child_inode, child_name) in children.iter() {
 342                    self.fmt_entry(f, *child_inode, child_name, indent + 2)?;
 343                }
 344                Ok(())
 345            }
 346            Entry::File { .. } => write!(
 347                f,
 348                "{}{} ({})\n",
 349                " ".repeat(indent),
 350                name.to_string_lossy(),
 351                ino
 352            ),
 353        }
 354    }
 355}
 356
 357impl fmt::Debug for Snapshot {
 358    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 359        if let Some(root_ino) = self.root_inode {
 360            self.fmt_entry(f, root_ino, self.path.file_name().unwrap(), 0)
 361        } else {
 362            write!(f, "Empty tree\n")
 363        }
 364    }
 365}
 366
 367impl FileHandle {
 368    pub fn path(&self, ctx: &AppContext) -> PathBuf {
 369        self.worktree
 370            .read(ctx)
 371            .path_for_inode(self.inode, false)
 372            .unwrap()
 373    }
 374
 375    pub fn load_history(&self, ctx: &AppContext) -> impl Future<Output = Result<History>> {
 376        self.worktree.read(ctx).load_history(self.inode, ctx)
 377    }
 378
 379    pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task<Result<()>> {
 380        let worktree = self.worktree.read(ctx);
 381        worktree.save(self.inode, content, ctx)
 382    }
 383
 384    pub fn entry_id(&self) -> (usize, u64) {
 385        (self.worktree.id(), self.inode)
 386    }
 387}
 388
 389#[derive(Clone, Debug)]
 390pub enum Entry {
 391    Dir {
 392        inode: u64,
 393        parent: Option<u64>,
 394        is_symlink: bool,
 395        is_ignored: bool,
 396        children: Arc<[(u64, Arc<OsStr>)]>,
 397        pending: bool,
 398    },
 399    File {
 400        inode: u64,
 401        parent: Option<u64>,
 402        is_symlink: bool,
 403        is_ignored: bool,
 404        path: PathEntry,
 405    },
 406}
 407
 408impl Entry {
 409    fn inode(&self) -> u64 {
 410        match self {
 411            Entry::Dir { inode, .. } => *inode,
 412            Entry::File { inode, .. } => *inode,
 413        }
 414    }
 415
 416    fn is_dir(&self) -> bool {
 417        matches!(self, Entry::Dir { .. })
 418    }
 419
 420    fn parent(&self) -> Option<u64> {
 421        match self {
 422            Entry::Dir { parent, .. } => *parent,
 423            Entry::File { parent, .. } => *parent,
 424        }
 425    }
 426}
 427
 428impl sum_tree::Item for Entry {
 429    type Summary = EntrySummary;
 430
 431    fn summary(&self) -> Self::Summary {
 432        EntrySummary {
 433            max_ino: self.inode(),
 434            file_count: if matches!(self, Self::File { .. }) {
 435                1
 436            } else {
 437                0
 438            },
 439        }
 440    }
 441}
 442
 443impl sum_tree::KeyedItem for Entry {
 444    type Key = u64;
 445
 446    fn key(&self) -> Self::Key {
 447        self.inode()
 448    }
 449}
 450
 451#[derive(Clone, Debug, Default)]
 452pub struct EntrySummary {
 453    max_ino: u64,
 454    file_count: usize,
 455}
 456
 457impl<'a> AddAssign<&'a EntrySummary> for EntrySummary {
 458    fn add_assign(&mut self, rhs: &'a EntrySummary) {
 459        self.max_ino = rhs.max_ino;
 460        self.file_count += rhs.file_count;
 461    }
 462}
 463
 464impl<'a> sum_tree::Dimension<'a, EntrySummary> for u64 {
 465    fn add_summary(&mut self, summary: &'a EntrySummary) {
 466        *self = summary.max_ino;
 467    }
 468}
 469
 470#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd)]
 471struct FileCount(usize);
 472
 473impl<'a> sum_tree::Dimension<'a, EntrySummary> for FileCount {
 474    fn add_summary(&mut self, summary: &'a EntrySummary) {
 475        self.0 += summary.file_count;
 476    }
 477}
 478
 479struct BackgroundScanner {
 480    snapshot: Mutex<Snapshot>,
 481    notify: Sender<ScanState>,
 482    thread_pool: scoped_pool::Pool,
 483}
 484
 485impl BackgroundScanner {
 486    fn new(snapshot: Snapshot, notify: Sender<ScanState>) -> Self {
 487        Self {
 488            snapshot: Mutex::new(snapshot),
 489            notify,
 490            thread_pool: scoped_pool::Pool::new(16),
 491        }
 492    }
 493
 494    fn path(&self) -> Arc<Path> {
 495        self.snapshot.lock().path.clone()
 496    }
 497
 498    fn snapshot(&self) -> Snapshot {
 499        self.snapshot.lock().clone()
 500    }
 501
 502    fn run(&self) {
 503        let path = {
 504            let mut snapshot = self.snapshot.lock();
 505            let canonical_path = snapshot
 506                .path
 507                .canonicalize()
 508                .map(Arc::from)
 509                .unwrap_or_else(|_| snapshot.path.clone());
 510            snapshot.path = canonical_path.clone();
 511            canonical_path
 512        };
 513
 514        // Create the event stream before we start scanning to ensure we receive events for changes
 515        // that occur in the middle of the scan.
 516        let event_stream =
 517            fsevent::EventStream::new(&[path.as_ref()], Duration::from_millis(100), |events| {
 518                if smol::block_on(self.notify.send(ScanState::Scanning)).is_err() {
 519                    return false;
 520                }
 521
 522                self.process_events(events);
 523
 524                if smol::block_on(self.notify.send(ScanState::Idle)).is_err() {
 525                    return false;
 526                }
 527
 528                true
 529            });
 530
 531        if smol::block_on(self.notify.send(ScanState::Scanning)).is_err() {
 532            return;
 533        }
 534
 535        if let Err(err) = self.scan_dirs() {
 536            if smol::block_on(self.notify.send(ScanState::Err(err))).is_err() {
 537                return;
 538            }
 539        }
 540
 541        if smol::block_on(self.notify.send(ScanState::Idle)).is_err() {
 542            return;
 543        }
 544
 545        event_stream.run();
 546    }
 547
 548    fn scan_dirs(&self) -> io::Result<()> {
 549        let path = self.path();
 550        let metadata = fs::metadata(&path)?;
 551        let inode = metadata.ino();
 552        let is_symlink = fs::symlink_metadata(&path)?.file_type().is_symlink();
 553        let name = Arc::from(path.file_name().unwrap_or(OsStr::new("/")));
 554        let relative_path = PathBuf::from(&name);
 555
 556        let mut ignore = IgnoreBuilder::new().build().add_parents(&path).unwrap();
 557        if metadata.is_dir() {
 558            ignore = ignore.add_child(&path).unwrap();
 559        }
 560        let is_ignored = ignore.matched(&path, metadata.is_dir()).is_ignore();
 561
 562        if metadata.file_type().is_dir() {
 563            let is_ignored = is_ignored || name.as_ref() == ".git";
 564            let dir_entry = Entry::Dir {
 565                parent: None,
 566                inode,
 567                is_symlink,
 568                is_ignored,
 569                children: Arc::from([]),
 570                pending: true,
 571            };
 572            self.insert_entries(Some(dir_entry.clone()));
 573            self.snapshot.lock().root_inode = Some(inode);
 574
 575            let (tx, rx) = crossbeam_channel::unbounded();
 576
 577            tx.send(Ok(ScanJob {
 578                inode,
 579                path: path.clone(),
 580                relative_path,
 581                dir_entry,
 582                ignore: Some(ignore),
 583                scan_queue: tx.clone(),
 584            }))
 585            .unwrap();
 586            drop(tx);
 587
 588            let mut results = Vec::new();
 589            results.resize_with(self.thread_pool.workers(), || Ok(()));
 590            self.thread_pool.scoped(|pool| {
 591                for result in &mut results {
 592                    pool.execute(|| {
 593                        let result = result;
 594                        while let Ok(job) = rx.recv() {
 595                            if let Err(err) = job.and_then(|job| self.scan_dir(job)) {
 596                                *result = Err(err);
 597                                break;
 598                            }
 599                        }
 600                    });
 601                }
 602            });
 603            results.into_iter().collect::<io::Result<()>>()?;
 604        } else {
 605            self.insert_entries(Some(Entry::File {
 606                parent: None,
 607                path: PathEntry::new(inode, &relative_path, is_ignored),
 608                inode,
 609                is_symlink,
 610                is_ignored,
 611            }));
 612            self.snapshot.lock().root_inode = Some(inode);
 613        }
 614
 615        Ok(())
 616    }
 617
 618    fn scan_dir(&self, job: ScanJob) -> io::Result<()> {
 619        let scan_queue = job.scan_queue;
 620        let mut dir_entry = job.dir_entry;
 621
 622        let mut new_children = Vec::new();
 623        let mut new_entries = Vec::new();
 624        let mut new_jobs = Vec::new();
 625
 626        for child_entry in fs::read_dir(&job.path)? {
 627            let child_entry = child_entry?;
 628            let name: Arc<OsStr> = child_entry.file_name().into();
 629            let relative_path = job.relative_path.join(name.as_ref());
 630            let metadata = child_entry.metadata()?;
 631            let ino = metadata.ino();
 632            let is_symlink = metadata.file_type().is_symlink();
 633            let path = job.path.join(name.as_ref());
 634
 635            new_children.push((ino, name.clone()));
 636            if metadata.is_dir() {
 637                let mut is_ignored = true;
 638                let mut ignore = None;
 639
 640                if let Some(parent_ignore) = job.ignore.as_ref() {
 641                    let child_ignore = parent_ignore.add_child(&path).unwrap();
 642                    is_ignored =
 643                        child_ignore.matched(&path, true).is_ignore() || name.as_ref() == ".git";
 644                    if !is_ignored {
 645                        ignore = Some(child_ignore);
 646                    }
 647                }
 648
 649                let dir_entry = Entry::Dir {
 650                    parent: Some(job.inode),
 651                    inode: ino,
 652                    is_symlink,
 653                    is_ignored,
 654                    children: Arc::from([]),
 655                    pending: true,
 656                };
 657                new_entries.push(dir_entry.clone());
 658                new_jobs.push(ScanJob {
 659                    inode: ino,
 660                    path: Arc::from(path),
 661                    relative_path,
 662                    dir_entry,
 663                    ignore,
 664                    scan_queue: scan_queue.clone(),
 665                });
 666            } else {
 667                let is_ignored = job
 668                    .ignore
 669                    .as_ref()
 670                    .map_or(true, |i| i.matched(&path, false).is_ignore());
 671                new_entries.push(Entry::File {
 672                    parent: Some(job.inode),
 673                    path: PathEntry::new(ino, &relative_path, is_ignored),
 674                    inode: ino,
 675                    is_symlink,
 676                    is_ignored,
 677                });
 678            };
 679        }
 680
 681        if let Entry::Dir {
 682            children, pending, ..
 683        } = &mut dir_entry
 684        {
 685            *children = Arc::from(new_children);
 686            *pending = false;
 687        } else {
 688            unreachable!()
 689        }
 690        new_entries.push(dir_entry);
 691
 692        self.insert_entries(new_entries);
 693        for new_job in new_jobs {
 694            scan_queue.send(Ok(new_job)).unwrap();
 695        }
 696
 697        Ok(())
 698    }
 699
 700    fn process_events(&self, mut events: Vec<fsevent::Event>) {
 701        let mut snapshot = self.snapshot();
 702
 703        events.sort_unstable_by(|a, b| a.path.cmp(&b.path));
 704        let mut paths = events.into_iter().map(|e| e.path).peekable();
 705        let (scan_queue_tx, scan_queue_rx) = crossbeam_channel::unbounded();
 706        while let Some(path) = paths.next() {
 707            let relative_path = match path.strip_prefix(&snapshot.path) {
 708                Ok(relative_path) => relative_path.to_path_buf(),
 709                Err(e) => {
 710                    log::error!("unexpected event {:?}", e);
 711                    continue;
 712                }
 713            };
 714
 715            while paths.peek().map_or(false, |p| p.starts_with(&path)) {
 716                paths.next();
 717            }
 718
 719            snapshot.remove_entry(&relative_path);
 720
 721            match self.fs_entry_for_path(&snapshot.path, &path) {
 722                Ok(Some((fs_entry, ignore))) => {
 723                    snapshot.insert_entry(&path, fs_entry.clone());
 724
 725                    if fs_entry.is_dir() {
 726                        scan_queue_tx
 727                            .send(Ok(ScanJob {
 728                                inode: fs_entry.inode(),
 729                                path: Arc::from(path),
 730                                relative_path: snapshot
 731                                    .root_name()
 732                                    .map_or(PathBuf::new(), PathBuf::from)
 733                                    .join(relative_path),
 734                                dir_entry: fs_entry,
 735                                ignore: Some(ignore),
 736                                scan_queue: scan_queue_tx.clone(),
 737                            }))
 738                            .unwrap();
 739                    }
 740                }
 741                Ok(None) => {}
 742                Err(err) => {
 743                    // TODO - create a special 'error' entry in the entries tree to mark this
 744                    log::error!("error reading file on event {:?}", err);
 745                }
 746            }
 747        }
 748
 749        *self.snapshot.lock() = snapshot;
 750
 751        // Scan any directories that were created as part of this event batch.
 752        drop(scan_queue_tx);
 753        self.thread_pool.scoped(|pool| {
 754            for _ in 0..self.thread_pool.workers() {
 755                pool.execute(|| {
 756                    while let Ok(job) = scan_queue_rx.recv() {
 757                        if let Err(err) = job.and_then(|job| self.scan_dir(job)) {
 758                            log::error!("Error scanning {:?}", err);
 759                        }
 760                    }
 761                });
 762            }
 763        });
 764    }
 765
 766    fn fs_entry_for_path(&self, root_path: &Path, path: &Path) -> Result<Option<(Entry, Ignore)>> {
 767        match fs::metadata(&path) {
 768            Ok(metadata) => {
 769                let mut ignore = IgnoreBuilder::new().build().add_parents(&path).unwrap();
 770                if metadata.is_dir() {
 771                    ignore = ignore.add_child(&path).unwrap();
 772                }
 773                let is_ignored = ignore.matched(&path, metadata.is_dir()).is_ignore();
 774
 775                let inode = metadata.ino();
 776                let is_symlink = fs::symlink_metadata(&path)?.file_type().is_symlink();
 777                let parent = if path == root_path {
 778                    None
 779                } else {
 780                    Some(fs::metadata(path.parent().unwrap())?.ino())
 781                };
 782                if metadata.file_type().is_dir() {
 783                    Ok(Some((
 784                        Entry::Dir {
 785                            parent,
 786                            inode,
 787                            is_symlink,
 788                            is_ignored,
 789                            children: Arc::from([]),
 790                            pending: true,
 791                        },
 792                        ignore,
 793                    )))
 794                } else {
 795                    Ok(Some((
 796                        Entry::File {
 797                            parent,
 798                            path: PathEntry::new(
 799                                inode,
 800                                root_path
 801                                    .parent()
 802                                    .map_or(path, |parent| path.strip_prefix(parent).unwrap()),
 803                                is_ignored,
 804                            ),
 805                            inode,
 806                            is_symlink,
 807                            is_ignored,
 808                        },
 809                        ignore,
 810                    )))
 811                }
 812            }
 813            Err(err) => {
 814                if err.kind() == io::ErrorKind::NotFound {
 815                    Ok(None)
 816                } else {
 817                    Err(anyhow::Error::new(err))
 818                }
 819            }
 820        }
 821    }
 822
 823    fn insert_entries(&self, entries: impl IntoIterator<Item = Entry>) {
 824        self.snapshot
 825            .lock()
 826            .entries
 827            .edit(entries.into_iter().map(Edit::Insert).collect::<Vec<_>>());
 828    }
 829}
 830
 831impl Drop for BackgroundScanner {
 832    fn drop(&mut self) {
 833        self.thread_pool.shutdown();
 834    }
 835}
 836
 837struct ScanJob {
 838    inode: u64,
 839    path: Arc<Path>,
 840    relative_path: PathBuf,
 841    dir_entry: Entry,
 842    ignore: Option<Ignore>,
 843    scan_queue: crossbeam_channel::Sender<io::Result<ScanJob>>,
 844}
 845
 846pub trait WorktreeHandle {
 847    fn file(&self, entry_id: u64, app: &AppContext) -> Result<FileHandle>;
 848}
 849
 850impl WorktreeHandle for ModelHandle<Worktree> {
 851    fn file(&self, inode: u64, app: &AppContext) -> Result<FileHandle> {
 852        if self.read(app).has_inode(inode) {
 853            Ok(FileHandle {
 854                worktree: self.clone(),
 855                inode,
 856            })
 857        } else {
 858            Err(anyhow!("entry does not exist in tree"))
 859        }
 860    }
 861}
 862
 863trait UnwrapIgnoreTuple {
 864    fn unwrap(self) -> Ignore;
 865}
 866
 867impl UnwrapIgnoreTuple for (Ignore, Option<ignore::Error>) {
 868    fn unwrap(self) -> Ignore {
 869        if let Some(error) = self.1 {
 870            log::error!("error loading gitignore data: {}", error);
 871        }
 872        self.0
 873    }
 874}
 875
 876#[cfg(test)]
 877mod tests {
 878    use super::*;
 879    use crate::editor::Buffer;
 880    use crate::test::*;
 881    use anyhow::Result;
 882    use gpui::App;
 883    use log::LevelFilter;
 884    use rand::prelude::*;
 885    use serde_json::json;
 886    use simplelog::SimpleLogger;
 887    use std::env;
 888    use std::os::unix;
 889    use std::time::{SystemTime, UNIX_EPOCH};
 890
 891    #[test]
 892    fn test_populate_and_search() {
 893        App::test_async((), |mut app| async move {
 894            let dir = temp_tree(json!({
 895                "root": {
 896                    "apple": "",
 897                    "banana": {
 898                        "carrot": {
 899                            "date": "",
 900                            "endive": "",
 901                        }
 902                    },
 903                    "fennel": {
 904                        "grape": "",
 905                    }
 906                }
 907            }));
 908
 909            let root_link_path = dir.path().join("root_link");
 910            unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();
 911
 912            let tree = app.add_model(|ctx| Worktree::new(root_link_path, ctx));
 913            assert_condition(1, 300, || app.read(|ctx| tree.read(ctx).file_count() == 4)).await;
 914            app.read(|ctx| {
 915                let tree = tree.read(ctx);
 916                let results = match_paths(
 917                    Some(tree.snapshot()).iter(),
 918                    "bna",
 919                    false,
 920                    false,
 921                    false,
 922                    10,
 923                    ctx.thread_pool().clone(),
 924                )
 925                .iter()
 926                .map(|result| tree.path_for_inode(result.entry_id, true))
 927                .collect::<Result<Vec<PathBuf>, _>>()
 928                .unwrap();
 929                assert_eq!(
 930                    results,
 931                    vec![
 932                        PathBuf::from("root_link/banana/carrot/date"),
 933                        PathBuf::from("root_link/banana/carrot/endive"),
 934                    ]
 935                );
 936            })
 937        });
 938    }
 939
 940    #[test]
 941    fn test_save_file() {
 942        App::test_async((), |mut app| async move {
 943            let dir = temp_tree(json!({
 944                "file1": "the old contents",
 945            }));
 946
 947            let tree = app.add_model(|ctx| Worktree::new(dir.path(), ctx));
 948            assert_condition(1, 300, || app.read(|ctx| tree.read(ctx).file_count() == 1)).await;
 949
 950            let buffer = Buffer::new(1, "a line of text.\n".repeat(10 * 1024));
 951
 952            let file_inode = app.read(|ctx| {
 953                let tree = tree.read(ctx);
 954                let inode = tree.files().next().unwrap();
 955                assert_eq!(
 956                    tree.path_for_inode(inode, false)
 957                        .unwrap()
 958                        .file_name()
 959                        .unwrap(),
 960                    "file1"
 961                );
 962                inode
 963            });
 964
 965            tree.update(&mut app, |tree, ctx| {
 966                smol::block_on(tree.save(file_inode, buffer.snapshot(), ctx.as_ref())).unwrap()
 967            });
 968
 969            let loaded_history = app
 970                .read(|ctx| tree.read(ctx).load_history(file_inode, ctx))
 971                .await
 972                .unwrap();
 973            assert_eq!(loaded_history.base_text.as_ref(), buffer.text());
 974        });
 975    }
 976
 977    #[test]
 978    fn test_rescan() {
 979        App::test_async((), |mut app| async move {
 980            let dir2 = temp_tree(json!({
 981                "dir1": {
 982                    "dir3": {
 983                        "file": "contents",
 984                    }
 985                },
 986                "dir2": {
 987                }
 988            }));
 989            let dir = temp_tree(json!({
 990                "dir1": {
 991                    "dir3": {
 992                        "file": "contents",
 993                    }
 994                },
 995                "dir2": {
 996                }
 997            }));
 998
 999            let tree = app.add_model(|ctx| Worktree::new(dir.path(), ctx));
1000            assert_condition(1, 300, || app.read(|ctx| tree.read(ctx).file_count() == 1)).await;
1001
1002            let dir_inode = app.read(|ctx| {
1003                tree.read(ctx)
1004                    .snapshot()
1005                    .inode_for_path("dir1/dir3")
1006                    .unwrap()
1007            });
1008            app.read(|ctx| {
1009                let tree = tree.read(ctx);
1010                assert_eq!(
1011                    tree.path_for_inode(dir_inode, false)
1012                        .unwrap()
1013                        .to_str()
1014                        .unwrap(),
1015                    "dir1/dir3"
1016                );
1017            });
1018
1019            std::fs::rename(dir2.path(), dir.path().join("foo")).unwrap();
1020            assert_condition(1, 300, || {
1021                app.read(|ctx| {
1022                    let tree = tree.read(ctx);
1023                    tree.path_for_inode(dir_inode, false)
1024                        .unwrap()
1025                        .to_str()
1026                        .unwrap()
1027                        == "dir2/dir3"
1028                })
1029            })
1030            .await;
1031            app.read(|ctx| {
1032                let tree = tree.read(ctx);
1033                assert_eq!(tree.snapshot().inode_for_path("dir2/dir3"), Some(dir_inode));
1034            });
1035        });
1036    }
1037
1038    #[test]
1039    fn test_random() {
1040        let iterations = env::var("ITERATIONS")
1041            .map(|i| i.parse().unwrap())
1042            .unwrap_or(100);
1043        let operations = env::var("OPERATIONS")
1044            .map(|o| o.parse().unwrap())
1045            .unwrap_or(40);
1046        let initial_entries = env::var("INITIAL_ENTRIES")
1047            .map(|o| o.parse().unwrap())
1048            .unwrap_or(20);
1049        let seeds = if let Ok(seed) = env::var("SEED").map(|s| s.parse().unwrap()) {
1050            // Init logging so that we can debug the operations for this seed.
1051            SimpleLogger::init(LevelFilter::Info, Default::default()).unwrap();
1052            seed..seed + 1
1053        } else {
1054            0..iterations
1055        };
1056
1057        for seed in seeds {
1058            dbg!(seed);
1059            let mut rng = StdRng::seed_from_u64(seed);
1060
1061            let root_dir = tempdir::TempDir::new(&format!("test-{}", seed)).unwrap();
1062            for _ in 0..initial_entries {
1063                randomly_mutate_tree(root_dir.path(), 1.0, &mut rng).unwrap();
1064            }
1065            log::info!("Generated initial tree");
1066
1067            let (notify_tx, _notify_rx) = smol::channel::unbounded();
1068            let scanner = BackgroundScanner::new(
1069                Snapshot {
1070                    id: 0,
1071                    path: root_dir.path().into(),
1072                    root_inode: None,
1073                    entries: Default::default(),
1074                },
1075                notify_tx,
1076            );
1077            scanner.scan_dirs().unwrap();
1078
1079            let mut events = Vec::new();
1080            let mut mutations_len = operations;
1081            while mutations_len > 1 {
1082                if !events.is_empty() && rng.gen_bool(0.4) {
1083                    let len = rng.gen_range(0..=events.len());
1084                    let to_deliver = events.drain(0..len).collect::<Vec<_>>();
1085                    log::info!("Delivering events: {:#?}", to_deliver);
1086                    scanner.process_events(to_deliver);
1087                } else {
1088                    events.extend(randomly_mutate_tree(root_dir.path(), 0.6, &mut rng).unwrap());
1089                    mutations_len -= 1;
1090                }
1091            }
1092            log::info!("Quiescing: {:#?}", events);
1093            scanner.process_events(events);
1094
1095            let (notify_tx, _notify_rx) = smol::channel::unbounded();
1096            let new_scanner = BackgroundScanner::new(
1097                Snapshot {
1098                    id: 0,
1099                    path: root_dir.path().into(),
1100                    root_inode: None,
1101                    entries: Default::default(),
1102                },
1103                notify_tx,
1104            );
1105            new_scanner.scan_dirs().unwrap();
1106            assert_eq!(scanner.snapshot().to_vec(), new_scanner.snapshot().to_vec());
1107        }
1108    }
1109
1110    fn randomly_mutate_tree(
1111        root_path: &Path,
1112        insertion_probability: f64,
1113        rng: &mut impl Rng,
1114    ) -> Result<Vec<fsevent::Event>> {
1115        let (dirs, files) = read_dir_recursive(root_path.to_path_buf());
1116
1117        let mut events = Vec::new();
1118        let mut record_event = |path: PathBuf| {
1119            events.push(fsevent::Event {
1120                event_id: SystemTime::now()
1121                    .duration_since(UNIX_EPOCH)
1122                    .unwrap()
1123                    .as_secs(),
1124                flags: fsevent::StreamFlags::empty(),
1125                path,
1126            });
1127        };
1128
1129        if (files.is_empty() && dirs.len() == 1) || rng.gen_bool(insertion_probability) {
1130            let path = dirs.choose(rng).unwrap();
1131            let new_path = path.join(gen_name(rng));
1132
1133            if rng.gen() {
1134                log::info!("Creating dir {:?}", new_path.strip_prefix(root_path)?);
1135                fs::create_dir(&new_path)?;
1136            } else {
1137                log::info!("Creating file {:?}", new_path.strip_prefix(root_path)?);
1138                fs::write(&new_path, "")?;
1139            }
1140            record_event(new_path);
1141        } else {
1142            let old_path = {
1143                let file_path = files.choose(rng);
1144                let dir_path = dirs[1..].choose(rng);
1145                file_path.into_iter().chain(dir_path).choose(rng).unwrap()
1146            };
1147
1148            let is_rename = rng.gen();
1149            if is_rename {
1150                let new_path_parent = dirs
1151                    .iter()
1152                    .filter(|d| !d.starts_with(old_path))
1153                    .choose(rng)
1154                    .unwrap();
1155
1156                let overwrite_existing_dir =
1157                    !old_path.starts_with(&new_path_parent) && rng.gen_bool(0.3);
1158                let new_path = if overwrite_existing_dir {
1159                    fs::remove_dir_all(&new_path_parent).ok();
1160                    new_path_parent.to_path_buf()
1161                } else {
1162                    new_path_parent.join(gen_name(rng))
1163                };
1164
1165                log::info!(
1166                    "Renaming {:?} to {}{:?}",
1167                    old_path.strip_prefix(&root_path)?,
1168                    if overwrite_existing_dir {
1169                        "overwrite "
1170                    } else {
1171                        ""
1172                    },
1173                    new_path.strip_prefix(&root_path)?
1174                );
1175                fs::rename(&old_path, &new_path)?;
1176                record_event(old_path.clone());
1177                record_event(new_path);
1178            } else if old_path.is_dir() {
1179                let (dirs, files) = read_dir_recursive(old_path.clone());
1180
1181                log::info!("Deleting dir {:?}", old_path.strip_prefix(&root_path)?);
1182                fs::remove_dir_all(&old_path).unwrap();
1183                for file in files {
1184                    record_event(file);
1185                }
1186                for dir in dirs {
1187                    record_event(dir);
1188                }
1189            } else {
1190                log::info!("Deleting file {:?}", old_path.strip_prefix(&root_path)?);
1191                fs::remove_file(old_path).unwrap();
1192                record_event(old_path.clone());
1193            }
1194        }
1195
1196        Ok(events)
1197    }
1198
1199    fn read_dir_recursive(path: PathBuf) -> (Vec<PathBuf>, Vec<PathBuf>) {
1200        let child_entries = fs::read_dir(&path).unwrap();
1201        let mut dirs = vec![path];
1202        let mut files = Vec::new();
1203        for child_entry in child_entries {
1204            let child_path = child_entry.unwrap().path();
1205            if child_path.is_dir() {
1206                let (child_dirs, child_files) = read_dir_recursive(child_path);
1207                dirs.extend(child_dirs);
1208                files.extend(child_files);
1209            } else {
1210                files.push(child_path);
1211            }
1212        }
1213        (dirs, files)
1214    }
1215
1216    fn gen_name(rng: &mut impl Rng) -> String {
1217        (0..6)
1218            .map(|_| rng.sample(rand::distributions::Alphanumeric))
1219            .map(char::from)
1220            .collect()
1221    }
1222
1223    impl Snapshot {
1224        fn to_vec(&self) -> Vec<(PathBuf, u64)> {
1225            use std::iter::FromIterator;
1226
1227            let mut paths = Vec::new();
1228
1229            let mut stack = Vec::new();
1230            stack.extend(self.root_inode);
1231            while let Some(inode) = stack.pop() {
1232                let computed_path = self.path_for_inode(inode, true).unwrap();
1233                match self.entries.get(&inode).unwrap() {
1234                    Entry::Dir { children, .. } => {
1235                        stack.extend(children.iter().map(|c| c.0));
1236                    }
1237                    Entry::File { path, .. } => {
1238                        assert_eq!(
1239                            String::from_iter(path.path.iter()),
1240                            computed_path.to_str().unwrap()
1241                        );
1242                    }
1243                }
1244                paths.push((computed_path, inode));
1245            }
1246
1247            paths.sort_by(|a, b| a.0.cmp(&b.0));
1248            paths
1249        }
1250    }
1251}