Implement `Snapshot::interpolate`

Antonio Scandurra created

Change summary

zed/src/editor/display_map/tab_map.rs  |   6 +
zed/src/editor/display_map/wrap_map.rs | 118 ++++++++++++++++++++-------
2 files changed, 93 insertions(+), 31 deletions(-)

Detailed changes

zed/src/editor/display_map/tab_map.rs 🔗

@@ -261,6 +261,12 @@ impl OutputPoint {
     }
 }
 
+impl From<super::Point> for OutputPoint {
+    fn from(point: super::Point) -> Self {
+        Self(point)
+    }
+}
+
 impl AddAssign<Self> for OutputPoint {
     fn add_assign(&mut self, rhs: Self) {
         self.0 += &rhs.0;

zed/src/editor/display_map/wrap_map.rs 🔗

@@ -62,7 +62,6 @@ impl Sub<Self> for OutputPoint {
 pub struct Snapshot {
     transforms: SumTree<Transform>,
     input: InputSnapshot,
-    version: usize,
 }
 
 impl Snapshot {
@@ -78,11 +77,67 @@ impl Snapshot {
                 },
                 &(),
             ),
-            version: input.version(),
             input,
         }
     }
 
+    fn interpolate(&mut self, new_snapshot: InputSnapshot, edits: &[InputEdit]) {
+        if edits.is_empty() {
+            return;
+        }
+
+        let mut new_transforms;
+        {
+            let mut old_cursor = self.transforms.cursor::<InputPoint, ()>();
+            let mut edits = edits.into_iter().peekable();
+            new_transforms =
+                old_cursor.slice(&edits.peek().unwrap().old_lines.start, Bias::Right, &());
+
+            while let Some(edit) = edits.next() {
+                if edit.new_lines.start > InputPoint::from(new_transforms.summary().input.lines) {
+                    let summary = new_snapshot.text_summary_for_range(
+                        InputPoint::from(new_transforms.summary().input.lines)
+                            ..edit.new_lines.start,
+                    );
+                    new_transforms.push_or_extend(Transform::isomorphic(summary));
+                }
+
+                new_transforms.push_or_extend(Transform::isomorphic(
+                    new_snapshot.text_summary_for_range(edit.new_lines.clone()),
+                ));
+
+                old_cursor.seek_forward(&edit.old_lines.end, Bias::Right, &());
+                if let Some(next_edit) = edits.peek() {
+                    if next_edit.old_lines.start > old_cursor.seek_end(&()) {
+                        if old_cursor.seek_end(&()) > edit.old_lines.end {
+                            let summary = self.input.text_summary_for_range(
+                                edit.old_lines.end..old_cursor.seek_end(&()),
+                            );
+                            new_transforms.push_or_extend(Transform::isomorphic(summary));
+                        }
+                        old_cursor.next(&());
+                        new_transforms.push_tree(
+                            old_cursor.slice(&next_edit.old_lines.start, Bias::Right, &()),
+                            &(),
+                        );
+                    }
+                } else {
+                    if old_cursor.seek_end(&()) > edit.old_lines.end {
+                        let summary = self
+                            .input
+                            .text_summary_for_range(edit.old_lines.end..old_cursor.seek_end(&()));
+                        new_transforms.push_or_extend(Transform::isomorphic(summary));
+                    }
+                    old_cursor.next(&());
+                    new_transforms.push_tree(old_cursor.suffix(&()), &());
+                }
+            }
+        }
+
+        self.transforms = new_transforms;
+        self.input = new_snapshot;
+    }
+
     pub fn chunks_at(&self, point: OutputPoint) -> Chunks {
         let mut transforms = self.transforms.cursor();
         transforms.seek(&point, Bias::Right, &());
@@ -145,7 +200,6 @@ impl<'a> Iterator for Chunks<'a> {
 
 struct State {
     snapshot: Snapshot,
-    interpolated_version: usize,
 }
 
 #[derive(Clone)]
@@ -179,10 +233,7 @@ impl WrapMap {
         };
 
         Self {
-            state: Mutex::new(State {
-                interpolated_version: snapshot.version,
-                snapshot,
-            }),
+            state: Mutex::new(State { snapshot }),
             edits_tx,
             background_snapshots: background_snapshots_rx,
             _background_task: background_task,
@@ -190,7 +241,10 @@ impl WrapMap {
     }
 
     pub fn sync(&self, input: InputSnapshot, edits: Vec<InputEdit>) -> Snapshot {
-        // TODO: interpolate
+        self.state
+            .lock()
+            .snapshot
+            .interpolate(input.clone(), &edits);
         self.edits_tx.try_send((input, edits)).unwrap();
         self.state.lock().snapshot.clone()
     }
@@ -378,30 +432,8 @@ impl BackgroundWrapper {
 
         self.snapshot = Snapshot {
             transforms: new_transforms,
-            version: new_snapshot.version(),
             input: new_snapshot,
         };
-        self.check_invariants();
-    }
-
-    fn check_invariants(&self) {
-        #[cfg(debug_assertions)]
-        {
-            let summary = self.snapshot.transforms.summary();
-            assert_eq!(
-                InputPoint::new(summary.input.lines.row, summary.input.lines.column),
-                self.snapshot.input.max_point()
-            );
-
-            let mut transforms = self.snapshot.transforms.cursor::<(), ()>().peekable();
-            while let Some(transform) = transforms.next() {
-                let next_transform = transforms.peek();
-                assert!(
-                    !transform.is_isomorphic()
-                        || next_transform.map_or(true, |t| !t.is_isomorphic())
-                );
-            }
-        }
     }
 }
 
@@ -613,10 +645,14 @@ mod tests {
                 unwrapped_text
             );
 
+            let mut interpolated_snapshot = wrapper.snapshot.clone();
             for _i in 0..operations {
                 buffer.update(cx, |buffer, cx| buffer.randomly_mutate(&mut rng, cx));
                 let (snapshot, edits) = fold_map.read(cx.as_ref());
                 let (snapshot, edits) = tab_map.sync(snapshot, edits);
+                interpolated_snapshot.interpolate(snapshot.clone(), &edits);
+                interpolated_snapshot.check_invariants();
+
                 let unwrapped_text = snapshot.text();
                 let expected_text = wrap_text(
                     &unwrapped_text,
@@ -625,7 +661,7 @@ mod tests {
                     font_system.as_ref(),
                 );
                 wrapper.sync(snapshot, edits);
-
+                wrapper.snapshot.check_invariants();
                 let actual_text = wrapper
                     .snapshot
                     .chunks_at(OutputPoint::zero())
@@ -635,6 +671,8 @@ mod tests {
                     "unwrapped text is: {:?}",
                     unwrapped_text
                 );
+
+                interpolated_snapshot = wrapper.snapshot.clone();
             }
         }
     }
@@ -661,4 +699,22 @@ mod tests {
         }
         wrapped_text
     }
+
+    impl Snapshot {
+        fn check_invariants(&self) {
+            assert_eq!(
+                InputPoint::from(self.transforms.summary().input.lines),
+                self.input.max_point()
+            );
+
+            let mut transforms = self.transforms.cursor::<(), ()>().peekable();
+            while let Some(transform) = transforms.next() {
+                let next_transform = transforms.peek();
+                assert!(
+                    !transform.is_isomorphic()
+                        || next_transform.map_or(true, |t| !t.is_isomorphic())
+                );
+            }
+        }
+    }
 }