worktree.rs

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