mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-06 18:46:49 +00:00
Only mutate background snapshot in the background scanner
This commit is contained in:
parent
d742c758bc
commit
b10b0dbd75
1 changed files with 131 additions and 153 deletions
|
@ -9,7 +9,7 @@ use fs::LineEnding;
|
||||||
use fs::{repository::GitRepository, Fs};
|
use fs::{repository::GitRepository, Fs};
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::{
|
channel::{
|
||||||
mpsc::{self, UnboundedSender},
|
mpsc::{self, UnboundedReceiver, UnboundedSender},
|
||||||
oneshot,
|
oneshot,
|
||||||
},
|
},
|
||||||
Stream, StreamExt,
|
Stream, StreamExt,
|
||||||
|
@ -29,6 +29,7 @@ use language::{
|
||||||
Buffer, DiagnosticEntry, PointUtf16, Rope, RopeFingerprint, Unclipped,
|
Buffer, DiagnosticEntry, PointUtf16, Rope, RopeFingerprint, Unclipped,
|
||||||
};
|
};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use postage::barrier;
|
||||||
use postage::{
|
use postage::{
|
||||||
prelude::{Sink as _, Stream as _},
|
prelude::{Sink as _, Stream as _},
|
||||||
watch,
|
watch,
|
||||||
|
@ -55,7 +56,6 @@ use util::{ResultExt, TryFutureExt};
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
|
||||||
pub struct WorktreeId(usize);
|
pub struct WorktreeId(usize);
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
|
||||||
pub enum Worktree {
|
pub enum Worktree {
|
||||||
Local(LocalWorktree),
|
Local(LocalWorktree),
|
||||||
Remote(RemoteWorktree),
|
Remote(RemoteWorktree),
|
||||||
|
@ -63,7 +63,7 @@ pub enum Worktree {
|
||||||
|
|
||||||
pub struct LocalWorktree {
|
pub struct LocalWorktree {
|
||||||
snapshot: LocalSnapshot,
|
snapshot: LocalSnapshot,
|
||||||
background_snapshot: Arc<Mutex<LocalSnapshot>>,
|
path_changes_tx: mpsc::UnboundedSender<(Vec<PathBuf>, barrier::Sender)>,
|
||||||
is_scanning: (watch::Sender<bool>, watch::Receiver<bool>),
|
is_scanning: (watch::Sender<bool>, watch::Receiver<bool>),
|
||||||
_background_scanner_task: Task<()>,
|
_background_scanner_task: Task<()>,
|
||||||
share: Option<ShareState>,
|
share: Option<ShareState>,
|
||||||
|
@ -156,14 +156,17 @@ impl DerefMut for LocalSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
enum ScanState {
|
enum ScanState {
|
||||||
/// The worktree is performing its initial scan of the filesystem.
|
/// The worktree is performing its initial scan of the filesystem.
|
||||||
Initializing(LocalSnapshot),
|
Initializing(LocalSnapshot),
|
||||||
Initialized(LocalSnapshot),
|
Initialized(LocalSnapshot),
|
||||||
/// The worktree is updating in response to filesystem events.
|
/// The worktree is updating in response to filesystem events.
|
||||||
Updating,
|
Updating,
|
||||||
Updated(LocalSnapshot, HashMap<Arc<Path>, PathChange>),
|
Updated {
|
||||||
|
snapshot: LocalSnapshot,
|
||||||
|
changes: HashMap<Arc<Path>, PathChange>,
|
||||||
|
barrier: Option<barrier::Sender>,
|
||||||
|
},
|
||||||
Err(Arc<anyhow::Error>),
|
Err(Arc<anyhow::Error>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,8 +237,8 @@ impl Worktree {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (path_changes_tx, path_changes_rx) = mpsc::unbounded();
|
||||||
let (scan_states_tx, mut scan_states_rx) = mpsc::unbounded();
|
let (scan_states_tx, mut scan_states_rx) = mpsc::unbounded();
|
||||||
let background_snapshot = Arc::new(Mutex::new(snapshot.clone()));
|
|
||||||
|
|
||||||
cx.spawn_weak(|this, mut cx| async move {
|
cx.spawn_weak(|this, mut cx| async move {
|
||||||
while let Some((state, this)) = scan_states_rx.next().await.zip(this.upgrade(&cx)) {
|
while let Some((state, this)) = scan_states_rx.next().await.zip(this.upgrade(&cx)) {
|
||||||
|
@ -250,21 +253,21 @@ impl Worktree {
|
||||||
|
|
||||||
let background_scanner_task = cx.background().spawn({
|
let background_scanner_task = cx.background().spawn({
|
||||||
let fs = fs.clone();
|
let fs = fs.clone();
|
||||||
let background_snapshot = background_snapshot.clone();
|
let snapshot = snapshot.clone();
|
||||||
let background = cx.background().clone();
|
let background = cx.background().clone();
|
||||||
async move {
|
async move {
|
||||||
let events = fs.watch(&abs_path, Duration::from_millis(100)).await;
|
let events = fs.watch(&abs_path, Duration::from_millis(100)).await;
|
||||||
BackgroundScanner::new(background_snapshot, scan_states_tx, fs, background)
|
BackgroundScanner::new(snapshot, scan_states_tx, fs, background)
|
||||||
.run(events)
|
.run(events, path_changes_rx)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Worktree::Local(LocalWorktree {
|
Worktree::Local(LocalWorktree {
|
||||||
snapshot,
|
snapshot,
|
||||||
background_snapshot,
|
|
||||||
is_scanning: watch::channel_with(true),
|
is_scanning: watch::channel_with(true),
|
||||||
share: None,
|
share: None,
|
||||||
|
path_changes_tx,
|
||||||
_background_scanner_task: background_scanner_task,
|
_background_scanner_task: background_scanner_task,
|
||||||
diagnostics: Default::default(),
|
diagnostics: Default::default(),
|
||||||
diagnostic_summaries: Default::default(),
|
diagnostic_summaries: Default::default(),
|
||||||
|
@ -546,10 +549,15 @@ impl LocalWorktree {
|
||||||
ScanState::Updating => {
|
ScanState::Updating => {
|
||||||
*self.is_scanning.0.borrow_mut() = true;
|
*self.is_scanning.0.borrow_mut() = true;
|
||||||
}
|
}
|
||||||
ScanState::Updated(new_snapshot, changes) => {
|
ScanState::Updated {
|
||||||
|
snapshot: new_snapshot,
|
||||||
|
changes,
|
||||||
|
barrier,
|
||||||
|
} => {
|
||||||
*self.is_scanning.0.borrow_mut() = false;
|
*self.is_scanning.0.borrow_mut() = false;
|
||||||
cx.emit(Event::UpdatedEntries(changes));
|
cx.emit(Event::UpdatedEntries(changes));
|
||||||
self.set_snapshot(new_snapshot, cx);
|
self.set_snapshot(new_snapshot, cx);
|
||||||
|
drop(barrier);
|
||||||
}
|
}
|
||||||
ScanState::Err(error) => {
|
ScanState::Err(error) => {
|
||||||
*self.is_scanning.0.borrow_mut() = false;
|
*self.is_scanning.0.borrow_mut() = false;
|
||||||
|
@ -660,9 +668,7 @@ impl LocalWorktree {
|
||||||
// Eagerly populate the snapshot with an updated entry for the loaded file
|
// Eagerly populate the snapshot with an updated entry for the loaded file
|
||||||
let entry = this
|
let entry = this
|
||||||
.update(&mut cx, |this, cx| {
|
.update(&mut cx, |this, cx| {
|
||||||
this.as_local()
|
this.as_local().unwrap().refresh_entry(path, None, cx)
|
||||||
.unwrap()
|
|
||||||
.refresh_entry(path, abs_path, None, cx)
|
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -780,10 +786,13 @@ impl LocalWorktree {
|
||||||
cx: &mut ModelContext<Worktree>,
|
cx: &mut ModelContext<Worktree>,
|
||||||
) -> Option<Task<Result<()>>> {
|
) -> Option<Task<Result<()>>> {
|
||||||
let entry = self.entry_for_id(entry_id)?.clone();
|
let entry = self.entry_for_id(entry_id)?.clone();
|
||||||
let abs_path = self.absolutize(&entry.path);
|
let path = entry.path.clone();
|
||||||
|
let abs_path = self.absolutize(&path);
|
||||||
|
let (tx, mut rx) = barrier::channel();
|
||||||
|
|
||||||
let delete = cx.background().spawn({
|
let delete = cx.background().spawn({
|
||||||
|
let abs_path = abs_path.clone();
|
||||||
let fs = self.fs.clone();
|
let fs = self.fs.clone();
|
||||||
let abs_path = abs_path;
|
|
||||||
async move {
|
async move {
|
||||||
if entry.is_file() {
|
if entry.is_file() {
|
||||||
fs.remove_file(&abs_path, Default::default()).await
|
fs.remove_file(&abs_path, Default::default()).await
|
||||||
|
@ -802,17 +811,14 @@ impl LocalWorktree {
|
||||||
|
|
||||||
Some(cx.spawn(|this, mut cx| async move {
|
Some(cx.spawn(|this, mut cx| async move {
|
||||||
delete.await?;
|
delete.await?;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, _| {
|
||||||
let this = this.as_local_mut().unwrap();
|
this.as_local_mut()
|
||||||
|
.unwrap()
|
||||||
this.background_snapshot.lock().delete_entry(entry_id);
|
.path_changes_tx
|
||||||
|
.unbounded_send((vec![abs_path], tx))
|
||||||
if let Some(path) = this.snapshot.delete_entry(entry_id) {
|
.unwrap();
|
||||||
cx.emit(Event::UpdatedEntries(
|
|
||||||
[(path, PathChange::Removed)].into_iter().collect(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
rx.recv().await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -826,29 +832,21 @@ impl LocalWorktree {
|
||||||
let old_path = self.entry_for_id(entry_id)?.path.clone();
|
let old_path = self.entry_for_id(entry_id)?.path.clone();
|
||||||
let new_path = new_path.into();
|
let new_path = new_path.into();
|
||||||
let abs_old_path = self.absolutize(&old_path);
|
let abs_old_path = self.absolutize(&old_path);
|
||||||
let abs_new_path = self.absolutize(new_path.as_ref());
|
let abs_new_path = self.absolutize(&new_path);
|
||||||
let rename = cx.background().spawn({
|
let fs = self.fs.clone();
|
||||||
let fs = self.fs.clone();
|
let rename = cx.background().spawn(async move {
|
||||||
let abs_new_path = abs_new_path.clone();
|
fs.rename(&abs_old_path, &abs_new_path, Default::default())
|
||||||
async move {
|
.await
|
||||||
fs.rename(&abs_old_path, &abs_new_path, Default::default())
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(cx.spawn(|this, mut cx| async move {
|
Some(cx.spawn(|this, mut cx| async move {
|
||||||
rename.await?;
|
rename.await?;
|
||||||
let entry = this
|
this.update(&mut cx, |this, cx| {
|
||||||
.update(&mut cx, |this, cx| {
|
this.as_local_mut()
|
||||||
this.as_local_mut().unwrap().refresh_entry(
|
.unwrap()
|
||||||
new_path.clone(),
|
.refresh_entry(new_path.clone(), Some(old_path), cx)
|
||||||
abs_new_path,
|
})
|
||||||
Some(old_path),
|
.await
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
Ok(entry)
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -862,33 +860,25 @@ impl LocalWorktree {
|
||||||
let new_path = new_path.into();
|
let new_path = new_path.into();
|
||||||
let abs_old_path = self.absolutize(&old_path);
|
let abs_old_path = self.absolutize(&old_path);
|
||||||
let abs_new_path = self.absolutize(&new_path);
|
let abs_new_path = self.absolutize(&new_path);
|
||||||
let copy = cx.background().spawn({
|
let fs = self.fs.clone();
|
||||||
let fs = self.fs.clone();
|
let copy = cx.background().spawn(async move {
|
||||||
let abs_new_path = abs_new_path.clone();
|
copy_recursive(
|
||||||
async move {
|
fs.as_ref(),
|
||||||
copy_recursive(
|
&abs_old_path,
|
||||||
fs.as_ref(),
|
&abs_new_path,
|
||||||
&abs_old_path,
|
Default::default(),
|
||||||
&abs_new_path,
|
)
|
||||||
Default::default(),
|
.await
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(cx.spawn(|this, mut cx| async move {
|
Some(cx.spawn(|this, mut cx| async move {
|
||||||
copy.await?;
|
copy.await?;
|
||||||
let entry = this
|
this.update(&mut cx, |this, cx| {
|
||||||
.update(&mut cx, |this, cx| {
|
this.as_local_mut()
|
||||||
this.as_local_mut().unwrap().refresh_entry(
|
.unwrap()
|
||||||
new_path.clone(),
|
.refresh_entry(new_path.clone(), None, cx)
|
||||||
abs_new_path,
|
})
|
||||||
None,
|
.await
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
Ok(entry)
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -900,90 +890,51 @@ impl LocalWorktree {
|
||||||
) -> Task<Result<Entry>> {
|
) -> Task<Result<Entry>> {
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
let abs_path = self.absolutize(&path);
|
let abs_path = self.absolutize(&path);
|
||||||
let write = cx.background().spawn({
|
let fs = self.fs.clone();
|
||||||
let fs = self.fs.clone();
|
let write = cx.background().spawn(async move {
|
||||||
let abs_path = abs_path.clone();
|
if let Some((text, line_ending)) = text_if_file {
|
||||||
async move {
|
fs.save(&abs_path, &text, line_ending).await
|
||||||
if let Some((text, line_ending)) = text_if_file {
|
} else {
|
||||||
fs.save(&abs_path, &text, line_ending).await
|
fs.create_dir(&abs_path).await
|
||||||
} else {
|
|
||||||
fs.create_dir(&abs_path).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
write.await?;
|
write.await?;
|
||||||
let entry = this
|
this.update(&mut cx, |this, cx| {
|
||||||
.update(&mut cx, |this, cx| {
|
this.as_local_mut().unwrap().refresh_entry(path, None, cx)
|
||||||
this.as_local_mut()
|
})
|
||||||
.unwrap()
|
.await
|
||||||
.refresh_entry(path, abs_path, None, cx)
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
Ok(entry)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh_entry(
|
fn refresh_entry(
|
||||||
&self,
|
&self,
|
||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
abs_path: PathBuf,
|
|
||||||
old_path: Option<Arc<Path>>,
|
old_path: Option<Arc<Path>>,
|
||||||
cx: &mut ModelContext<Worktree>,
|
cx: &mut ModelContext<Worktree>,
|
||||||
) -> Task<Result<Entry>> {
|
) -> Task<Result<Entry>> {
|
||||||
let fs = self.fs.clone();
|
let fs = self.fs.clone();
|
||||||
let root_char_bag = self.snapshot.root_char_bag;
|
let abs_path = self.abs_path.clone();
|
||||||
let next_entry_id = self.snapshot.next_entry_id.clone();
|
let path_changes_tx = self.path_changes_tx.clone();
|
||||||
cx.spawn_weak(|this, mut cx| async move {
|
cx.spawn_weak(move |this, mut cx| async move {
|
||||||
let metadata = fs
|
let abs_path = fs.canonicalize(&abs_path).await?;
|
||||||
.metadata(&abs_path)
|
let paths = if let Some(old_path) = old_path {
|
||||||
.await?
|
vec![abs_path.join(&path), abs_path.join(&old_path)]
|
||||||
.ok_or_else(|| anyhow!("could not read saved file metadata"))?;
|
} else {
|
||||||
let this = this
|
vec![abs_path.join(&path)]
|
||||||
.upgrade(&cx)
|
};
|
||||||
.ok_or_else(|| anyhow!("worktree was dropped"))?;
|
|
||||||
this.update(&mut cx, |this, cx| {
|
|
||||||
let this = this.as_local_mut().unwrap();
|
|
||||||
let mut entry = Entry::new(path, &metadata, &next_entry_id, root_char_bag);
|
|
||||||
entry.is_ignored = this
|
|
||||||
.snapshot
|
|
||||||
.ignore_stack_for_abs_path(&abs_path, entry.is_dir())
|
|
||||||
.is_abs_path_ignored(&abs_path, entry.is_dir());
|
|
||||||
|
|
||||||
{
|
let (tx, mut rx) = barrier::channel();
|
||||||
let mut snapshot = this.background_snapshot.lock();
|
path_changes_tx.unbounded_send((paths, tx)).unwrap();
|
||||||
snapshot.scan_started();
|
rx.recv().await;
|
||||||
if let Some(old_path) = &old_path {
|
this.upgrade(&cx)
|
||||||
snapshot.remove_path(old_path);
|
.ok_or_else(|| anyhow!("worktree was dropped"))?
|
||||||
}
|
.update(&mut cx, |this, _| {
|
||||||
snapshot.insert_entry(entry.clone(), fs.as_ref());
|
this.entry_for_path(path)
|
||||||
snapshot.scan_completed();
|
.cloned()
|
||||||
}
|
.ok_or_else(|| anyhow!("failed to read path after update"))
|
||||||
|
})
|
||||||
let mut changes = HashMap::default();
|
|
||||||
|
|
||||||
this.snapshot.scan_started();
|
|
||||||
if let Some(old_path) = &old_path {
|
|
||||||
this.snapshot.remove_path(old_path);
|
|
||||||
changes.insert(old_path.clone(), PathChange::Removed);
|
|
||||||
}
|
|
||||||
let exists = this.snapshot.entry_for_path(&entry.path).is_some();
|
|
||||||
let inserted_entry = this.snapshot.insert_entry(entry, fs.as_ref());
|
|
||||||
changes.insert(
|
|
||||||
inserted_entry.path.clone(),
|
|
||||||
if exists {
|
|
||||||
PathChange::Updated
|
|
||||||
} else {
|
|
||||||
PathChange::Added
|
|
||||||
},
|
|
||||||
);
|
|
||||||
this.snapshot.scan_completed();
|
|
||||||
|
|
||||||
eprintln!("refreshed {:?}", changes);
|
|
||||||
cx.emit(Event::UpdatedEntries(changes));
|
|
||||||
Ok(inserted_entry)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2188,14 +2139,14 @@ struct BackgroundScanner {
|
||||||
|
|
||||||
impl BackgroundScanner {
|
impl BackgroundScanner {
|
||||||
fn new(
|
fn new(
|
||||||
snapshot: Arc<Mutex<LocalSnapshot>>,
|
snapshot: LocalSnapshot,
|
||||||
notify: UnboundedSender<ScanState>,
|
notify: UnboundedSender<ScanState>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
executor: Arc<executor::Background>,
|
executor: Arc<executor::Background>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
fs,
|
fs,
|
||||||
snapshot,
|
snapshot: Arc::new(Mutex::new(snapshot)),
|
||||||
notify,
|
notify,
|
||||||
executor,
|
executor,
|
||||||
changes: Default::default(),
|
changes: Default::default(),
|
||||||
|
@ -2206,7 +2157,13 @@ impl BackgroundScanner {
|
||||||
self.snapshot.lock().abs_path.clone()
|
self.snapshot.lock().abs_path.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(mut self, events_rx: impl Stream<Item = Vec<fsevent::Event>>) {
|
async fn run(
|
||||||
|
mut self,
|
||||||
|
events_rx: impl Stream<Item = Vec<fsevent::Event>>,
|
||||||
|
mut changed_paths: UnboundedReceiver<(Vec<PathBuf>, barrier::Sender)>,
|
||||||
|
) {
|
||||||
|
use futures::{select_biased, FutureExt as _};
|
||||||
|
|
||||||
// While performing the initial scan, send a new snapshot to the main
|
// While performing the initial scan, send a new snapshot to the main
|
||||||
// thread on a recurring interval.
|
// thread on a recurring interval.
|
||||||
let initializing_task = self.executor.spawn({
|
let initializing_task = self.executor.spawn({
|
||||||
|
@ -2260,7 +2217,7 @@ impl BackgroundScanner {
|
||||||
// Process any events that occurred while performing the initial scan. These
|
// Process any events that occurred while performing the initial scan. These
|
||||||
// events can't be reported as precisely, because there is no snapshot of the
|
// events can't be reported as precisely, because there is no snapshot of the
|
||||||
// worktree before they occurred.
|
// worktree before they occurred.
|
||||||
if let Some(mut events) = events_rx.next().await {
|
if let Poll::Ready(Some(mut events)) = futures::poll!(events_rx.next()) {
|
||||||
while let Poll::Ready(Some(additional_events)) = futures::poll!(events_rx.next()) {
|
while let Poll::Ready(Some(additional_events)) = futures::poll!(events_rx.next()) {
|
||||||
events.extend(additional_events);
|
events.extend(additional_events);
|
||||||
}
|
}
|
||||||
|
@ -2272,10 +2229,11 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
if self
|
if self
|
||||||
.notify
|
.notify
|
||||||
.unbounded_send(ScanState::Updated(
|
.unbounded_send(ScanState::Updated {
|
||||||
self.snapshot.lock().clone(),
|
snapshot: self.snapshot.lock().clone(),
|
||||||
mem::take(&mut self.changes),
|
changes: mem::take(&mut self.changes),
|
||||||
))
|
barrier: None,
|
||||||
|
})
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -2283,10 +2241,29 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue processing events until the worktree is dropped.
|
// Continue processing events until the worktree is dropped.
|
||||||
while let Some(mut events) = events_rx.next().await {
|
loop {
|
||||||
while let Poll::Ready(Some(additional_events)) = futures::poll!(events_rx.next()) {
|
let events;
|
||||||
events.extend(additional_events);
|
let barrier;
|
||||||
|
select_biased! {
|
||||||
|
request = changed_paths.next().fuse() => {
|
||||||
|
let Some((paths, b)) = request else { break; };
|
||||||
|
events = paths
|
||||||
|
.into_iter()
|
||||||
|
.map(|path| fsevent::Event {
|
||||||
|
path,
|
||||||
|
event_id: 0,
|
||||||
|
flags: fsevent::StreamFlags::NONE
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
barrier = Some(b);
|
||||||
|
}
|
||||||
|
e = events_rx.next().fuse() => {
|
||||||
|
let Some(e) = e else { break; };
|
||||||
|
events = e;
|
||||||
|
barrier = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.notify.unbounded_send(ScanState::Updating).is_err() {
|
if self.notify.unbounded_send(ScanState::Updating).is_err() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2295,10 +2272,11 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
if self
|
if self
|
||||||
.notify
|
.notify
|
||||||
.unbounded_send(ScanState::Updated(
|
.unbounded_send(ScanState::Updated {
|
||||||
self.snapshot.lock().clone(),
|
snapshot: self.snapshot.lock().clone(),
|
||||||
mem::take(&mut self.changes),
|
changes: mem::take(&mut self.changes),
|
||||||
))
|
barrier,
|
||||||
|
})
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -3132,7 +3110,7 @@ mod tests {
|
||||||
|
|
||||||
let tree = Worktree::local(
|
let tree = Worktree::local(
|
||||||
client,
|
client,
|
||||||
Arc::from(Path::new("/root")),
|
Path::new("/root"),
|
||||||
true,
|
true,
|
||||||
fs,
|
fs,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
@ -3193,7 +3171,7 @@ mod tests {
|
||||||
let client = cx.read(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
|
let client = cx.read(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
|
||||||
let tree = Worktree::local(
|
let tree = Worktree::local(
|
||||||
client,
|
client,
|
||||||
Arc::from(Path::new("/root")),
|
Path::new("/root"),
|
||||||
true,
|
true,
|
||||||
fs.clone(),
|
fs.clone(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
|
Loading…
Reference in a new issue