Make writing/reading `FoldMap` explicit so that we can retrieve edits

Antonio Scandurra created

Change summary

zed/src/editor/buffer.rs               |  13 +
zed/src/editor/display_map.rs          |  26 +-
zed/src/editor/display_map/fold_map.rs | 247 +++++++++++++++------------
zed/src/editor/display_map/wrap_map.rs | 112 +++++------
4 files changed, 219 insertions(+), 179 deletions(-)

Detailed changes

zed/src/editor/buffer.rs 🔗

@@ -1930,6 +1930,19 @@ pub struct Snapshot {
     query_cursor: QueryCursorHandle,
 }
 
+impl Clone for Snapshot {
+    fn clone(&self) -> Self {
+        Self {
+            visible_text: self.visible_text.clone(),
+            fragments: self.fragments.clone(),
+            version: self.version.clone(),
+            tree: self.tree.clone(),
+            language: self.language.clone(),
+            query_cursor: QueryCursorHandle::new(),
+        }
+    }
+}
+
 impl Snapshot {
     pub fn len(&self) -> usize {
         self.visible_text.len()

zed/src/editor/display_map.rs 🔗

@@ -1,31 +1,37 @@
 mod fold_map;
 mod wrap_map;
 
-use crate::settings::StyleId;
-
 use super::{buffer, Anchor, Bias, Buffer, Point, ToOffset, ToPoint};
+use crate::settings::StyleId;
 pub use fold_map::BufferRows;
-use fold_map::{FoldMap, FoldMapSnapshot};
+use fold_map::FoldMap;
 use gpui::{AppContext, ModelHandle};
 use std::{mem, ops::Range};
+use wrap_map::WrapMap;
 
 pub struct DisplayMap {
     buffer: ModelHandle<Buffer>,
     fold_map: FoldMap,
+    wrap_map: WrapMap,
     tab_size: usize,
 }
 
 impl DisplayMap {
     pub fn new(buffer: ModelHandle<Buffer>, tab_size: usize, cx: &AppContext) -> Self {
+        let fold_map = FoldMap::new(buffer.clone(), cx);
+        let (snapshot, edits) = fold_map.read(cx);
+        assert_eq!(edits.len(), 0);
+        let wrap_map = WrapMap::new(snapshot, cx);
         DisplayMap {
-            buffer: buffer.clone(),
-            fold_map: FoldMap::new(buffer, cx),
+            buffer,
+            fold_map,
+            wrap_map,
             tab_size,
         }
     }
 
     pub fn snapshot(&self, cx: &AppContext) -> DisplayMapSnapshot {
-        let (folds_snapshot, edits) = self.fold_map.snapshot(cx);
+        let (folds_snapshot, edits) = self.fold_map.read(cx);
         DisplayMapSnapshot {
             buffer_snapshot: self.buffer.read(cx).snapshot(),
             folds_snapshot,
@@ -38,7 +44,8 @@ impl DisplayMap {
         ranges: impl IntoIterator<Item = Range<T>>,
         cx: &AppContext,
     ) {
-        self.fold_map.fold(ranges, cx)
+        let (mut fold_map, snapshot, edits) = self.fold_map.write(cx);
+        let edits = fold_map.fold(ranges, cx);
     }
 
     pub fn unfold<T: ToOffset>(
@@ -46,13 +53,14 @@ impl DisplayMap {
         ranges: impl IntoIterator<Item = Range<T>>,
         cx: &AppContext,
     ) {
-        self.fold_map.unfold(ranges, cx)
+        let (mut fold_map, snapshot, edits) = self.fold_map.write(cx);
+        let edits = fold_map.unfold(ranges, cx);
     }
 }
 
 pub struct DisplayMapSnapshot {
     buffer_snapshot: buffer::Snapshot,
-    folds_snapshot: FoldMapSnapshot,
+    folds_snapshot: fold_map::Snapshot,
     tab_size: usize,
 }
 

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

@@ -15,61 +15,26 @@ use std::{
     cmp::{self, Ordering},
     iter,
     ops::Range,
+    sync::atomic::{AtomicUsize, Ordering::SeqCst},
 };
 
-pub struct FoldMap {
-    buffer: ModelHandle<Buffer>,
-    transforms: Mutex<SumTree<Transform>>,
-    folds: SumTree<Fold>,
-    last_sync: Mutex<time::Global>,
-}
-
-impl FoldMap {
-    pub fn new(buffer_handle: ModelHandle<Buffer>, cx: &AppContext) -> Self {
-        let buffer = buffer_handle.read(cx);
-        Self {
-            buffer: buffer_handle,
-            folds: Default::default(),
-            transforms: Mutex::new(SumTree::from_item(
-                Transform {
-                    summary: TransformSummary {
-                        buffer: buffer.text_summary(),
-                        display: buffer.text_summary(),
-                    },
-                    display_text: None,
-                },
-                &(),
-            )),
-            last_sync: Mutex::new(buffer.version()),
-        }
-    }
-
-    pub fn snapshot(&self, cx: &AppContext) -> (FoldMapSnapshot, Vec<Edit>) {
-        let edits = self.sync(cx);
-        let snapshot = FoldMapSnapshot {
-            transforms: self.transforms.lock().clone(),
-            folds: self.folds.clone(),
-            buffer: self.buffer.read(cx).snapshot(),
-        };
-        (snapshot, edits)
-    }
+pub struct FoldMapWriter<'a>(&'a mut FoldMap);
 
+impl<'a> FoldMapWriter<'a> {
     pub fn fold<T: ToOffset>(
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
         cx: &AppContext,
-    ) {
-        let edits = self.sync(cx);
-
-        let mut fold_edits = Vec::new();
+    ) -> Vec<Edit> {
+        let mut edits = Vec::new();
         let mut folds = Vec::new();
-        let buffer = self.buffer.read(cx).snapshot();
+        let buffer = self.0.buffer.read(cx).snapshot();
         for range in ranges.into_iter() {
             let range = range.start.to_offset(&buffer)..range.end.to_offset(&buffer);
             if range.start != range.end {
                 let fold = Fold(buffer.anchor_after(range.start)..buffer.anchor_before(range.end));
                 folds.push(fold);
-                fold_edits.push(Edit {
+                edits.push(Edit {
                     old_bytes: range.clone(),
                     new_bytes: range.clone(),
                 });
@@ -78,9 +43,9 @@ impl FoldMap {
 
         folds.sort_unstable_by(|a, b| sum_tree::SeekDimension::cmp(a, b, &buffer));
 
-        self.folds = {
+        self.0.folds = {
             let mut new_tree = SumTree::new();
-            let mut cursor = self.folds.cursor::<_, ()>();
+            let mut cursor = self.0.folds.cursor::<_, ()>();
             for fold in folds {
                 new_tree.push_tree(cursor.slice(&fold, Bias::Right, &buffer), &buffer);
                 new_tree.push(fold, &buffer);
@@ -89,26 +54,24 @@ impl FoldMap {
             new_tree
         };
 
-        self.consolidate_edits(&mut fold_edits);
-        self.apply_edits(fold_edits, cx);
+        self.consolidate_edits(&mut edits);
+        self.0.apply_edits(edits, cx)
     }
 
     pub fn unfold<T: ToOffset>(
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
         cx: &AppContext,
-    ) {
-        let edits = self.sync(cx);
-
-        let mut fold_edits = Vec::new();
+    ) -> Vec<Edit> {
+        let mut edits = Vec::new();
         let mut fold_ixs_to_delete = Vec::new();
-        let buffer = self.buffer.read(cx).snapshot();
+        let buffer = self.0.buffer.read(cx).snapshot();
         for range in ranges.into_iter() {
             // Remove intersecting folds and add their ranges to edits that are passed to apply_edits.
-            let mut folds_cursor = intersecting_folds(&buffer, &self.folds, range);
+            let mut folds_cursor = intersecting_folds(&buffer, &self.0.folds, range);
             while let Some(fold) = folds_cursor.item() {
                 let offset_range = fold.0.start.to_offset(&buffer)..fold.0.end.to_offset(&buffer);
-                fold_edits.push(Edit {
+                edits.push(Edit {
                     old_bytes: offset_range.clone(),
                     new_bytes: offset_range,
                 });
@@ -120,8 +83,8 @@ impl FoldMap {
         fold_ixs_to_delete.sort_unstable();
         fold_ixs_to_delete.dedup();
 
-        self.folds = {
-            let mut cursor = self.folds.cursor::<_, ()>();
+        self.0.folds = {
+            let mut cursor = self.0.folds.cursor::<_, ()>();
             let mut folds = SumTree::new();
             for fold_ix in fold_ixs_to_delete {
                 folds.push_tree(cursor.slice(&fold_ix, Bias::Right, &buffer), &buffer);
@@ -131,11 +94,79 @@ impl FoldMap {
             folds
         };
 
-        self.consolidate_edits(&mut fold_edits);
-        self.apply_edits(fold_edits, cx);
+        self.consolidate_edits(&mut edits);
+        self.0.apply_edits(edits, cx)
+    }
+
+    fn consolidate_edits(&self, edits: &mut Vec<Edit>) {
+        edits.sort_unstable_by(|a, b| {
+            a.old_bytes
+                .start
+                .cmp(&b.old_bytes.start)
+                .then_with(|| b.old_bytes.end.cmp(&a.old_bytes.end))
+        });
+
+        let mut i = 0;
+        while i < edits.len() {
+            let range = edits[i].old_bytes.clone();
+            if i > 0 {
+                if edits[i - 1].old_bytes.end >= range.start {
+                    edits[i - 1].old_bytes.end = edits[i - 1].old_bytes.end.max(range.end);
+                    edits[i - 1].new_bytes.end = edits[i - 1].new_bytes.end.max(range.end);
+                    edits.remove(i);
+                    continue;
+                }
+            }
+            i += 1;
+        }
+    }
+}
+
+pub struct FoldMap {
+    buffer: ModelHandle<Buffer>,
+    transforms: Mutex<SumTree<Transform>>,
+    folds: SumTree<Fold>,
+    last_sync: Mutex<time::Global>,
+    version: AtomicUsize,
+}
+
+impl FoldMap {
+    pub fn new(buffer_handle: ModelHandle<Buffer>, cx: &AppContext) -> Self {
+        let buffer = buffer_handle.read(cx);
+        Self {
+            buffer: buffer_handle,
+            folds: Default::default(),
+            transforms: Mutex::new(SumTree::from_item(
+                Transform {
+                    summary: TransformSummary {
+                        buffer: buffer.text_summary(),
+                        display: buffer.text_summary(),
+                    },
+                    display_text: None,
+                },
+                &(),
+            )),
+            last_sync: Mutex::new(buffer.version()),
+            version: AtomicUsize::new(0),
+        }
+    }
+
+    pub fn read(&self, cx: &AppContext) -> (Snapshot, Vec<Edit>) {
+        let edits = self.sync(cx);
+        let snapshot = Snapshot {
+            transforms: self.transforms.lock().clone(),
+            folds: self.folds.clone(),
+            buffer: self.buffer.read(cx).snapshot(),
+            version: self.version.load(SeqCst),
+        };
+        (snapshot, edits)
+    }
+
+    pub fn write(&mut self, cx: &AppContext) -> (FoldMapWriter, Snapshot, Vec<Edit>) {
+        let (snapshot, edits) = self.read(cx);
+        (FoldMapWriter(self), snapshot, edits)
     }
 
-    #[must_use]
     fn sync(&self, cx: &AppContext) -> Vec<Edit> {
         let buffer = self.buffer.read(cx);
         let edits = buffer
@@ -325,45 +356,29 @@ impl FoldMap {
         }
 
         *transforms = new_transforms;
+        self.version.fetch_add(1, SeqCst);
         edits
     }
-
-    fn consolidate_edits(&self, edits: &mut Vec<Edit>) {
-        edits.sort_unstable_by(|a, b| {
-            a.old_bytes
-                .start
-                .cmp(&b.old_bytes.start)
-                .then_with(|| b.old_bytes.end.cmp(&a.old_bytes.end))
-        });
-
-        let mut i = 0;
-        while i < edits.len() {
-            let range = edits[i].old_bytes.clone();
-            if i > 0 {
-                if edits[i - 1].old_bytes.end >= range.start {
-                    edits[i - 1].old_bytes.end = edits[i - 1].old_bytes.end.max(range.end);
-                    edits[i - 1].new_bytes.end = edits[i - 1].new_bytes.end.max(range.end);
-                    edits.remove(i);
-                    continue;
-                }
-            }
-            i += 1;
-        }
-    }
 }
 
-pub struct FoldMapSnapshot {
+#[derive(Clone)]
+pub struct Snapshot {
     transforms: SumTree<Transform>,
     folds: SumTree<Fold>,
     buffer: buffer::Snapshot,
+    pub version: usize,
 }
 
-impl FoldMapSnapshot {
+impl Snapshot {
     #[cfg(test)]
     pub fn text(&self) -> String {
         self.chunks_at(DisplayOffset(0)).collect()
     }
 
+    pub fn text_summary(&self) -> TextSummary {
+        self.transforms.summary().display
+    }
+
     pub fn len(&self) -> usize {
         self.transforms.summary().display.bytes
     }
@@ -928,14 +943,15 @@ mod tests {
         let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6), cx));
         let mut map = FoldMap::new(buffer.clone(), cx.as_ref());
 
-        map.fold(
+        let (mut writer, _, _) = map.write(cx.as_ref());
+        writer.fold(
             vec![
                 Point::new(0, 2)..Point::new(2, 2),
                 Point::new(2, 4)..Point::new(4, 1),
             ],
             cx.as_ref(),
         );
-        let (snapshot2, edits) = map.snapshot(cx.as_ref());
+        let (snapshot2, edits) = map.read(cx.as_ref());
         assert_eq!(snapshot2.text(), "aa…cc…eeeee");
         assert_eq!(
             edits,
@@ -961,7 +977,7 @@ mod tests {
                 cx,
             );
         });
-        let (snapshot3, edits) = map.snapshot(cx.as_ref());
+        let (snapshot3, edits) = map.read(cx.as_ref());
         assert_eq!(snapshot3.text(), "123a…c123c…eeeee");
         assert_eq!(
             edits,
@@ -980,11 +996,12 @@ mod tests {
         buffer.update(cx, |buffer, cx| {
             buffer.edit(vec![Point::new(2, 6)..Point::new(4, 3)], "456", cx)
         });
-        let (snapshot4, _) = map.snapshot(cx.as_ref());
+        let (snapshot4, _) = map.read(cx.as_ref());
         assert_eq!(snapshot4.text(), "123a…c123456eee");
 
-        map.unfold(Some(Point::new(0, 4)..Point::new(0, 5)), cx.as_ref());
-        let (snapshot5, _) = map.snapshot(cx.as_ref());
+        let (mut writer, _, _) = map.write(cx.as_ref());
+        writer.unfold(Some(Point::new(0, 4)..Point::new(0, 5)), cx.as_ref());
+        let (snapshot5, _) = map.read(cx.as_ref());
         assert_eq!(snapshot5.text(), "123aaaaa\nbbbbbb\nccc123456eee");
     }
 
@@ -995,21 +1012,24 @@ mod tests {
         {
             let mut map = FoldMap::new(buffer.clone(), cx.as_ref());
 
-            map.fold(vec![5..8], cx.as_ref());
+            let (mut writer, _, _) = map.write(cx.as_ref());
+            writer.fold(vec![5..8], cx.as_ref());
             map.check_invariants(cx.as_ref());
-            let (snapshot, _) = map.snapshot(cx.as_ref());
+            let (snapshot, _) = map.read(cx.as_ref());
             assert_eq!(snapshot.text(), "abcde…ijkl");
 
             // Create an fold adjacent to the start of the first fold.
-            map.fold(vec![0..1, 2..5], cx.as_ref());
+            let (mut writer, _, _) = map.write(cx.as_ref());
+            writer.fold(vec![0..1, 2..5], cx.as_ref());
             map.check_invariants(cx.as_ref());
-            let (snapshot, _) = map.snapshot(cx.as_ref());
+            let (snapshot, _) = map.read(cx.as_ref());
             assert_eq!(snapshot.text(), "…b…ijkl");
 
             // Create an fold adjacent to the end of the first fold.
-            map.fold(vec![11..11, 8..10], cx.as_ref());
+            let (mut writer, _, _) = map.write(cx.as_ref());
+            writer.fold(vec![11..11, 8..10], cx.as_ref());
             map.check_invariants(cx.as_ref());
-            let (snapshot, _) = map.snapshot(cx.as_ref());
+            let (snapshot, _) = map.read(cx.as_ref());
             assert_eq!(snapshot.text(), "…b…kl");
         }
 
@@ -1017,9 +1037,10 @@ mod tests {
             let mut map = FoldMap::new(buffer.clone(), cx.as_ref());
 
             // Create two adjacent folds.
-            map.fold(vec![0..2, 2..5], cx.as_ref());
+            let (mut writer, _, _) = map.write(cx.as_ref());
+            writer.fold(vec![0..2, 2..5], cx.as_ref());
             map.check_invariants(cx.as_ref());
-            let (snapshot, _) = map.snapshot(cx.as_ref());
+            let (snapshot, _) = map.read(cx.as_ref());
             assert_eq!(snapshot.text(), "…fghijkl");
 
             // Edit within one of the folds.
@@ -1029,7 +1050,7 @@ mod tests {
                 buffer.edits_since(version).collect::<Vec<_>>()
             });
             map.check_invariants(cx.as_ref());
-            let (snapshot, _) = map.snapshot(cx.as_ref());
+            let (snapshot, _) = map.read(cx.as_ref());
             assert_eq!(snapshot.text(), "12345…fghijkl");
         }
     }
@@ -1038,7 +1059,8 @@ mod tests {
     fn test_overlapping_folds(cx: &mut gpui::MutableAppContext) {
         let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6), cx));
         let mut map = FoldMap::new(buffer.clone(), cx.as_ref());
-        map.fold(
+        let (mut writer, _, _) = map.write(cx.as_ref());
+        writer.fold(
             vec![
                 Point::new(0, 2)..Point::new(2, 2),
                 Point::new(0, 4)..Point::new(1, 0),
@@ -1047,7 +1069,7 @@ mod tests {
             ],
             cx.as_ref(),
         );
-        let (snapshot, _) = map.snapshot(cx.as_ref());
+        let (snapshot, _) = map.read(cx.as_ref());
         assert_eq!(snapshot.text(), "aa…eeeee");
     }
 
@@ -1056,20 +1078,21 @@ mod tests {
         let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6), cx));
         let mut map = FoldMap::new(buffer.clone(), cx.as_ref());
 
-        map.fold(
+        let (mut writer, _, _) = map.write(cx.as_ref());
+        writer.fold(
             vec![
                 Point::new(0, 2)..Point::new(2, 2),
                 Point::new(3, 1)..Point::new(4, 1),
             ],
             cx.as_ref(),
         );
-        let (snapshot, _) = map.snapshot(cx.as_ref());
+        let (snapshot, _) = map.read(cx.as_ref());
         assert_eq!(snapshot.text(), "aa…cccc\nd…eeeee");
 
         buffer.update(cx, |buffer, cx| {
             buffer.edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", cx)
         });
-        let (snapshot, _) = map.snapshot(cx.as_ref());
+        let (snapshot, _) = map.read(cx.as_ref());
         assert_eq!(snapshot.text(), "aa…eeeee");
     }
 
@@ -1079,7 +1102,8 @@ mod tests {
         let mut map = FoldMap::new(buffer.clone(), cx.as_ref());
         let buffer = buffer.read(cx);
 
-        map.fold(
+        let (mut writer, _, _) = map.write(cx.as_ref());
+        writer.fold(
             vec![
                 Point::new(0, 2)..Point::new(2, 2),
                 Point::new(0, 4)..Point::new(1, 0),
@@ -1088,7 +1112,7 @@ mod tests {
             ],
             cx.as_ref(),
         );
-        let (snapshot, _) = map.snapshot(cx.as_ref());
+        let (snapshot, _) = map.read(cx.as_ref());
         let fold_ranges = snapshot
             .folds_in_range(Point::new(1, 0)..Point::new(1, 3))
             .map(|fold| fold.start.to_point(buffer)..fold.end.to_point(buffer))
@@ -1146,7 +1170,8 @@ mod tests {
                             to_fold.push(start..end);
                         }
                         log::info!("folding {:?}", to_fold);
-                        map.fold(to_fold, cx.as_ref());
+                        let (mut writer, _, _) = map.write(cx.as_ref());
+                        writer.fold(to_fold, cx.as_ref());
                     }
                     35..=59 if !map.folds.is_empty() => {
                         let buffer = buffer.read(cx);
@@ -1157,7 +1182,8 @@ mod tests {
                             to_unfold.push(start..end);
                         }
                         log::info!("unfolding {:?}", to_unfold);
-                        map.unfold(to_unfold, cx.as_ref());
+                        let (mut writer, _, _) = map.write(cx.as_ref());
+                        writer.unfold(to_unfold, cx.as_ref());
                     }
                     _ => {
                         let edits = buffer.update(cx, |buffer, cx| {
@@ -1186,7 +1212,7 @@ mod tests {
                 expected_buffer_rows.extend((0..=next_row).rev());
                 expected_buffer_rows.reverse();
 
-                let (snapshot, _) = map.snapshot(cx.as_ref());
+                let (snapshot, _) = map.read(cx.as_ref());
                 assert_eq!(snapshot.text(), expected_text);
 
                 for (display_row, line) in expected_text.lines().enumerate() {
@@ -1306,7 +1332,8 @@ mod tests {
 
         let mut map = FoldMap::new(buffer.clone(), cx.as_ref());
 
-        map.fold(
+        let (mut writer, _, _) = map.write(cx.as_ref());
+        writer.fold(
             vec![
                 Point::new(0, 2)..Point::new(2, 2),
                 Point::new(3, 1)..Point::new(4, 1),
@@ -1314,7 +1341,7 @@ mod tests {
             cx.as_ref(),
         );
 
-        let (snapshot, _) = map.snapshot(cx.as_ref());
+        let (snapshot, _) = map.read(cx.as_ref());
         assert_eq!(snapshot.text(), "aa…cccc\nd…eeeee\nffffff\n");
         assert_eq!(snapshot.buffer_rows(0).collect::<Vec<_>>(), [0, 3, 5, 6]);
         assert_eq!(snapshot.buffer_rows(3).collect::<Vec<_>>(), [6]);

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

@@ -1,97 +1,85 @@
 use crate::{
-    editor::{display_map::FoldMap, Buffer, Point, TextSummary},
+    editor::{display_map::fold_map, Point, TextSummary},
     sum_tree::{self, SumTree},
-    time,
     util::Bias,
 };
-use gpui::{Entity, ModelContext, ModelHandle, Task};
+use gpui::{AppContext, Task};
 use parking_lot::Mutex;
-use postage::{
-    mpsc,
-    prelude::{Sink, Stream},
-    watch,
-};
+use postage::{prelude::Sink, watch};
+use smol::channel;
 
 #[derive(Clone)]
-struct Snapshot {
+pub struct Snapshot {
     transforms: SumTree<Transform>,
-    version: time::Global,
+    version: usize,
 }
 
 struct State {
     snapshot: Snapshot,
-    interpolated_version: time::Global,
+    interpolated_version: usize,
 }
 
-struct WrapMap {
-    buffer: ModelHandle<Buffer>,
-    fold_map: FoldMap,
+pub struct WrapMap {
     state: Mutex<State>,
+    edits_tx: channel::Sender<(fold_map::Snapshot, Vec<fold_map::Edit>)>,
     background_snapshots: watch::Receiver<Snapshot>,
     _background_task: Task<()>,
 }
 
-impl Entity for WrapMap {
-    type Event = ();
-}
-
 impl WrapMap {
-    fn new(buffer_handle: ModelHandle<Buffer>, cx: &mut ModelContext<Self>) -> Self {
-        let buffer = buffer_handle.read(cx).clone();
-        let version = buffer.version();
+    pub fn new(folds_snapshot: fold_map::Snapshot, cx: &AppContext) -> Self {
         let snapshot = Snapshot {
             transforms: SumTree::from_item(
                 Transform {
                     summary: TransformSummary {
-                        buffer: buffer.text_summary(),
-                        display: buffer.text_summary(),
+                        folded: folds_snapshot.text_summary(),
+                        wrapped: folds_snapshot.text_summary(),
                     },
                     display_text: None,
                 },
                 &(),
             ),
-            version: version.clone(),
+            version: folds_snapshot.version,
         };
         let (background_snapshots_tx, background_snapshots_rx) =
             watch::channel_with(snapshot.clone());
-        let (buffers_tx, buffers_rx) = mpsc::channel(32);
-        cx.observe(&buffer_handle, move |_, buffer, cx| {
-            let mut buffers_tx = buffers_tx.clone();
-            // TODO: replace cloning buffers with sending `Buffer::snapshot`.
-            let buffer = buffer.read(cx).clone();
-            cx.spawn_weak(|_, _| async move {
-                let _ = buffers_tx.send(buffer).await;
-            })
-            .detach();
-        });
+        let (edits_tx, edits_rx) = channel::unbounded();
         let background_task = cx.background().spawn(async move {
-            let mut wrapper = BackgroundWrapper::new(buffers_rx, background_snapshots_tx);
-            wrapper.run(buffer).await;
+            let mut wrapper = BackgroundWrapper::new(edits_rx, background_snapshots_tx);
+            wrapper.run(folds_snapshot).await;
         });
 
         Self {
-            buffer: buffer_handle.clone(),
-            fold_map: FoldMap::new(buffer_handle, cx.as_ref()),
             state: Mutex::new(State {
+                interpolated_version: snapshot.version,
                 snapshot,
-                interpolated_version: version,
             }),
+            edits_tx,
             background_snapshots: background_snapshots_rx,
             _background_task: background_task,
         }
     }
+
+    pub fn read(&self, folds_snapshot: fold_map::Snapshot, edits: Vec<fold_map::Edit>) -> Snapshot {
+        // TODO: interpolate
+        self.edits_tx.try_send((folds_snapshot, edits)).unwrap();
+        self.state.lock().snapshot.clone()
+    }
 }
 
 struct BackgroundWrapper {
-    buffers_rx: mpsc::Receiver<Buffer>,
+    edits_rx: channel::Receiver<(fold_map::Snapshot, Vec<fold_map::Edit>)>,
     snapshots_tx: watch::Sender<Snapshot>,
     snapshot: Snapshot,
 }
 
 impl BackgroundWrapper {
-    fn new(buffers_rx: mpsc::Receiver<Buffer>, snapshots_tx: watch::Sender<Snapshot>) -> Self {
+    fn new(
+        edits_rx: channel::Receiver<(fold_map::Snapshot, Vec<fold_map::Edit>)>,
+        snapshots_tx: watch::Sender<Snapshot>,
+    ) -> Self {
         Self {
-            buffers_rx,
+            edits_rx,
             snapshots_tx,
             snapshot: Snapshot {
                 transforms: Default::default(),
@@ -100,32 +88,36 @@ impl BackgroundWrapper {
         }
     }
 
-    async fn run(&mut self, buffer: Buffer) {
-        if !self.sync(buffer).await {
+    async fn run(&mut self, snapshot: fold_map::Snapshot) {
+        let edit = fold_map::Edit {
+            old_bytes: 0..0,
+            new_bytes: 0..snapshot.len(),
+        };
+        if !self.sync(snapshot, vec![edit]).await {
             return;
         }
 
-        while let Some(buffer) = self.buffers_rx.recv().await {
-            if !self.sync(buffer).await {
+        while let Ok((snapshot, edits)) = self.edits_rx.recv().await {
+            if !self.sync(snapshot, edits).await {
                 break;
             }
         }
     }
 
-    async fn sync(&mut self, buffer: Buffer) -> bool {
+    async fn sync(&mut self, snapshot: fold_map::Snapshot, edits: Vec<fold_map::Edit>) -> bool {
         let mut new_transforms = SumTree::new();
         {
-            let mut old_cursor = self.snapshot.transforms.cursor::<Point, ()>();
-            for edit in buffer.edits_since(self.snapshot.version.clone()) {
-                new_transforms.push_tree(
-                    old_cursor.slice(&Point::new(edit.old_lines.start.row, 0), Bias::Left, &()),
-                    &(),
-                );
-            }
+            // let mut old_cursor = self.snapshot.transforms.cursor::<Point, ()>();
+            // for edit in buffer.edits_since(self.snapshot.version.clone()) {
+            //     new_transforms.push_tree(
+            //         old_cursor.slice(&Point::new(edit.old_lines.start.row, 0), Bias::Left, &()),
+            //         &(),
+            //     );
+            // }
         }
 
         self.snapshot.transforms = new_transforms;
-        self.snapshot.version = buffer.version();
+        self.snapshot.version = snapshot.version;
         self.snapshots_tx.send(self.snapshot.clone()).await.is_ok()
     }
 }
@@ -146,21 +138,21 @@ impl sum_tree::Item for Transform {
 
 #[derive(Clone, Debug, Default, Eq, PartialEq)]
 struct TransformSummary {
-    display: TextSummary,
-    buffer: TextSummary,
+    folded: TextSummary,
+    wrapped: TextSummary,
 }
 
 impl sum_tree::Summary for TransformSummary {
     type Context = ();
 
     fn add_summary(&mut self, other: &Self, _: &()) {
-        self.buffer += &other.buffer;
-        self.display += &other.display;
+        self.folded += &other.folded;
+        self.wrapped += &other.wrapped;
     }
 }
 
 impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
     fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
-        *self += &summary.buffer.lines;
+        *self += &summary.folded.lines;
     }
 }