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