Conservatively report fs events that occurred during initial worktree scan
Co-authored-by: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
61172c8478
commit
be5868e1c0
2 changed files with 49 additions and 16 deletions
|
@ -4604,7 +4604,10 @@ impl Project {
|
||||||
typ: match change {
|
typ: match change {
|
||||||
PathChange::Added => lsp::FileChangeType::CREATED,
|
PathChange::Added => lsp::FileChangeType::CREATED,
|
||||||
PathChange::Removed => lsp::FileChangeType::DELETED,
|
PathChange::Removed => lsp::FileChangeType::DELETED,
|
||||||
PathChange::Updated => lsp::FileChangeType::CHANGED,
|
PathChange::Updated
|
||||||
|
| PathChange::AddedOrUpdated => {
|
||||||
|
lsp::FileChangeType::CHANGED
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|
|
@ -610,8 +610,6 @@ impl LocalWorktree {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
cx.emit(Event::UpdatedEntries(Default::default()));
|
|
||||||
|
|
||||||
if !updated_repos.is_empty() {
|
if !updated_repos.is_empty() {
|
||||||
cx.emit(Event::UpdatedGitRepositories(updated_repos));
|
cx.emit(Event::UpdatedGitRepositories(updated_repos));
|
||||||
}
|
}
|
||||||
|
@ -2072,6 +2070,7 @@ pub enum PathChange {
|
||||||
Added,
|
Added,
|
||||||
Removed,
|
Removed,
|
||||||
Updated,
|
Updated,
|
||||||
|
AddedOrUpdated,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entry {
|
impl Entry {
|
||||||
|
@ -2283,21 +2282,37 @@ impl BackgroundScanner {
|
||||||
|
|
||||||
futures::pin_mut!(events_rx);
|
futures::pin_mut!(events_rx);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// worktree before they occurred.
|
||||||
|
if let Some(mut events) = events_rx.next().await {
|
||||||
|
while let Poll::Ready(Some(additional_events)) = futures::poll!(events_rx.next()) {
|
||||||
|
events.extend(additional_events);
|
||||||
|
}
|
||||||
|
if self.notify.unbounded_send(ScanState::Updating).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if !self.process_events(events, true).await {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.notify.unbounded_send(ScanState::Idle).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue processing events until the worktree is dropped.
|
||||||
while let Some(mut events) = events_rx.next().await {
|
while let Some(mut events) = events_rx.next().await {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.notify.unbounded_send(ScanState::Updating).is_err() {
|
if self.notify.unbounded_send(ScanState::Updating).is_err() {
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
|
if !self.process_events(events, false).await {
|
||||||
if !self.process_events(events).await {
|
return;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.notify.unbounded_send(ScanState::Idle).is_err() {
|
if self.notify.unbounded_send(ScanState::Idle).is_err() {
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2508,7 +2523,11 @@ impl BackgroundScanner {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn process_events(&mut self, mut events: Vec<fsevent::Event>) -> bool {
|
async fn process_events(
|
||||||
|
&mut self,
|
||||||
|
mut events: Vec<fsevent::Event>,
|
||||||
|
received_before_initialized: bool,
|
||||||
|
) -> bool {
|
||||||
events.sort_unstable_by(|a, b| a.path.cmp(&b.path));
|
events.sort_unstable_by(|a, b| a.path.cmp(&b.path));
|
||||||
events.dedup_by(|a, b| a.path.starts_with(&b.path));
|
events.dedup_by(|a, b| a.path.starts_with(&b.path));
|
||||||
|
|
||||||
|
@ -2632,7 +2651,7 @@ impl BackgroundScanner {
|
||||||
|
|
||||||
self.update_ignore_statuses().await;
|
self.update_ignore_statuses().await;
|
||||||
self.update_git_repositories();
|
self.update_git_repositories();
|
||||||
self.build_change_set(prev_snapshot, event_paths);
|
self.build_change_set(prev_snapshot, event_paths, received_before_initialized);
|
||||||
self.snapshot.lock().scan_completed();
|
self.snapshot.lock().scan_completed();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -2749,7 +2768,12 @@ impl BackgroundScanner {
|
||||||
snapshot.entries_by_id.edit(entries_by_id_edits, &());
|
snapshot.entries_by_id.edit(entries_by_id_edits, &());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_change_set(&self, old_snapshot: Snapshot, event_paths: Vec<Arc<Path>>) {
|
fn build_change_set(
|
||||||
|
&self,
|
||||||
|
old_snapshot: Snapshot,
|
||||||
|
event_paths: Vec<Arc<Path>>,
|
||||||
|
received_before_initialized: bool,
|
||||||
|
) {
|
||||||
let new_snapshot = self.snapshot.lock();
|
let new_snapshot = self.snapshot.lock();
|
||||||
let mut old_paths = old_snapshot.entries_by_path.cursor::<PathKey>();
|
let mut old_paths = old_snapshot.entries_by_path.cursor::<PathKey>();
|
||||||
let mut new_paths = new_snapshot.entries_by_path.cursor::<PathKey>();
|
let mut new_paths = new_snapshot.entries_by_path.cursor::<PathKey>();
|
||||||
|
@ -2777,7 +2801,13 @@ impl BackgroundScanner {
|
||||||
old_paths.next(&());
|
old_paths.next(&());
|
||||||
}
|
}
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
if old_entry.mtime != new_entry.mtime {
|
if received_before_initialized {
|
||||||
|
// If the worktree was not fully initialized when this event was generated,
|
||||||
|
// we can't know whether this entry was added during the scan or whether
|
||||||
|
// it was merely updated.
|
||||||
|
change_set
|
||||||
|
.insert(old_entry.path.clone(), PathChange::AddedOrUpdated);
|
||||||
|
} else if old_entry.mtime != new_entry.mtime {
|
||||||
change_set.insert(old_entry.path.clone(), PathChange::Updated);
|
change_set.insert(old_entry.path.clone(), PathChange::Updated);
|
||||||
}
|
}
|
||||||
old_paths.next(&());
|
old_paths.next(&());
|
||||||
|
@ -3604,7 +3634,7 @@ mod tests {
|
||||||
let len = rng.gen_range(0..=events.len());
|
let len = rng.gen_range(0..=events.len());
|
||||||
let to_deliver = events.drain(0..len).collect::<Vec<_>>();
|
let to_deliver = events.drain(0..len).collect::<Vec<_>>();
|
||||||
log::info!("Delivering events: {:#?}", to_deliver);
|
log::info!("Delivering events: {:#?}", to_deliver);
|
||||||
smol::block_on(scanner.process_events(to_deliver));
|
smol::block_on(scanner.process_events(to_deliver, false));
|
||||||
scanner.snapshot().check_invariants();
|
scanner.snapshot().check_invariants();
|
||||||
} else {
|
} else {
|
||||||
events.extend(randomly_mutate_tree(root_dir.path(), 0.6, &mut rng).unwrap());
|
events.extend(randomly_mutate_tree(root_dir.path(), 0.6, &mut rng).unwrap());
|
||||||
|
@ -3616,7 +3646,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log::info!("Quiescing: {:#?}", events);
|
log::info!("Quiescing: {:#?}", events);
|
||||||
smol::block_on(scanner.process_events(events));
|
smol::block_on(scanner.process_events(events, false));
|
||||||
scanner.snapshot().check_invariants();
|
scanner.snapshot().check_invariants();
|
||||||
|
|
||||||
let (notify_tx, _notify_rx) = mpsc::unbounded();
|
let (notify_tx, _notify_rx) = mpsc::unbounded();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue