Implement worktree::Snapshot::diff

Nathan Sobo and Max Brunsfeld created

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>

Change summary

zed/src/worktree.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+), 1 deletion(-)

Detailed changes

zed/src/worktree.rs 🔗

@@ -22,7 +22,7 @@ use postage::{
 use smol::{channel::Sender, lock::Mutex as AsyncMutex};
 use std::{
     cmp,
-    collections::HashMap,
+    collections::{HashMap, HashSet},
     ffi::{OsStr, OsString},
     fmt, fs,
     future::Future,
@@ -621,6 +621,57 @@ impl Snapshot {
 
         ignore_stack
     }
+
+    fn diff(&self, old: &Self) -> Diff {
+        let mut new = self.entries.cursor::<(), ()>().peekable();
+        let mut old = old.entries.cursor::<(), ()>().peekable();
+
+        let mut diff = Diff::default();
+        let mut removed_inodes = HashMap::new();
+        let mut added_inodes = HashMap::new();
+
+        loop {
+            match (new.peek(), old.peek()) {
+                (Some(new_entry), Some(old_entry)) => match new_entry.path.cmp(&old_entry.path) {
+                    cmp::Ordering::Equal => {
+                        new.next();
+                        old.next();
+                    }
+                    cmp::Ordering::Less => {
+                        added_inodes.insert(new_entry.inode, new_entry.path.clone());
+                        diff.added.insert(new_entry.path.clone());
+                        new.next();
+                    }
+                    cmp::Ordering::Greater => {
+                        removed_inodes.insert(old_entry.path.clone(), old_entry.inode);
+                        diff.removed.insert(old_entry.path.clone());
+                        old.next();
+                    }
+                },
+                (Some(new_entry), None) => {
+                    added_inodes.insert(new_entry.inode, new_entry.path.clone());
+                    diff.added.insert(new_entry.path.clone());
+                    new.next();
+                }
+                (None, Some(old_entry)) => {
+                    removed_inodes.insert(old_entry.path.clone(), old_entry.inode);
+                    diff.removed.insert(old_entry.path.clone());
+                    old.next();
+                }
+                (None, None) => break,
+            }
+        }
+
+        for (removed_path, inode) in removed_inodes {
+            if let Some(added_path) = added_inodes.remove(&inode) {
+                diff.removed.remove(&removed_path);
+                diff.added.remove(&added_path);
+                diff.moved.insert(removed_path, added_path);
+            }
+        }
+
+        diff
+    }
 }
 
 impl fmt::Debug for Snapshot {
@@ -635,6 +686,14 @@ impl fmt::Debug for Snapshot {
     }
 }
 
+#[derive(Default)]
+struct Diff {
+    moved: HashMap<Arc<Path>, Arc<Path>>,
+    removed: HashSet<Arc<Path>>,
+    added: HashSet<Arc<Path>>,
+    modified: HashSet<Arc<Path>>,
+}
+
 impl FileHandle {
     pub fn id(&self) -> u64 {
         self.state.lock().id