mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-05 10:20:51 +00:00
Include paths loaded during initial scan in worktree UpdatedEntries event
This commit is contained in:
parent
7900d2a20a
commit
f890eefdef
2 changed files with 162 additions and 150 deletions
|
@ -5025,22 +5025,20 @@ impl Project {
|
||||||
changes: changes
|
changes: changes
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|((path, _), change)| {
|
.filter_map(|((path, _), change)| {
|
||||||
if watched_paths.is_match(&path) {
|
if !watched_paths.is_match(&path) {
|
||||||
Some(lsp::FileEvent {
|
return None;
|
||||||
uri: lsp::Url::from_file_path(abs_path.join(path))
|
}
|
||||||
.unwrap(),
|
let typ = match change {
|
||||||
typ: match change {
|
PathChange::Loaded => return None,
|
||||||
PathChange::Added => lsp::FileChangeType::CREATED,
|
PathChange::Added => lsp::FileChangeType::CREATED,
|
||||||
PathChange::Removed => lsp::FileChangeType::DELETED,
|
PathChange::Removed => lsp::FileChangeType::DELETED,
|
||||||
PathChange::Updated
|
PathChange::Updated => lsp::FileChangeType::CHANGED,
|
||||||
| PathChange::AddedOrUpdated => {
|
PathChange::AddedOrUpdated => lsp::FileChangeType::CHANGED,
|
||||||
lsp::FileChangeType::CHANGED
|
};
|
||||||
}
|
Some(lsp::FileEvent {
|
||||||
},
|
uri: lsp::Url::from_file_path(abs_path.join(path)).unwrap(),
|
||||||
|
typ,
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -317,13 +317,15 @@ pub struct LocalSnapshot {
|
||||||
git_repositories: TreeMap<ProjectEntryId, LocalRepositoryEntry>,
|
git_repositories: TreeMap<ProjectEntryId, LocalRepositoryEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LocalMutableSnapshot {
|
pub struct BackgroundScannerState {
|
||||||
snapshot: LocalSnapshot,
|
snapshot: LocalSnapshot,
|
||||||
/// The ids of all of the entries that were removed from the snapshot
|
/// The ids of all of the entries that were removed from the snapshot
|
||||||
/// as part of the current update. These entry ids may be re-used
|
/// as part of the current update. These entry ids may be re-used
|
||||||
/// if the same inode is discovered at a new path, or if the given
|
/// if the same inode is discovered at a new path, or if the given
|
||||||
/// path is re-created after being deleted.
|
/// path is re-created after being deleted.
|
||||||
removed_entry_ids: HashMap<u64, ProjectEntryId>,
|
removed_entry_ids: HashMap<u64, ProjectEntryId>,
|
||||||
|
changed_paths: Vec<Arc<Path>>,
|
||||||
|
prev_snapshot: Snapshot,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -357,20 +359,6 @@ impl DerefMut for LocalSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for LocalMutableSnapshot {
|
|
||||||
type Target = LocalSnapshot;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.snapshot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for LocalMutableSnapshot {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.snapshot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ScanState {
|
enum ScanState {
|
||||||
Started,
|
Started,
|
||||||
Updated {
|
Updated {
|
||||||
|
@ -2090,11 +2078,11 @@ impl LocalSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocalMutableSnapshot {
|
impl BackgroundScannerState {
|
||||||
fn reuse_entry_id(&mut self, entry: &mut Entry) {
|
fn reuse_entry_id(&mut self, entry: &mut Entry) {
|
||||||
if let Some(removed_entry_id) = self.removed_entry_ids.remove(&entry.inode) {
|
if let Some(removed_entry_id) = self.removed_entry_ids.remove(&entry.inode) {
|
||||||
entry.id = removed_entry_id;
|
entry.id = removed_entry_id;
|
||||||
} else if let Some(existing_entry) = self.entry_for_path(&entry.path) {
|
} else if let Some(existing_entry) = self.snapshot.entry_for_path(&entry.path) {
|
||||||
entry.id = existing_entry.id;
|
entry.id = existing_entry.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2111,8 +2099,10 @@ impl LocalMutableSnapshot {
|
||||||
ignore: Option<Arc<Gitignore>>,
|
ignore: Option<Arc<Gitignore>>,
|
||||||
fs: &dyn Fs,
|
fs: &dyn Fs,
|
||||||
) {
|
) {
|
||||||
let mut parent_entry = if let Some(parent_entry) =
|
let mut parent_entry = if let Some(parent_entry) = self
|
||||||
self.entries_by_path.get(&PathKey(parent_path.clone()), &())
|
.snapshot
|
||||||
|
.entries_by_path
|
||||||
|
.get(&PathKey(parent_path.clone()), &())
|
||||||
{
|
{
|
||||||
parent_entry.clone()
|
parent_entry.clone()
|
||||||
} else {
|
} else {
|
||||||
|
@ -2132,13 +2122,14 @@ impl LocalMutableSnapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ignore) = ignore {
|
if let Some(ignore) = ignore {
|
||||||
let abs_parent_path = self.abs_path.join(&parent_path).into();
|
let abs_parent_path = self.snapshot.abs_path.join(&parent_path).into();
|
||||||
self.ignores_by_parent_abs_path
|
self.snapshot
|
||||||
|
.ignores_by_parent_abs_path
|
||||||
.insert(abs_parent_path, (ignore, false));
|
.insert(abs_parent_path, (ignore, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
if parent_path.file_name() == Some(&DOT_GIT) {
|
if parent_path.file_name() == Some(&DOT_GIT) {
|
||||||
self.build_repo(parent_path, fs);
|
self.snapshot.build_repo(parent_path, fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut entries_by_path_edits = vec![Edit::Insert(parent_entry)];
|
let mut entries_by_path_edits = vec![Edit::Insert(parent_entry)];
|
||||||
|
@ -2150,25 +2141,27 @@ impl LocalMutableSnapshot {
|
||||||
id: entry.id,
|
id: entry.id,
|
||||||
path: entry.path.clone(),
|
path: entry.path.clone(),
|
||||||
is_ignored: entry.is_ignored,
|
is_ignored: entry.is_ignored,
|
||||||
scan_id: self.scan_id,
|
scan_id: self.snapshot.scan_id,
|
||||||
}));
|
}));
|
||||||
entries_by_path_edits.push(Edit::Insert(entry));
|
entries_by_path_edits.push(Edit::Insert(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.entries_by_path.edit(entries_by_path_edits, &());
|
self.snapshot
|
||||||
self.entries_by_id.edit(entries_by_id_edits, &());
|
.entries_by_path
|
||||||
|
.edit(entries_by_path_edits, &());
|
||||||
|
self.snapshot.entries_by_id.edit(entries_by_id_edits, &());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_path(&mut self, path: &Path) {
|
fn remove_path(&mut self, path: &Path) {
|
||||||
let mut new_entries;
|
let mut new_entries;
|
||||||
let removed_entries;
|
let removed_entries;
|
||||||
{
|
{
|
||||||
let mut cursor = self.entries_by_path.cursor::<TraversalProgress>();
|
let mut cursor = self.snapshot.entries_by_path.cursor::<TraversalProgress>();
|
||||||
new_entries = cursor.slice(&TraversalTarget::Path(path), Bias::Left, &());
|
new_entries = cursor.slice(&TraversalTarget::Path(path), Bias::Left, &());
|
||||||
removed_entries = cursor.slice(&TraversalTarget::PathSuccessor(path), Bias::Left, &());
|
removed_entries = cursor.slice(&TraversalTarget::PathSuccessor(path), Bias::Left, &());
|
||||||
new_entries.push_tree(cursor.suffix(&()), &());
|
new_entries.push_tree(cursor.suffix(&()), &());
|
||||||
}
|
}
|
||||||
self.entries_by_path = new_entries;
|
self.snapshot.entries_by_path = new_entries;
|
||||||
|
|
||||||
let mut entries_by_id_edits = Vec::new();
|
let mut entries_by_id_edits = Vec::new();
|
||||||
for entry in removed_entries.cursor::<()>() {
|
for entry in removed_entries.cursor::<()>() {
|
||||||
|
@ -2179,11 +2172,12 @@ impl LocalMutableSnapshot {
|
||||||
*removed_entry_id = cmp::max(*removed_entry_id, entry.id);
|
*removed_entry_id = cmp::max(*removed_entry_id, entry.id);
|
||||||
entries_by_id_edits.push(Edit::Remove(entry.id));
|
entries_by_id_edits.push(Edit::Remove(entry.id));
|
||||||
}
|
}
|
||||||
self.entries_by_id.edit(entries_by_id_edits, &());
|
self.snapshot.entries_by_id.edit(entries_by_id_edits, &());
|
||||||
|
|
||||||
if path.file_name() == Some(&GITIGNORE) {
|
if path.file_name() == Some(&GITIGNORE) {
|
||||||
let abs_parent_path = self.abs_path.join(path.parent().unwrap());
|
let abs_parent_path = self.snapshot.abs_path.join(path.parent().unwrap());
|
||||||
if let Some((_, needs_update)) = self
|
if let Some((_, needs_update)) = self
|
||||||
|
.snapshot
|
||||||
.ignores_by_parent_abs_path
|
.ignores_by_parent_abs_path
|
||||||
.get_mut(abs_parent_path.as_path())
|
.get_mut(abs_parent_path.as_path())
|
||||||
{
|
{
|
||||||
|
@ -2473,10 +2467,18 @@ pub enum EntryKind {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum PathChange {
|
pub enum PathChange {
|
||||||
|
/// A filesystem entry was was created.
|
||||||
Added,
|
Added,
|
||||||
|
/// A filesystem entry was removed.
|
||||||
Removed,
|
Removed,
|
||||||
|
/// A filesystem entry was updated.
|
||||||
Updated,
|
Updated,
|
||||||
|
/// A filesystem entry was either updated or added. We don't know
|
||||||
|
/// whether or not it already existed, because the path had not
|
||||||
|
/// been loaded before the event.
|
||||||
AddedOrUpdated,
|
AddedOrUpdated,
|
||||||
|
/// A filesystem entry was found during the initial scan of the worktree.
|
||||||
|
Loaded,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entry {
|
impl Entry {
|
||||||
|
@ -2635,19 +2637,20 @@ impl<'a> sum_tree::Dimension<'a, EntrySummary> for PathKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BackgroundScanner {
|
struct BackgroundScanner {
|
||||||
snapshot: Mutex<LocalMutableSnapshot>,
|
state: Mutex<BackgroundScannerState>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
status_updates_tx: UnboundedSender<ScanState>,
|
status_updates_tx: UnboundedSender<ScanState>,
|
||||||
executor: Arc<executor::Background>,
|
executor: Arc<executor::Background>,
|
||||||
refresh_requests_rx: channel::Receiver<(Vec<PathBuf>, barrier::Sender)>,
|
refresh_requests_rx: channel::Receiver<(Vec<PathBuf>, barrier::Sender)>,
|
||||||
prev_state: Mutex<BackgroundScannerState>,
|
|
||||||
next_entry_id: Arc<AtomicUsize>,
|
next_entry_id: Arc<AtomicUsize>,
|
||||||
finished_initial_scan: bool,
|
phase: BackgroundScannerPhase,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BackgroundScannerState {
|
#[derive(PartialEq)]
|
||||||
snapshot: Snapshot,
|
enum BackgroundScannerPhase {
|
||||||
event_paths: Vec<Arc<Path>>,
|
InitialScan,
|
||||||
|
EventsReceivedDuringInitialScan,
|
||||||
|
Events,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackgroundScanner {
|
impl BackgroundScanner {
|
||||||
|
@ -2665,15 +2668,13 @@ impl BackgroundScanner {
|
||||||
executor,
|
executor,
|
||||||
refresh_requests_rx,
|
refresh_requests_rx,
|
||||||
next_entry_id,
|
next_entry_id,
|
||||||
prev_state: Mutex::new(BackgroundScannerState {
|
state: Mutex::new(BackgroundScannerState {
|
||||||
snapshot: snapshot.snapshot.clone(),
|
prev_snapshot: snapshot.snapshot.clone(),
|
||||||
event_paths: Default::default(),
|
|
||||||
}),
|
|
||||||
snapshot: Mutex::new(LocalMutableSnapshot {
|
|
||||||
snapshot,
|
snapshot,
|
||||||
removed_entry_ids: Default::default(),
|
removed_entry_ids: Default::default(),
|
||||||
|
changed_paths: Default::default(),
|
||||||
}),
|
}),
|
||||||
finished_initial_scan: false,
|
phase: BackgroundScannerPhase::InitialScan,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2684,7 +2685,7 @@ impl BackgroundScanner {
|
||||||
use futures::FutureExt as _;
|
use futures::FutureExt as _;
|
||||||
|
|
||||||
let (root_abs_path, root_inode) = {
|
let (root_abs_path, root_inode) = {
|
||||||
let snapshot = self.snapshot.lock();
|
let snapshot = &self.state.lock().snapshot;
|
||||||
(
|
(
|
||||||
snapshot.abs_path.clone(),
|
snapshot.abs_path.clone(),
|
||||||
snapshot.root_entry().map(|e| e.inode),
|
snapshot.root_entry().map(|e| e.inode),
|
||||||
|
@ -2696,20 +2697,23 @@ impl BackgroundScanner {
|
||||||
for ancestor in root_abs_path.ancestors().skip(1) {
|
for ancestor in root_abs_path.ancestors().skip(1) {
|
||||||
if let Ok(ignore) = build_gitignore(&ancestor.join(&*GITIGNORE), self.fs.as_ref()).await
|
if let Ok(ignore) = build_gitignore(&ancestor.join(&*GITIGNORE), self.fs.as_ref()).await
|
||||||
{
|
{
|
||||||
self.snapshot
|
self.state
|
||||||
.lock()
|
.lock()
|
||||||
|
.snapshot
|
||||||
.ignores_by_parent_abs_path
|
.ignores_by_parent_abs_path
|
||||||
.insert(ancestor.into(), (ignore.into(), false));
|
.insert(ancestor.into(), (ignore.into(), false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let mut snapshot = self.snapshot.lock();
|
let mut state = self.state.lock();
|
||||||
snapshot.scan_id += 1;
|
state.snapshot.scan_id += 1;
|
||||||
ignore_stack = snapshot.ignore_stack_for_abs_path(&root_abs_path, true);
|
ignore_stack = state
|
||||||
|
.snapshot
|
||||||
|
.ignore_stack_for_abs_path(&root_abs_path, true);
|
||||||
if ignore_stack.is_all() {
|
if ignore_stack.is_all() {
|
||||||
if let Some(mut root_entry) = snapshot.root_entry().cloned() {
|
if let Some(mut root_entry) = state.snapshot.root_entry().cloned() {
|
||||||
root_entry.is_ignored = true;
|
root_entry.is_ignored = true;
|
||||||
snapshot.insert_entry(root_entry, self.fs.as_ref());
|
state.insert_entry(root_entry, self.fs.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2727,14 +2731,15 @@ impl BackgroundScanner {
|
||||||
drop(scan_job_tx);
|
drop(scan_job_tx);
|
||||||
self.scan_dirs(true, scan_job_rx).await;
|
self.scan_dirs(true, scan_job_rx).await;
|
||||||
{
|
{
|
||||||
let mut snapshot = self.snapshot.lock();
|
let mut state = self.state.lock();
|
||||||
snapshot.completed_scan_id = snapshot.scan_id;
|
state.snapshot.completed_scan_id = state.snapshot.scan_id;
|
||||||
}
|
}
|
||||||
self.send_status_update(false, None);
|
self.send_status_update(false, None);
|
||||||
|
|
||||||
// Process any any FS events that occurred while performing the initial scan.
|
// Process any any FS events that occurred while performing the initial scan.
|
||||||
// For these events, update events cannot be as precise, because we didn't
|
// For these events, update events cannot be as precise, because we didn't
|
||||||
// have the previous state loaded yet.
|
// have the previous state loaded yet.
|
||||||
|
self.phase = BackgroundScannerPhase::EventsReceivedDuringInitialScan;
|
||||||
if let Poll::Ready(Some(events)) = futures::poll!(events_rx.next()) {
|
if let Poll::Ready(Some(events)) = futures::poll!(events_rx.next()) {
|
||||||
let mut paths = events.into_iter().map(|e| e.path).collect::<Vec<_>>();
|
let mut paths = events.into_iter().map(|e| e.path).collect::<Vec<_>>();
|
||||||
while let Poll::Ready(Some(more_events)) = futures::poll!(events_rx.next()) {
|
while let Poll::Ready(Some(more_events)) = futures::poll!(events_rx.next()) {
|
||||||
|
@ -2743,9 +2748,8 @@ impl BackgroundScanner {
|
||||||
self.process_events(paths).await;
|
self.process_events(paths).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finished_initial_scan = true;
|
|
||||||
|
|
||||||
// Continue processing events until the worktree is dropped.
|
// Continue processing events until the worktree is dropped.
|
||||||
|
self.phase = BackgroundScannerPhase::Events;
|
||||||
loop {
|
loop {
|
||||||
select_biased! {
|
select_biased! {
|
||||||
// Process any path refresh requests from the worktree. Prioritize
|
// Process any path refresh requests from the worktree. Prioritize
|
||||||
|
@ -2770,15 +2774,7 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn process_refresh_request(&self, paths: Vec<PathBuf>, barrier: barrier::Sender) -> bool {
|
async fn process_refresh_request(&self, paths: Vec<PathBuf>, barrier: barrier::Sender) -> bool {
|
||||||
if let Some(mut paths) = self.reload_entries_for_paths(paths, None).await {
|
self.reload_entries_for_paths(paths, None).await;
|
||||||
paths.sort_unstable();
|
|
||||||
util::extend_sorted(
|
|
||||||
&mut self.prev_state.lock().event_paths,
|
|
||||||
paths,
|
|
||||||
usize::MAX,
|
|
||||||
Ord::cmp,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
self.send_status_update(false, Some(barrier))
|
self.send_status_update(false, Some(barrier))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2787,20 +2783,13 @@ impl BackgroundScanner {
|
||||||
let paths = self
|
let paths = self
|
||||||
.reload_entries_for_paths(paths, Some(scan_job_tx.clone()))
|
.reload_entries_for_paths(paths, Some(scan_job_tx.clone()))
|
||||||
.await;
|
.await;
|
||||||
if let Some(paths) = &paths {
|
|
||||||
util::extend_sorted(
|
|
||||||
&mut self.prev_state.lock().event_paths,
|
|
||||||
paths.iter().cloned(),
|
|
||||||
usize::MAX,
|
|
||||||
Ord::cmp,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
drop(scan_job_tx);
|
drop(scan_job_tx);
|
||||||
self.scan_dirs(false, scan_job_rx).await;
|
self.scan_dirs(false, scan_job_rx).await;
|
||||||
|
|
||||||
self.update_ignore_statuses().await;
|
self.update_ignore_statuses().await;
|
||||||
|
|
||||||
let mut snapshot = self.snapshot.lock();
|
{
|
||||||
|
let mut snapshot = &mut self.state.lock().snapshot;
|
||||||
|
|
||||||
if let Some(paths) = paths {
|
if let Some(paths) = paths {
|
||||||
for path in paths {
|
for path in paths {
|
||||||
|
@ -2827,10 +2816,9 @@ impl BackgroundScanner {
|
||||||
});
|
});
|
||||||
snapshot.snapshot.repository_entries = git_repository_entries;
|
snapshot.snapshot.repository_entries = git_repository_entries;
|
||||||
snapshot.completed_scan_id = snapshot.scan_id;
|
snapshot.completed_scan_id = snapshot.scan_id;
|
||||||
drop(snapshot);
|
}
|
||||||
|
|
||||||
self.send_status_update(false, None);
|
self.send_status_update(false, None);
|
||||||
self.prev_state.lock().event_paths.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn scan_dirs(
|
async fn scan_dirs(
|
||||||
|
@ -2907,15 +2895,13 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_status_update(&self, scanning: bool, barrier: Option<barrier::Sender>) -> bool {
|
fn send_status_update(&self, scanning: bool, barrier: Option<barrier::Sender>) -> bool {
|
||||||
let mut prev_state = self.prev_state.lock();
|
let mut state = self.state.lock();
|
||||||
let new_snapshot = self.snapshot.lock().clone();
|
let new_snapshot = state.snapshot.clone();
|
||||||
let old_snapshot = mem::replace(&mut prev_state.snapshot, new_snapshot.snapshot.clone());
|
let old_snapshot = mem::replace(&mut state.prev_snapshot, new_snapshot.snapshot.clone());
|
||||||
|
|
||||||
let changes = self.build_change_set(
|
let changes =
|
||||||
&old_snapshot,
|
self.build_change_set(&old_snapshot, &new_snapshot.snapshot, &state.changed_paths);
|
||||||
&new_snapshot.snapshot,
|
state.changed_paths.clear();
|
||||||
&prev_state.event_paths,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.status_updates_tx
|
self.status_updates_tx
|
||||||
.unbounded_send(ScanState::Updated {
|
.unbounded_send(ScanState::Updated {
|
||||||
|
@ -2933,7 +2919,7 @@ impl BackgroundScanner {
|
||||||
let mut ignore_stack = job.ignore_stack.clone();
|
let mut ignore_stack = job.ignore_stack.clone();
|
||||||
let mut new_ignore = None;
|
let mut new_ignore = None;
|
||||||
let (root_abs_path, root_char_bag, next_entry_id) = {
|
let (root_abs_path, root_char_bag, next_entry_id) = {
|
||||||
let snapshot = self.snapshot.lock();
|
let snapshot = &self.state.lock().snapshot;
|
||||||
(
|
(
|
||||||
snapshot.abs_path().clone(),
|
snapshot.abs_path().clone(),
|
||||||
snapshot.root_char_bag,
|
snapshot.root_char_bag,
|
||||||
|
@ -3037,12 +3023,13 @@ impl BackgroundScanner {
|
||||||
new_entries.push(child_entry);
|
new_entries.push(child_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.snapshot.lock().populate_dir(
|
{
|
||||||
job.path.clone(),
|
let mut state = self.state.lock();
|
||||||
new_entries,
|
state.populate_dir(job.path.clone(), new_entries, new_ignore, self.fs.as_ref());
|
||||||
new_ignore,
|
if let Err(ix) = state.changed_paths.binary_search(&job.path) {
|
||||||
self.fs.as_ref(),
|
state.changed_paths.insert(ix, job.path.clone());
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for new_job in new_jobs {
|
for new_job in new_jobs {
|
||||||
if let Some(new_job) = new_job {
|
if let Some(new_job) = new_job {
|
||||||
|
@ -3063,7 +3050,7 @@ impl BackgroundScanner {
|
||||||
abs_paths.sort_unstable();
|
abs_paths.sort_unstable();
|
||||||
abs_paths.dedup_by(|a, b| a.starts_with(&b));
|
abs_paths.dedup_by(|a, b| a.starts_with(&b));
|
||||||
|
|
||||||
let root_abs_path = self.snapshot.lock().abs_path.clone();
|
let root_abs_path = self.state.lock().snapshot.abs_path.clone();
|
||||||
let root_canonical_path = self.fs.canonicalize(&root_abs_path).await.log_err()?;
|
let root_canonical_path = self.fs.canonicalize(&root_abs_path).await.log_err()?;
|
||||||
let metadata = futures::future::join_all(
|
let metadata = futures::future::join_all(
|
||||||
abs_paths
|
abs_paths
|
||||||
|
@ -3073,7 +3060,8 @@ impl BackgroundScanner {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let mut snapshot = self.snapshot.lock();
|
let mut state = self.state.lock();
|
||||||
|
let snapshot = &mut state.snapshot;
|
||||||
let is_idle = snapshot.completed_scan_id == snapshot.scan_id;
|
let is_idle = snapshot.completed_scan_id == snapshot.scan_id;
|
||||||
snapshot.scan_id += 1;
|
snapshot.scan_id += 1;
|
||||||
if is_idle && !doing_recursive_update {
|
if is_idle && !doing_recursive_update {
|
||||||
|
@ -3087,7 +3075,7 @@ impl BackgroundScanner {
|
||||||
for (abs_path, metadata) in abs_paths.iter().zip(metadata.iter()) {
|
for (abs_path, metadata) in abs_paths.iter().zip(metadata.iter()) {
|
||||||
if let Ok(path) = abs_path.strip_prefix(&root_canonical_path) {
|
if let Ok(path) = abs_path.strip_prefix(&root_canonical_path) {
|
||||||
if matches!(metadata, Ok(None)) || doing_recursive_update {
|
if matches!(metadata, Ok(None)) || doing_recursive_update {
|
||||||
snapshot.remove_path(path);
|
state.remove_path(path);
|
||||||
}
|
}
|
||||||
event_paths.push(path.into());
|
event_paths.push(path.into());
|
||||||
} else {
|
} else {
|
||||||
|
@ -3104,19 +3092,20 @@ impl BackgroundScanner {
|
||||||
|
|
||||||
match metadata {
|
match metadata {
|
||||||
Ok(Some(metadata)) => {
|
Ok(Some(metadata)) => {
|
||||||
let ignore_stack =
|
let ignore_stack = state
|
||||||
snapshot.ignore_stack_for_abs_path(&abs_path, metadata.is_dir);
|
.snapshot
|
||||||
|
.ignore_stack_for_abs_path(&abs_path, metadata.is_dir);
|
||||||
let mut fs_entry = Entry::new(
|
let mut fs_entry = Entry::new(
|
||||||
path.clone(),
|
path.clone(),
|
||||||
&metadata,
|
&metadata,
|
||||||
self.next_entry_id.as_ref(),
|
self.next_entry_id.as_ref(),
|
||||||
snapshot.root_char_bag,
|
state.snapshot.root_char_bag,
|
||||||
);
|
);
|
||||||
fs_entry.is_ignored = ignore_stack.is_all();
|
fs_entry.is_ignored = ignore_stack.is_all();
|
||||||
snapshot.insert_entry(fs_entry, self.fs.as_ref());
|
state.insert_entry(fs_entry, self.fs.as_ref());
|
||||||
|
|
||||||
if let Some(scan_queue_tx) = &scan_queue_tx {
|
if let Some(scan_queue_tx) = &scan_queue_tx {
|
||||||
let mut ancestor_inodes = snapshot.ancestor_inodes_for_path(&path);
|
let mut ancestor_inodes = state.snapshot.ancestor_inodes_for_path(&path);
|
||||||
if metadata.is_dir && !ancestor_inodes.contains(&metadata.inode) {
|
if metadata.is_dir && !ancestor_inodes.contains(&metadata.inode) {
|
||||||
ancestor_inodes.insert(metadata.inode);
|
ancestor_inodes.insert(metadata.inode);
|
||||||
smol::block_on(scan_queue_tx.send(ScanJob {
|
smol::block_on(scan_queue_tx.send(ScanJob {
|
||||||
|
@ -3131,7 +3120,7 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
self.remove_repo_path(&path, &mut snapshot);
|
self.remove_repo_path(&path, &mut state.snapshot);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// TODO - create a special 'error' entry in the entries tree to mark this
|
// TODO - create a special 'error' entry in the entries tree to mark this
|
||||||
|
@ -3140,6 +3129,13 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
util::extend_sorted(
|
||||||
|
&mut state.changed_paths,
|
||||||
|
event_paths.iter().cloned(),
|
||||||
|
usize::MAX,
|
||||||
|
Ord::cmp,
|
||||||
|
);
|
||||||
|
|
||||||
Some(event_paths)
|
Some(event_paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3161,9 +3157,7 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
let repo = snapshot.repository_for_path(&path)?;
|
let repo = snapshot.repository_for_path(&path)?;
|
||||||
|
|
||||||
let repo_path = repo.work_directory.relativize(&snapshot, &path)?;
|
let repo_path = repo.work_directory.relativize(&snapshot, &path)?;
|
||||||
|
|
||||||
let work_dir = repo.work_directory(snapshot)?;
|
let work_dir = repo.work_directory(snapshot)?;
|
||||||
let work_dir_id = repo.work_directory;
|
let work_dir_id = repo.work_directory;
|
||||||
|
|
||||||
|
@ -3276,7 +3270,7 @@ impl BackgroundScanner {
|
||||||
async fn update_ignore_statuses(&self) {
|
async fn update_ignore_statuses(&self) {
|
||||||
use futures::FutureExt as _;
|
use futures::FutureExt as _;
|
||||||
|
|
||||||
let mut snapshot = self.snapshot.lock().clone();
|
let mut snapshot = self.state.lock().snapshot.clone();
|
||||||
let mut ignores_to_update = Vec::new();
|
let mut ignores_to_update = Vec::new();
|
||||||
let mut ignores_to_delete = Vec::new();
|
let mut ignores_to_delete = Vec::new();
|
||||||
let abs_path = snapshot.abs_path.clone();
|
let abs_path = snapshot.abs_path.clone();
|
||||||
|
@ -3298,8 +3292,9 @@ impl BackgroundScanner {
|
||||||
|
|
||||||
for parent_abs_path in ignores_to_delete {
|
for parent_abs_path in ignores_to_delete {
|
||||||
snapshot.ignores_by_parent_abs_path.remove(&parent_abs_path);
|
snapshot.ignores_by_parent_abs_path.remove(&parent_abs_path);
|
||||||
self.snapshot
|
self.state
|
||||||
.lock()
|
.lock()
|
||||||
|
.snapshot
|
||||||
.ignores_by_parent_abs_path
|
.ignores_by_parent_abs_path
|
||||||
.remove(&parent_abs_path);
|
.remove(&parent_abs_path);
|
||||||
}
|
}
|
||||||
|
@ -3391,7 +3386,7 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut snapshot = self.snapshot.lock();
|
let snapshot = &mut self.state.lock().snapshot;
|
||||||
snapshot.entries_by_path.edit(entries_by_path_edits, &());
|
snapshot.entries_by_path.edit(entries_by_path_edits, &());
|
||||||
snapshot.entries_by_id.edit(entries_by_id_edits, &());
|
snapshot.entries_by_id.edit(entries_by_id_edits, &());
|
||||||
}
|
}
|
||||||
|
@ -3402,12 +3397,12 @@ impl BackgroundScanner {
|
||||||
new_snapshot: &Snapshot,
|
new_snapshot: &Snapshot,
|
||||||
event_paths: &[Arc<Path>],
|
event_paths: &[Arc<Path>],
|
||||||
) -> HashMap<(Arc<Path>, ProjectEntryId), PathChange> {
|
) -> HashMap<(Arc<Path>, ProjectEntryId), PathChange> {
|
||||||
use PathChange::{Added, AddedOrUpdated, Removed, Updated};
|
use BackgroundScannerPhase::*;
|
||||||
|
use PathChange::{Added, AddedOrUpdated, Loaded, Removed, Updated};
|
||||||
|
|
||||||
let mut changes = HashMap::default();
|
let mut changes = HashMap::default();
|
||||||
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>();
|
||||||
let received_before_initialized = !self.finished_initial_scan;
|
|
||||||
|
|
||||||
for path in event_paths {
|
for path in event_paths {
|
||||||
let path = PathKey(path.clone());
|
let path = PathKey(path.clone());
|
||||||
|
@ -3431,7 +3426,7 @@ impl BackgroundScanner {
|
||||||
old_paths.next(&());
|
old_paths.next(&());
|
||||||
}
|
}
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
if received_before_initialized {
|
if self.phase == EventsReceivedDuringInitialScan {
|
||||||
// If the worktree was not fully initialized when this event was generated,
|
// 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
|
// we can't know whether this entry was added during the scan or whether
|
||||||
// it was merely updated.
|
// it was merely updated.
|
||||||
|
@ -3446,7 +3441,14 @@ impl BackgroundScanner {
|
||||||
new_paths.next(&());
|
new_paths.next(&());
|
||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
changes.insert((new_entry.path.clone(), new_entry.id), Added);
|
changes.insert(
|
||||||
|
(new_entry.path.clone(), new_entry.id),
|
||||||
|
if self.phase == InitialScan {
|
||||||
|
Loaded
|
||||||
|
} else {
|
||||||
|
Added
|
||||||
|
},
|
||||||
|
);
|
||||||
new_paths.next(&());
|
new_paths.next(&());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3456,7 +3458,14 @@ impl BackgroundScanner {
|
||||||
old_paths.next(&());
|
old_paths.next(&());
|
||||||
}
|
}
|
||||||
(None, Some(new_entry)) => {
|
(None, Some(new_entry)) => {
|
||||||
changes.insert((new_entry.path.clone(), new_entry.id), Added);
|
changes.insert(
|
||||||
|
(new_entry.path.clone(), new_entry.id),
|
||||||
|
if self.phase == InitialScan {
|
||||||
|
Loaded
|
||||||
|
} else {
|
||||||
|
Added
|
||||||
|
},
|
||||||
|
);
|
||||||
new_paths.next(&());
|
new_paths.next(&());
|
||||||
}
|
}
|
||||||
(None, None) => break,
|
(None, None) => break,
|
||||||
|
@ -4318,12 +4327,8 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
worktree
|
// The worktree's `UpdatedEntries` event can be used to follow along with
|
||||||
.update(cx, |tree, _| tree.as_local_mut().unwrap().scan_complete())
|
// all changes to the worktree's snapshot.
|
||||||
.await;
|
|
||||||
|
|
||||||
// After the initial scan is complete, the `UpdatedEntries` event can
|
|
||||||
// be used to follow along with all changes to the worktree's snapshot.
|
|
||||||
worktree.update(cx, |tree, cx| {
|
worktree.update(cx, |tree, cx| {
|
||||||
let mut paths = tree
|
let mut paths = tree
|
||||||
.as_local()
|
.as_local()
|
||||||
|
@ -4340,6 +4345,11 @@ mod tests {
|
||||||
Ok(ix) | Err(ix) => ix,
|
Ok(ix) | Err(ix) => ix,
|
||||||
};
|
};
|
||||||
match change_type {
|
match change_type {
|
||||||
|
PathChange::Loaded => {
|
||||||
|
assert_ne!(paths.get(ix), Some(&path));
|
||||||
|
paths.insert(ix, path);
|
||||||
|
}
|
||||||
|
|
||||||
PathChange::Added => {
|
PathChange::Added => {
|
||||||
assert_ne!(paths.get(ix), Some(&path));
|
assert_ne!(paths.get(ix), Some(&path));
|
||||||
paths.insert(ix, path);
|
paths.insert(ix, path);
|
||||||
|
@ -4369,6 +4379,10 @@ mod tests {
|
||||||
.detach();
|
.detach();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
worktree
|
||||||
|
.update(cx, |tree, _| tree.as_local_mut().unwrap().scan_complete())
|
||||||
|
.await;
|
||||||
|
|
||||||
fs.as_fake().pause_events();
|
fs.as_fake().pause_events();
|
||||||
let mut snapshots = Vec::new();
|
let mut snapshots = Vec::new();
|
||||||
let mut mutations_len = operations;
|
let mut mutations_len = operations;
|
||||||
|
|
Loading…
Reference in a new issue