worktree.rs

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