worktree.rs

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