mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-03 08:54:04 +00:00
WIP
This commit is contained in:
parent
06858c023c
commit
f8f6a85ab0
1 changed files with 97 additions and 124 deletions
|
@ -252,6 +252,41 @@ impl Snapshot {
|
||||||
Ok(path)
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_entry(&mut self, inode: u64) {
|
||||||
|
if let Some(entry) = self.entries.get(&inode).cloned() {
|
||||||
|
let mut edits = Vec::new();
|
||||||
|
|
||||||
|
if let Some(parent) = entry.parent() {
|
||||||
|
let mut parent_entry = self.entries.get(&parent).unwrap().clone();
|
||||||
|
if let Entry::Dir { children, .. } = &mut parent_entry {
|
||||||
|
*children = children
|
||||||
|
.into_iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|child| *child != entry.inode())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into();
|
||||||
|
edits.push(Edit::Insert(parent_entry));
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively remove the orphaned nodes' descendants.
|
||||||
|
let mut descendant_stack = vec![entry.inode()];
|
||||||
|
while let Some(inode) = descendant_stack.pop() {
|
||||||
|
if let Some(entry) = self.entries.get(&inode) {
|
||||||
|
edits.push(Edit::Remove(inode));
|
||||||
|
if let Entry::Dir { children, .. } = entry {
|
||||||
|
descendant_stack.extend_from_slice(children.as_ref());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbg!(&edits);
|
||||||
|
self.entries.edit(edits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn move_entry(
|
fn move_entry(
|
||||||
&mut self,
|
&mut self,
|
||||||
child_inode: u64,
|
child_inode: u64,
|
||||||
|
@ -638,7 +673,7 @@ impl BackgroundScanner {
|
||||||
let (tx, rx) = crossbeam_channel::unbounded();
|
let (tx, rx) = crossbeam_channel::unbounded();
|
||||||
|
|
||||||
tx.send(Ok(ScanJob {
|
tx.send(Ok(ScanJob {
|
||||||
ino: inode,
|
inode,
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
relative_path,
|
relative_path,
|
||||||
dir_entry,
|
dir_entry,
|
||||||
|
@ -655,7 +690,7 @@ impl BackgroundScanner {
|
||||||
pool.execute(|| {
|
pool.execute(|| {
|
||||||
let result = result;
|
let result = result;
|
||||||
while let Ok(job) = rx.recv() {
|
while let Ok(job) = rx.recv() {
|
||||||
if let Err(err) = job.and_then(|job| self.scan_dir(job, None)) {
|
if let Err(err) = job.and_then(|job| self.scan_dir(job)) {
|
||||||
*result = Err(err);
|
*result = Err(err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -679,7 +714,7 @@ impl BackgroundScanner {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_dir(&self, job: ScanJob, mut children: Option<&mut Vec<u64>>) -> io::Result<()> {
|
fn scan_dir(&self, job: ScanJob) -> io::Result<()> {
|
||||||
let scan_queue = job.scan_queue;
|
let scan_queue = job.scan_queue;
|
||||||
let mut dir_entry = job.dir_entry;
|
let mut dir_entry = job.dir_entry;
|
||||||
|
|
||||||
|
@ -697,9 +732,6 @@ impl BackgroundScanner {
|
||||||
let path = job.path.join(name.as_ref());
|
let path = job.path.join(name.as_ref());
|
||||||
|
|
||||||
new_children.push(ino);
|
new_children.push(ino);
|
||||||
if let Some(children) = children.as_mut() {
|
|
||||||
children.push(ino);
|
|
||||||
}
|
|
||||||
if metadata.is_dir() {
|
if metadata.is_dir() {
|
||||||
let mut is_ignored = true;
|
let mut is_ignored = true;
|
||||||
let mut ignore = None;
|
let mut ignore = None;
|
||||||
|
@ -714,7 +746,7 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
let dir_entry = Entry::Dir {
|
let dir_entry = Entry::Dir {
|
||||||
parent: Some(job.ino),
|
parent: Some(job.inode),
|
||||||
name,
|
name,
|
||||||
inode: ino,
|
inode: ino,
|
||||||
is_symlink,
|
is_symlink,
|
||||||
|
@ -724,7 +756,7 @@ impl BackgroundScanner {
|
||||||
};
|
};
|
||||||
new_entries.push(dir_entry.clone());
|
new_entries.push(dir_entry.clone());
|
||||||
new_jobs.push(ScanJob {
|
new_jobs.push(ScanJob {
|
||||||
ino,
|
inode: ino,
|
||||||
path: Arc::from(path),
|
path: Arc::from(path),
|
||||||
relative_path,
|
relative_path,
|
||||||
dir_entry,
|
dir_entry,
|
||||||
|
@ -737,7 +769,7 @@ impl BackgroundScanner {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(true, |i| i.matched(&path, false).is_ignore());
|
.map_or(true, |i| i.matched(&path, false).is_ignore());
|
||||||
new_entries.push(Entry::File {
|
new_entries.push(Entry::File {
|
||||||
parent: Some(job.ino),
|
parent: Some(job.inode),
|
||||||
name,
|
name,
|
||||||
path: PathEntry::new(ino, &relative_path, is_ignored),
|
path: PathEntry::new(ino, &relative_path, is_ignored),
|
||||||
inode: ino,
|
inode: ino,
|
||||||
|
@ -771,9 +803,7 @@ impl BackgroundScanner {
|
||||||
|
|
||||||
events.sort_unstable_by(|a, b| a.path.cmp(&b.path));
|
events.sort_unstable_by(|a, b| a.path.cmp(&b.path));
|
||||||
let mut paths = events.into_iter().map(|e| e.path).peekable();
|
let mut paths = events.into_iter().map(|e| e.path).peekable();
|
||||||
let mut possible_removed_inodes = HashSet::new();
|
|
||||||
let (scan_queue_tx, scan_queue_rx) = crossbeam_channel::unbounded();
|
let (scan_queue_tx, scan_queue_rx) = crossbeam_channel::unbounded();
|
||||||
|
|
||||||
while let Some(path) = paths.next() {
|
while let Some(path) = paths.next() {
|
||||||
let relative_path = match path.strip_prefix(&snapshot.path) {
|
let relative_path = match path.strip_prefix(&snapshot.path) {
|
||||||
Ok(relative_path) => relative_path.to_path_buf(),
|
Ok(relative_path) => relative_path.to_path_buf(),
|
||||||
|
@ -782,145 +812,81 @@ impl BackgroundScanner {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// dbg!(&path, &relative_path, snapshot.entries.items());
|
||||||
|
|
||||||
let snapshot_entry = snapshot.entry_for_path(&relative_path);
|
while paths.peek().map_or(false, |p| p.starts_with(&path)) {
|
||||||
let fs_entry = self.fs_entry_for_path(&snapshot.path, &path);
|
paths.next();
|
||||||
|
}
|
||||||
|
|
||||||
match fs_entry {
|
if let Some(snapshot_inode) = snapshot.inode_for_path(&relative_path) {
|
||||||
// If this path currently exists on the filesystem, then ensure that the snapshot's
|
snapshot.remove_entry(snapshot_inode);
|
||||||
// entry for this path is up-to-date.
|
}
|
||||||
|
|
||||||
|
match self.fs_entry_for_path(&snapshot.path, &path) {
|
||||||
Ok(Some((fs_entry, ignore))) => {
|
Ok(Some((fs_entry, ignore))) => {
|
||||||
let fs_inode = fs_entry.inode();
|
snapshot.remove_entry(fs_entry.inode());
|
||||||
let fs_parent_inode = fs_entry.parent();
|
|
||||||
|
|
||||||
// If the snapshot already contains an entry for this path, then ensure that the
|
let mut edits = Vec::new();
|
||||||
// entry has the correct inode and parent.
|
edits.push(Edit::Insert(fs_entry.clone()));
|
||||||
if let Some(snapshot_entry) = snapshot_entry {
|
if let Some(parent) = fs_entry.parent() {
|
||||||
let snapshot_inode = snapshot_entry.inode();
|
let mut parent_entry = snapshot.entries.get(&parent).unwrap().clone();
|
||||||
let snapshot_parent_inode = snapshot_entry.parent();
|
if let Entry::Dir { children, .. } = &mut parent_entry {
|
||||||
|
if !children.contains(&fs_entry.inode()) {
|
||||||
// If the snapshot entry already matches the filesystem, then skip to the
|
*children = children
|
||||||
// next event path.
|
.into_iter()
|
||||||
if snapshot_inode == fs_inode && snapshot_parent_inode == fs_parent_inode {
|
.copied()
|
||||||
continue;
|
.chain(Some(fs_entry.inode()))
|
||||||
}
|
.collect::<Vec<_>>()
|
||||||
|
.into();
|
||||||
// If it does not match, then detach this inode from its current parent, and
|
edits.push(Edit::Insert(parent_entry));
|
||||||
// record that it may have been removed from the worktree.
|
|
||||||
snapshot.move_entry(snapshot_inode, None, snapshot_parent_inode, None);
|
|
||||||
possible_removed_inodes.insert(snapshot_inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the snapshot already contained an entry for the inode that is now located
|
|
||||||
// at this path in the filesystem, then move it to reflect its current parent on
|
|
||||||
// the filesystem.
|
|
||||||
if let Some(snapshot_entry_for_inode) = snapshot.entries.get(&fs_inode) {
|
|
||||||
let snapshot_parent_inode = snapshot_entry_for_inode.parent();
|
|
||||||
snapshot.move_entry(
|
|
||||||
fs_inode,
|
|
||||||
Some(&path),
|
|
||||||
snapshot_parent_inode,
|
|
||||||
fs_parent_inode,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// If the snapshot has no entry for this inode, then scan the filesystem to find
|
|
||||||
// all descendents of this new inode. Discard any subsequent events that are
|
|
||||||
// contained by the current path, since the directory is already being scanned
|
|
||||||
// from scratch.
|
|
||||||
else {
|
|
||||||
while let Some(next_path) = paths.peek() {
|
|
||||||
if next_path.starts_with(&path) {
|
|
||||||
paths.next();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
unreachable!();
|
||||||
snapshot.entries.insert(fs_entry.clone());
|
|
||||||
snapshot.move_entry(fs_inode, None, None, fs_parent_inode);
|
|
||||||
|
|
||||||
if fs_entry.is_dir() {
|
|
||||||
let relative_path = snapshot
|
|
||||||
.path
|
|
||||||
.parent()
|
|
||||||
.map_or(path.as_path(), |parent| path.strip_prefix(parent).unwrap())
|
|
||||||
.to_path_buf();
|
|
||||||
scan_queue_tx
|
|
||||||
.send(Ok(ScanJob {
|
|
||||||
ino: fs_inode,
|
|
||||||
path: Arc::from(path),
|
|
||||||
relative_path,
|
|
||||||
dir_entry: fs_entry,
|
|
||||||
ignore: Some(ignore),
|
|
||||||
scan_queue: scan_queue_tx.clone(),
|
|
||||||
}))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
snapshot.entries.edit(edits);
|
||||||
|
|
||||||
// If this path no longer exists on the filesystem, then remove it from the snapshot.
|
if fs_entry.is_dir() {
|
||||||
Ok(None) => {
|
let relative_path = snapshot
|
||||||
if let Some(snapshot_entry) = snapshot_entry {
|
.path
|
||||||
let snapshot_inode = snapshot_entry.inode();
|
.parent()
|
||||||
let snapshot_parent_inode = snapshot_entry.parent();
|
.map_or(path.as_path(), |parent| path.strip_prefix(parent).unwrap())
|
||||||
snapshot.move_entry(snapshot_inode, None, snapshot_parent_inode, None);
|
.to_path_buf();
|
||||||
possible_removed_inodes.insert(snapshot_inode);
|
scan_queue_tx
|
||||||
|
.send(Ok(ScanJob {
|
||||||
|
inode: fs_entry.inode(),
|
||||||
|
path: Arc::from(path),
|
||||||
|
relative_path,
|
||||||
|
dir_entry: fs_entry,
|
||||||
|
ignore: Some(ignore),
|
||||||
|
scan_queue: scan_queue_tx.clone(),
|
||||||
|
}))
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Ok(None) => {}
|
||||||
|
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
|
||||||
log::error!("Error reading file on event {:?}", e);
|
log::error!("Error reading file on event {:?}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now, update the locked snapshot at this point, because `scan_dir` uses that.
|
|
||||||
*self.snapshot.lock() = snapshot;
|
*self.snapshot.lock() = snapshot;
|
||||||
|
|
||||||
// Scan any directories that were moved into this worktree as part of this event batch.
|
// Scan any directories that were moved into this worktree as part of this event batch.
|
||||||
drop(scan_queue_tx);
|
drop(scan_queue_tx);
|
||||||
let mut scanned_inodes = Vec::new();
|
|
||||||
scanned_inodes.resize_with(self.thread_pool.workers(), || Ok(Vec::new()));
|
|
||||||
self.thread_pool.scoped(|pool| {
|
self.thread_pool.scoped(|pool| {
|
||||||
for worker_inodes in &mut scanned_inodes {
|
for _ in 0..self.thread_pool.workers() {
|
||||||
pool.execute(|| {
|
pool.execute(|| {
|
||||||
let worker_inodes = worker_inodes;
|
|
||||||
while let Ok(job) = scan_queue_rx.recv() {
|
while let Ok(job) = scan_queue_rx.recv() {
|
||||||
if let Err(err) = job.and_then(|job| {
|
if let Err(err) = job.and_then(|job| self.scan_dir(job)) {
|
||||||
self.scan_dir(job, Some(worker_inodes.as_mut().unwrap()))
|
log::error!("Error scanning {:?}", err);
|
||||||
}) {
|
|
||||||
*worker_inodes = Err(err);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove any entries that became orphaned when processing this events batch.
|
|
||||||
let mut snapshot = self.snapshot();
|
|
||||||
let mut deletions = Vec::new();
|
|
||||||
let mut descendent_stack = Vec::new();
|
|
||||||
for inode in possible_removed_inodes {
|
|
||||||
if let Some(entry) = snapshot.entries.get(&inode) {
|
|
||||||
if entry.parent().is_none() {
|
|
||||||
descendent_stack.push(inode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively remove the orphaned nodes' descendants.
|
|
||||||
while let Some(inode) = descendent_stack.pop() {
|
|
||||||
if let Some(entry) = snapshot.entries.get(&inode) {
|
|
||||||
deletions.push(Edit::Remove(inode));
|
|
||||||
if let Entry::Dir { children, .. } = entry {
|
|
||||||
descendent_stack.extend_from_slice(children.as_ref());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
snapshot.entries.edit(deletions);
|
|
||||||
*self.snapshot.lock() = snapshot;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fs_entry_for_path(&self, root_path: &Path, path: &Path) -> Result<Option<(Entry, Ignore)>> {
|
fn fs_entry_for_path(&self, root_path: &Path, path: &Path) -> Result<Option<(Entry, Ignore)>> {
|
||||||
|
@ -991,8 +957,14 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for BackgroundScanner {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.thread_pool.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ScanJob {
|
struct ScanJob {
|
||||||
ino: u64,
|
inode: u64,
|
||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
relative_path: PathBuf,
|
relative_path: PathBuf,
|
||||||
dir_entry: Entry,
|
dir_entry: Entry,
|
||||||
|
@ -1260,6 +1232,7 @@ mod tests {
|
||||||
notify_tx,
|
notify_tx,
|
||||||
);
|
);
|
||||||
new_scanner.scan_dirs().unwrap();
|
new_scanner.scan_dirs().unwrap();
|
||||||
|
dbg!(scanner.snapshot().entries.items());
|
||||||
assert_eq!(scanner.snapshot().to_vec(), new_scanner.snapshot().to_vec());
|
assert_eq!(scanner.snapshot().to_vec(), new_scanner.snapshot().to_vec());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue