diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index 6f59035550..065b32ebac 100644 --- a/zed/src/file_finder.rs +++ b/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); diff --git a/zed/src/workspace/workspace.rs b/zed/src/workspace/workspace.rs index 21b51327c0..e8fc21c819 100644 --- a/zed/src/workspace/workspace.rs +++ b/zed/src/workspace/workspace.rs @@ -94,6 +94,19 @@ impl Workspace { &self.worktrees } + pub fn worktree_scans_complete(&self, ctx: &AppContext) -> impl Future + 'static { + let futures = self + .worktrees + .iter() + .map(|worktree| worktree.read(ctx).scan_complete()) + .collect::>(); + 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()); diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index 1496ddef76..23ab0960df 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/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) = diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index c55dadc743..eed829fd8d 100644 --- a/zed/src/worktree.rs +++ b/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, + scan_listeners: Mutex>>, 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.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",