worktree.rs

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