Add a method for waiting until a worktree's current scan is complete

Max Brunsfeld created

Start removing usages of finish_pending_tasks in tests

Change summary

zed/src/file_finder.rs              |  4 +-
zed/src/workspace/workspace.rs      | 16 ++++++++++++
zed/src/workspace/workspace_view.rs |  3 +
zed/src/worktree.rs                 | 37 ++++++++++++++++++++++++++++--
4 files changed, 53 insertions(+), 7 deletions(-)

Detailed changes

zed/src/file_finder.rs 🔗

@@ -421,7 +421,8 @@ mod tests {
             let workspace = app.add_model(|ctx| Workspace::new(vec![tmp_dir.path().into()], ctx));
             let (window_id, workspace_view) =
                 app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx));
-            app.finish_pending_tasks().await; // Open and populate worktree.
+            app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx))
+                .await;
             app.dispatch_action(
                 window_id,
                 vec![workspace_view.id()],
@@ -444,7 +445,6 @@ mod tests {
             app.dispatch_action(window_id, chain.clone(), "buffer:insert", "b".to_string());
             app.dispatch_action(window_id, chain.clone(), "buffer:insert", "n".to_string());
             app.dispatch_action(window_id, chain.clone(), "buffer:insert", "a".to_string());
-            app.finish_pending_tasks().await; // Complete path search.
 
             // let view_state = finder.state(&app);
             // assert!(view_state.matches.len() > 1);

zed/src/workspace/workspace.rs 🔗

@@ -94,6 +94,19 @@ impl Workspace {
         &self.worktrees
     }
 
+    pub fn worktree_scans_complete(&self, ctx: &AppContext) -> impl Future<Output = ()> + 'static {
+        let futures = self
+            .worktrees
+            .iter()
+            .map(|worktree| worktree.read(ctx).scan_complete())
+            .collect::<Vec<_>>();
+        async move {
+            for future in futures {
+                future.await;
+            }
+        }
+    }
+
     pub fn contains_paths(&self, paths: &[PathBuf], app: &AppContext) -> bool {
         paths.iter().all(|path| self.contains_path(&path, app))
     }
@@ -235,7 +248,8 @@ mod tests {
             }));
 
             let workspace = app.add_model(|ctx| Workspace::new(vec![dir.path().into()], ctx));
-            app.finish_pending_tasks().await; // Open and populate worktree.
+            app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx))
+                .await;
 
             // Get the first file entry.
             let tree = app.read(|ctx| workspace.read(ctx).worktrees.iter().next().unwrap().clone());

zed/src/workspace/workspace_view.rs 🔗

@@ -393,7 +393,8 @@ mod tests {
 
             let settings = settings::channel(&app.font_cache()).unwrap().1;
             let workspace = app.add_model(|ctx| Workspace::new(vec![dir.path().into()], ctx));
-            app.finish_pending_tasks().await; // Open and populate worktree.
+            app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx))
+                .await;
             let entries = app.read(|ctx| workspace.file_entries(ctx));
 
             let (_, workspace_view) =

zed/src/worktree.rs 🔗

@@ -6,17 +6,20 @@ use crate::{
     sum_tree::{self, Edit, SumTree},
 };
 use anyhow::{anyhow, Result};
+use futures_core::future::BoxFuture;
 pub use fuzzy::match_paths;
 use fuzzy::PathEntry;
 use gpui::{scoped_pool, AppContext, Entity, ModelContext, ModelHandle, Task};
 use ignore::dir::{Ignore, IgnoreBuilder};
 use parking_lot::Mutex;
+use postage::{oneshot, prelude::Stream, sink::Sink};
 use smol::{channel::Sender, Timer};
-use std::future::Future;
 use std::{
     ffi::OsStr,
     fmt, fs,
+    future::Future,
     io::{self, Read, Write},
+    mem,
     ops::{AddAssign, Deref},
     os::unix::fs::MetadataExt,
     path::{Path, PathBuf},
@@ -36,6 +39,7 @@ enum ScanState {
 pub struct Worktree {
     snapshot: Snapshot,
     scanner: Arc<BackgroundScanner>,
+    scan_listeners: Mutex<Vec<postage::oneshot::Sender<()>>>,
     scan_state: ScanState,
     poll_scheduled: bool,
 }
@@ -59,7 +63,8 @@ impl Worktree {
         let tree = Self {
             snapshot,
             scanner,
-            scan_state: ScanState::Idle,
+            scan_listeners: Default::default(),
+            scan_state: ScanState::Scanning,
             poll_scheduled: false,
         };
 
@@ -72,6 +77,18 @@ impl Worktree {
         tree
     }
 
+    pub fn scan_complete(&self) -> BoxFuture<'static, ()> {
+        if self.is_scanning() {
+            let (tx, mut rx) = oneshot::channel::<()>();
+            self.scan_listeners.lock().push(tx);
+            Box::pin(async move {
+                rx.recv().await;
+            })
+        } else {
+            Box::pin(async {})
+        }
+    }
+
     fn observe_scan_state(&mut self, scan_state: ScanState, ctx: &mut ModelContext<Self>) {
         self.scan_state = scan_state;
         self.poll_entries(ctx);
@@ -88,6 +105,18 @@ impl Worktree {
             })
             .detach();
             self.poll_scheduled = true;
+        } else {
+            let mut listeners = Vec::new();
+            mem::swap(self.scan_listeners.lock().as_mut(), &mut listeners);
+            ctx.spawn(
+                async move {
+                    for mut tx in listeners {
+                        tx.send(()).await.ok();
+                    }
+                },
+                |_, _, _| {},
+            )
+            .detach();
         }
     }
 
@@ -906,9 +935,11 @@ mod tests {
             unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();
 
             let tree = app.add_model(|ctx| Worktree::new(root_link_path, ctx));
-            assert_condition(1, 300, || app.read(|ctx| tree.read(ctx).file_count() == 4)).await;
+
+            app.read(|ctx| tree.read(ctx).scan_complete()).await;
             app.read(|ctx| {
                 let tree = tree.read(ctx);
+                assert_eq!(tree.file_count(), 4);
                 let results = match_paths(
                     Some(tree.snapshot()).iter(),
                     "bna",