worktree.rs

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