Start work on sending buffer operations

Max Brunsfeld created

Change summary

zed-rpc/proto/zed.proto                |  17 +
zed/src/editor.rs                      |  41 +-
zed/src/editor/buffer.rs               | 404 ++++++++++++++++-----------
zed/src/editor/display_map.rs          |  24 
zed/src/editor/display_map/fold_map.rs |  28 -
zed/src/test.rs                        |  18 
zed/src/worktree.rs                    |  17 +
7 files changed, 312 insertions(+), 237 deletions(-)

Detailed changes

zed-rpc/proto/zed.proto 🔗

@@ -15,6 +15,7 @@ message Envelope {
         OpenBuffer open_buffer = 10;
         OpenBufferResponse open_buffer_response = 11;
         CloseBuffer close_buffer = 12;
+        EditBuffer edit_buffer = 13;
     }
 }
 
@@ -69,6 +70,12 @@ message CloseBuffer {
     uint64 buffer_id = 2;
 }
 
+message EditBuffer {
+    uint64 worktree_id = 1;
+    uint64 buffer_id = 2;
+    repeated Operation operations = 3;
+}
+
 message User {
     uint64 id = 1;
     string github_login = 2;
@@ -98,6 +105,7 @@ message Buffer {
 message Operation {
     oneof variant {
         Edit edit = 1;
+        Undo undo = 2;
     }
 
     message Edit {
@@ -108,6 +116,15 @@ message Operation {
         repeated Range ranges = 5;
         optional string new_text = 6;
     }
+
+    message Undo {
+        uint32 replica_id = 1;
+        uint32 local_timestamp = 2;
+        uint32 lamport_timestamp = 3;
+        uint32 edit_replica_id = 4;
+        uint32 edit_local_timestamp = 5;
+        uint32 count = 6;
+    }
 }
 
 message VectorClockEntry {

zed/src/editor.rs 🔗

@@ -413,7 +413,7 @@ impl Editor {
         let display_map = DisplayMap::new(buffer.clone(), settings.borrow().tab_size, cx.as_ref());
 
         let mut next_selection_id = 0;
-        let (selection_set_id, _) = buffer.update(cx, |buffer, cx| {
+        let selection_set_id = buffer.update(cx, |buffer, cx| {
             buffer.add_selection_set(
                 vec![Selection {
                     id: post_inc(&mut next_selection_id),
@@ -886,8 +886,7 @@ impl Editor {
             })
             .collect();
         self.buffer
-            .update(cx, |buffer, cx| buffer.edit(edit_ranges, "", cx))
-            .unwrap();
+            .update(cx, |buffer, cx| buffer.edit(edit_ranges, "", cx));
         self.update_selections(new_selections, true, cx);
         self.end_transaction(cx);
     }
@@ -939,7 +938,7 @@ impl Editor {
 
         self.buffer.update(cx, |buffer, cx| {
             for (offset, text) in edits.into_iter().rev() {
-                buffer.edit(Some(offset..offset), text, cx).unwrap();
+                buffer.edit(Some(offset..offset), text, cx);
             }
         });
 
@@ -1029,7 +1028,7 @@ impl Editor {
         self.unfold_ranges(old_folds, cx);
         self.buffer.update(cx, |buffer, cx| {
             for (range, text) in edits.into_iter().rev() {
-                buffer.edit(Some(range), text, cx).unwrap();
+                buffer.edit(Some(range), text, cx);
             }
         });
         self.fold_ranges(new_folds, cx);
@@ -1117,7 +1116,7 @@ impl Editor {
         self.unfold_ranges(old_folds, cx);
         self.buffer.update(cx, |buffer, cx| {
             for (range, text) in edits.into_iter().rev() {
-                buffer.edit(Some(range), text, cx).unwrap();
+                buffer.edit(Some(range), text, cx);
             }
         });
         self.fold_ranges(new_folds, cx);
@@ -1226,13 +1225,9 @@ impl Editor {
                         let new_selection_start = selection.end.bias_right(buffer);
                         if selection_start == selection_end && clipboard_selection.is_entire_line {
                             let line_start = Point::new(selection_start.row, 0);
-                            buffer
-                                .edit(Some(line_start..line_start), to_insert, cx)
-                                .unwrap();
+                            buffer.edit(Some(line_start..line_start), to_insert, cx);
                         } else {
-                            buffer
-                                .edit(Some(&selection.start..&selection.end), to_insert, cx)
-                                .unwrap();
+                            buffer.edit(Some(&selection.start..&selection.end), to_insert, cx);
                         };
 
                         let new_selection_start = new_selection_start.bias_left(buffer);
@@ -1996,9 +1991,7 @@ impl Editor {
         }
 
         self.buffer.update(cx, |buffer, cx| {
-            buffer
-                .update_selection_set(self.selection_set_id, selections, cx)
-                .unwrap()
+            buffer.update_selection_set(self.selection_set_id, selections, cx)
         });
         self.pause_cursor_blinking(cx);
 
@@ -2808,16 +2801,14 @@ mod tests {
         let (_, view) = cx.add_window(|cx| Editor::for_buffer(buffer.clone(), settings, cx));
 
         buffer.update(cx, |buffer, cx| {
-            buffer
-                .edit(
-                    vec![
-                        Point::new(1, 0)..Point::new(1, 0),
-                        Point::new(1, 1)..Point::new(1, 1),
-                    ],
-                    "\t",
-                    cx,
-                )
-                .unwrap();
+            buffer.edit(
+                vec![
+                    Point::new(1, 0)..Point::new(1, 0),
+                    Point::new(1, 1)..Point::new(1, 1),
+                ],
+                "\t",
+                cx,
+            );
         });
 
         view.update(cx, |view, cx| {

zed/src/editor/buffer.rs 🔗

@@ -130,8 +130,12 @@ pub struct Buffer {
     remote_id: Option<u64>,
     local_clock: time::Local,
     lamport_clock: time::Lamport,
+    operation_callback: Option<OperationCallback>,
 }
 
+type OperationCallback =
+    Box<dyn 'static + Send + Sync + FnMut(Operation, &mut ModelContext<Buffer>)>;
+
 #[derive(Clone)]
 struct SyntaxTree {
     tree: Tree,
@@ -493,6 +497,7 @@ impl Buffer {
             deferred_replicas: HashSet::default(),
             replica_id,
             remote_id,
+            operation_callback: None,
             local_clock: time::Local::new(replica_id),
             lamport_clock: time::Lamport::new(replica_id),
         };
@@ -555,6 +560,23 @@ impl Buffer {
                         new_text: edit.new_text,
                     })
                 }
+                proto::operation::Variant::Undo(undo) => Operation::Undo {
+                    lamport_timestamp: time::Lamport {
+                        replica_id: undo.replica_id as ReplicaId,
+                        value: undo.lamport_timestamp,
+                    },
+                    undo: UndoOperation {
+                        id: time::Local {
+                            replica_id: undo.replica_id as ReplicaId,
+                            value: undo.local_timestamp,
+                        },
+                        edit_id: time::Local {
+                            replica_id: undo.edit_replica_id as ReplicaId,
+                            value: undo.edit_local_timestamp,
+                        },
+                        count: undo.count,
+                    },
+                },
             });
         buffer.apply_ops(ops, cx)?;
         Ok(buffer)
@@ -673,7 +695,7 @@ impl Buffer {
                     .read_with(&cx, |this, cx| this.diff(new_text.into(), cx))
                     .await;
                 this.update(&mut cx, |this, cx| {
-                    if let Some(_ops) = this.set_text_via_diff(diff, cx) {
+                    if this.set_text_via_diff(diff, cx) {
                         this.saved_version = this.version.clone();
                         this.saved_mtime = mtime;
                         cx.emit(Event::Reloaded);
@@ -850,33 +872,25 @@ impl Buffer {
         })
     }
 
-    fn set_text_via_diff(
-        &mut self,
-        diff: Diff,
-        cx: &mut ModelContext<Self>,
-    ) -> Option<Vec<Operation>> {
+    fn set_text_via_diff(&mut self, diff: Diff, cx: &mut ModelContext<Self>) -> bool {
         if self.version == diff.base_version {
             self.start_transaction(None, cx).unwrap();
-            let mut operations = Vec::new();
             let mut offset = 0;
             for (tag, len) in diff.changes {
                 let range = offset..(offset + len);
                 match tag {
                     ChangeTag::Equal => offset += len,
-                    ChangeTag::Delete => operations.push(self.edit(Some(range), "", cx).unwrap()),
+                    ChangeTag::Delete => self.edit(Some(range), "", cx),
                     ChangeTag::Insert => {
-                        operations.push(
-                            self.edit(Some(offset..offset), &diff.new_text[range], cx)
-                                .unwrap(),
-                        );
+                        self.edit(Some(offset..offset), &diff.new_text[range], cx);
                         offset += len;
                     }
                 }
             }
             self.end_transaction(None, cx).unwrap();
-            Some(operations)
+            true
         } else {
-            None
+            false
         }
     }
 
@@ -1041,12 +1055,7 @@ impl Buffer {
         Ok(())
     }
 
-    pub fn edit<I, S, T>(
-        &mut self,
-        ranges_iter: I,
-        new_text: T,
-        cx: &mut ModelContext<Self>,
-    ) -> Option<Operation>
+    pub fn edit<I, S, T>(&mut self, ranges_iter: I, new_text: T, cx: &mut ModelContext<Self>)
     where
         I: IntoIterator<Item = Range<S>>,
         S: ToOffset,
@@ -1077,9 +1086,7 @@ impl Buffer {
             }
         }
 
-        if ranges.is_empty() {
-            None
-        } else {
+        if !ranges.is_empty() {
             self.start_transaction_at(None, Instant::now(), cx).unwrap();
             let timestamp = InsertionTimestamp {
                 replica_id: self.replica_id,
@@ -1094,9 +1101,8 @@ impl Buffer {
             self.version.observe(edit.timestamp.local());
 
             self.end_transaction_at(None, Instant::now(), cx).unwrap();
-
-            Some(Operation::Edit(edit))
-        }
+            self.send_operation(Operation::Edit(edit), cx);
+        };
     }
 
     fn did_edit(&self, was_dirty: bool, cx: &mut ModelContext<Self>) {
@@ -1110,7 +1116,7 @@ impl Buffer {
         &mut self,
         selections: impl Into<Arc<[Selection]>>,
         cx: &mut ModelContext<Self>,
-    ) -> (SelectionSetId, Operation) {
+    ) -> SelectionSetId {
         let selections = selections.into();
         let lamport_timestamp = self.lamport_clock.tick();
         self.selections
@@ -1119,14 +1125,16 @@ impl Buffer {
 
         cx.notify();
 
-        (
-            lamport_timestamp,
+        self.send_operation(
             Operation::UpdateSelections {
                 set_id: lamport_timestamp,
                 selections: Some(selections),
                 lamport_timestamp,
             },
-        )
+            cx,
+        );
+
+        lamport_timestamp
     }
 
     pub fn update_selection_set(
@@ -1134,7 +1142,7 @@ impl Buffer {
         set_id: SelectionSetId,
         selections: impl Into<Arc<[Selection]>>,
         cx: &mut ModelContext<Self>,
-    ) -> Result<Operation> {
+    ) {
         let selections = selections.into();
         self.selections.insert(set_id, selections.clone());
 
@@ -1143,18 +1151,21 @@ impl Buffer {
 
         cx.notify();
 
-        Ok(Operation::UpdateSelections {
-            set_id,
-            selections: Some(selections),
-            lamport_timestamp,
-        })
+        self.send_operation(
+            Operation::UpdateSelections {
+                set_id,
+                selections: Some(selections),
+                lamport_timestamp,
+            },
+            cx,
+        );
     }
 
     pub fn remove_selection_set(
         &mut self,
         set_id: SelectionSetId,
         cx: &mut ModelContext<Self>,
-    ) -> Result<Operation> {
+    ) -> Result<()> {
         self.selections
             .remove(&set_id)
             .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))?;
@@ -1162,12 +1173,15 @@ impl Buffer {
         self.selections_last_update += 1;
 
         cx.notify();
-
-        Ok(Operation::UpdateSelections {
-            set_id,
-            selections: None,
-            lamport_timestamp,
-        })
+        self.send_operation(
+            Operation::UpdateSelections {
+                set_id,
+                selections: None,
+                lamport_timestamp,
+            },
+            cx,
+        );
+        Ok(())
     }
 
     pub fn selections(&self, set_id: SelectionSetId) -> Result<&[Selection]> {
@@ -1392,15 +1406,27 @@ impl Buffer {
         self.lamport_clock.observe(timestamp.lamport());
     }
 
-    pub fn undo(&mut self, cx: &mut ModelContext<Self>) -> Vec<Operation> {
+    pub fn send_operation(&mut self, operation: Operation, cx: &mut ModelContext<Self>) {
+        if let Some(operation_callback) = self.operation_callback.as_mut() {
+            operation_callback(operation, cx);
+        }
+    }
+
+    pub fn on_operation(
+        &mut self,
+        callback: impl FnMut(Operation, &mut ModelContext<Self>) + Send + Sync + 'static,
+    ) {
+        self.operation_callback = Some(Box::new(callback));
+    }
+
+    pub fn undo(&mut self, cx: &mut ModelContext<Self>) {
         let was_dirty = self.is_dirty(cx.as_ref());
         let old_version = self.version.clone();
 
-        let mut ops = Vec::new();
         if let Some(transaction) = self.history.pop_undo() {
             let selections = transaction.selections_before.clone();
             for edit_id in transaction.edits.clone() {
-                ops.push(self.undo_or_redo(edit_id).unwrap());
+                self.undo_or_redo(edit_id, cx).unwrap();
             }
 
             if let Some((set_id, selections)) = selections {
@@ -1413,19 +1439,16 @@ impl Buffer {
             self.did_edit(was_dirty, cx);
             self.reparse(cx);
         }
-
-        ops
     }
 
-    pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Vec<Operation> {
+    pub fn redo(&mut self, cx: &mut ModelContext<Self>) {
         let was_dirty = self.is_dirty(cx.as_ref());
         let old_version = self.version.clone();
 
-        let mut ops = Vec::new();
         if let Some(transaction) = self.history.pop_redo() {
             let selections = transaction.selections_after.clone();
             for edit_id in transaction.edits.clone() {
-                ops.push(self.undo_or_redo(edit_id).unwrap());
+                self.undo_or_redo(edit_id, cx).unwrap();
             }
 
             if let Some((set_id, selections)) = selections {
@@ -1438,11 +1461,9 @@ impl Buffer {
             self.did_edit(was_dirty, cx);
             self.reparse(cx);
         }
-
-        ops
     }
 
-    fn undo_or_redo(&mut self, edit_id: time::Local) -> Result<Operation> {
+    fn undo_or_redo(&mut self, edit_id: time::Local, cx: &mut ModelContext<Self>) -> Result<()> {
         let undo = UndoOperation {
             id: self.local_clock.tick(),
             edit_id,
@@ -1451,10 +1472,13 @@ impl Buffer {
         self.apply_undo(undo)?;
         self.version.observe(undo.id);
 
-        Ok(Operation::Undo {
+        let operation = Operation::Undo {
             undo,
             lamport_timestamp: self.lamport_clock.tick(),
-        })
+        };
+        self.send_operation(operation, cx);
+
+        Ok(())
     }
 
     fn apply_undo(&mut self, undo: UndoOperation) -> Result<()> {
@@ -1803,6 +1827,7 @@ impl Clone for Buffer {
             is_parsing: false,
             deferred_replicas: self.deferred_replicas.clone(),
             replica_id: self.replica_id,
+            operation_callback: None,
             remote_id: self.remote_id.clone(),
             local_clock: self.local_clock.clone(),
             lamport_clock: self.lamport_clock.clone(),
@@ -2361,8 +2386,7 @@ mod tests {
     use std::{
         cell::RefCell,
         cmp::Ordering,
-        env, fs,
-        iter::FromIterator,
+        env, fs, mem,
         path::Path,
         rc::Rc,
         sync::atomic::{self, AtomicUsize},
@@ -2373,15 +2397,15 @@ mod tests {
         cx.add_model(|cx| {
             let mut buffer = Buffer::new(0, "abc", cx);
             assert_eq!(buffer.text(), "abc");
-            buffer.edit(vec![3..3], "def", cx).unwrap();
+            buffer.edit(vec![3..3], "def", cx);
             assert_eq!(buffer.text(), "abcdef");
-            buffer.edit(vec![0..0], "ghi", cx).unwrap();
+            buffer.edit(vec![0..0], "ghi", cx);
             assert_eq!(buffer.text(), "ghiabcdef");
-            buffer.edit(vec![5..5], "jkl", cx).unwrap();
+            buffer.edit(vec![5..5], "jkl", cx);
             assert_eq!(buffer.text(), "ghiabjklcdef");
-            buffer.edit(vec![6..7], "", cx).unwrap();
+            buffer.edit(vec![6..7], "", cx);
             assert_eq!(buffer.text(), "ghiabjlcdef");
-            buffer.edit(vec![4..9], "mno", cx).unwrap();
+            buffer.edit(vec![4..9], "mno", cx);
             assert_eq!(buffer.text(), "ghiamnoef");
             buffer
         });
@@ -2395,8 +2419,13 @@ mod tests {
 
         let buffer1 = cx.add_model(|cx| Buffer::new(0, "abcdef", cx));
         let buffer2 = cx.add_model(|cx| Buffer::new(1, "abcdef", cx));
-        let mut buffer_ops = Vec::new();
+        let buffer_ops = Arc::new(Mutex::new(Vec::new()));
         buffer1.update(cx, |buffer, cx| {
+            buffer.on_operation({
+                let buffer_ops = buffer_ops.clone();
+                move |op, _| buffer_ops.lock().push(op)
+            });
+
             let buffer_1_events = buffer_1_events.clone();
             cx.subscribe(&buffer1, move |_, event, _| {
                 buffer_1_events.borrow_mut().push(event.clone())
@@ -2408,8 +2437,7 @@ mod tests {
 
             // An edit emits an edited event, followed by a dirtied event,
             // since the buffer was previously in a clean state.
-            let op = buffer.edit(Some(2..4), "XYZ", cx).unwrap();
-            buffer_ops.push(op);
+            buffer.edit(Some(2..4), "XYZ", cx);
 
             // An empty transaction does not emit any events.
             buffer.start_transaction(None, cx).unwrap();
@@ -2418,19 +2446,20 @@ mod tests {
             // A transaction containing two edits emits one edited event.
             now += Duration::from_secs(1);
             buffer.start_transaction_at(None, now, cx).unwrap();
-            buffer_ops.push(buffer.edit(Some(5..5), "u", cx).unwrap());
-            buffer_ops.push(buffer.edit(Some(6..6), "w", cx).unwrap());
+            buffer.edit(Some(5..5), "u", cx);
+            buffer.edit(Some(6..6), "w", cx);
             buffer.end_transaction_at(None, now, cx).unwrap();
 
             // Undoing a transaction emits one edited event.
-            let ops = buffer.undo(cx);
-            buffer_ops.extend_from_slice(&ops);
+            buffer.undo(cx);
         });
 
         // Incorporating a set of remote ops emits a single edited event,
         // followed by a dirtied event.
         buffer2.update(cx, |buffer, cx| {
-            buffer.apply_ops(buffer_ops, cx).unwrap();
+            buffer
+                .apply_ops::<Vec<_>>(mem::take(buffer_ops.lock().as_mut()), cx)
+                .unwrap();
         });
 
         let buffer_1_events = buffer_1_events.borrow();
@@ -2472,7 +2501,7 @@ mod tests {
                 );
 
                 for _i in 0..operations {
-                    let (old_ranges, new_text, _) = buffer.randomly_mutate(rng, cx);
+                    let (old_ranges, new_text) = buffer.randomly_mutate(rng, cx);
                     for old_range in old_ranges.iter().rev() {
                         reference_string.replace_range(old_range.clone(), &new_text);
                     }
@@ -2484,7 +2513,7 @@ mod tests {
                     );
 
                     if rng.gen_bool(0.25) {
-                        buffer.randomly_undo_redo(rng);
+                        buffer.randomly_undo_redo(rng, cx);
                         reference_string = buffer.text();
                     }
 
@@ -2538,10 +2567,10 @@ mod tests {
     fn test_line_len(cx: &mut gpui::MutableAppContext) {
         cx.add_model(|cx| {
             let mut buffer = Buffer::new(0, "", cx);
-            buffer.edit(vec![0..0], "abcd\nefg\nhij", cx).unwrap();
-            buffer.edit(vec![12..12], "kl\nmno", cx).unwrap();
-            buffer.edit(vec![18..18], "\npqrs\n", cx).unwrap();
-            buffer.edit(vec![18..21], "\nPQ", cx).unwrap();
+            buffer.edit(vec![0..0], "abcd\nefg\nhij", cx);
+            buffer.edit(vec![12..12], "kl\nmno", cx);
+            buffer.edit(vec![18..18], "\npqrs\n", cx);
+            buffer.edit(vec![18..21], "\nPQ", cx);
 
             assert_eq!(buffer.line_len(0), 4);
             assert_eq!(buffer.line_len(1), 3);
@@ -2620,10 +2649,10 @@ mod tests {
     fn test_chars_at(cx: &mut gpui::MutableAppContext) {
         cx.add_model(|cx| {
             let mut buffer = Buffer::new(0, "", cx);
-            buffer.edit(vec![0..0], "abcd\nefgh\nij", cx).unwrap();
-            buffer.edit(vec![12..12], "kl\nmno", cx).unwrap();
-            buffer.edit(vec![18..18], "\npqrs", cx).unwrap();
-            buffer.edit(vec![18..21], "\nPQ", cx).unwrap();
+            buffer.edit(vec![0..0], "abcd\nefgh\nij", cx);
+            buffer.edit(vec![12..12], "kl\nmno", cx);
+            buffer.edit(vec![18..18], "\npqrs", cx);
+            buffer.edit(vec![18..21], "\nPQ", cx);
 
             let chars = buffer.chars_at(Point::new(0, 0));
             assert_eq!(chars.collect::<String>(), "abcd\nefgh\nijkl\nmno\nPQrs");
@@ -2642,8 +2671,8 @@ mod tests {
 
             // Regression test:
             let mut buffer = Buffer::new(0, "", cx);
-            buffer.edit(vec![0..0], "[workspace]\nmembers = [\n    \"xray_core\",\n    \"xray_server\",\n    \"xray_cli\",\n    \"xray_wasm\",\n]\n", cx).unwrap();
-            buffer.edit(vec![60..60], "\n", cx).unwrap();
+            buffer.edit(vec![0..0], "[workspace]\nmembers = [\n    \"xray_core\",\n    \"xray_server\",\n    \"xray_cli\",\n    \"xray_wasm\",\n]\n", cx);
+            buffer.edit(vec![60..60], "\n", cx);
 
             let chars = buffer.chars_at(Point::new(6, 0));
             assert_eq!(chars.collect::<String>(), "    \"xray_wasm\",\n]\n");
@@ -2656,32 +2685,32 @@ mod tests {
     fn test_anchors(cx: &mut gpui::MutableAppContext) {
         cx.add_model(|cx| {
             let mut buffer = Buffer::new(0, "", cx);
-            buffer.edit(vec![0..0], "abc", cx).unwrap();
+            buffer.edit(vec![0..0], "abc", cx);
             let left_anchor = buffer.anchor_before(2);
             let right_anchor = buffer.anchor_after(2);
 
-            buffer.edit(vec![1..1], "def\n", cx).unwrap();
+            buffer.edit(vec![1..1], "def\n", cx);
             assert_eq!(buffer.text(), "adef\nbc");
             assert_eq!(left_anchor.to_offset(&buffer), 6);
             assert_eq!(right_anchor.to_offset(&buffer), 6);
             assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
             assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
 
-            buffer.edit(vec![2..3], "", cx).unwrap();
+            buffer.edit(vec![2..3], "", cx);
             assert_eq!(buffer.text(), "adf\nbc");
             assert_eq!(left_anchor.to_offset(&buffer), 5);
             assert_eq!(right_anchor.to_offset(&buffer), 5);
             assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
             assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
 
-            buffer.edit(vec![5..5], "ghi\n", cx).unwrap();
+            buffer.edit(vec![5..5], "ghi\n", cx);
             assert_eq!(buffer.text(), "adf\nbghi\nc");
             assert_eq!(left_anchor.to_offset(&buffer), 5);
             assert_eq!(right_anchor.to_offset(&buffer), 9);
             assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
             assert_eq!(right_anchor.to_point(&buffer), Point { row: 2, column: 0 });
 
-            buffer.edit(vec![7..9], "", cx).unwrap();
+            buffer.edit(vec![7..9], "", cx);
             assert_eq!(buffer.text(), "adf\nbghc");
             assert_eq!(left_anchor.to_offset(&buffer), 5);
             assert_eq!(right_anchor.to_offset(&buffer), 7);
@@ -2798,7 +2827,7 @@ mod tests {
             let before_start_anchor = buffer.anchor_before(0);
             let after_end_anchor = buffer.anchor_after(0);
 
-            buffer.edit(vec![0..0], "abc", cx).unwrap();
+            buffer.edit(vec![0..0], "abc", cx);
             assert_eq!(buffer.text(), "abc");
             assert_eq!(before_start_anchor.to_offset(&buffer), 0);
             assert_eq!(after_end_anchor.to_offset(&buffer), 3);
@@ -2806,8 +2835,8 @@ mod tests {
             let after_start_anchor = buffer.anchor_after(0);
             let before_end_anchor = buffer.anchor_before(3);
 
-            buffer.edit(vec![3..3], "def", cx).unwrap();
-            buffer.edit(vec![0..0], "ghi", cx).unwrap();
+            buffer.edit(vec![3..3], "def", cx);
+            buffer.edit(vec![0..0], "ghi", cx);
             assert_eq!(buffer.text(), "ghiabcdef");
             assert_eq!(before_start_anchor.to_offset(&buffer), 0);
             assert_eq!(after_start_anchor.to_offset(&buffer), 3);
@@ -2849,7 +2878,7 @@ mod tests {
             assert!(!buffer.is_dirty(cx.as_ref()));
             assert!(events.borrow().is_empty());
 
-            buffer.edit(vec![1..2], "", cx).unwrap();
+            buffer.edit(vec![1..2], "", cx);
         });
 
         // after the first edit, the buffer is dirty, and emits a dirtied event.
@@ -2868,8 +2897,8 @@ mod tests {
             assert_eq!(*events.borrow(), &[Event::Saved]);
             events.borrow_mut().clear();
 
-            buffer.edit(vec![1..1], "B", cx).unwrap();
-            buffer.edit(vec![2..2], "D", cx).unwrap();
+            buffer.edit(vec![1..1], "B", cx);
+            buffer.edit(vec![2..2], "D", cx);
         });
 
         // after editing again, the buffer is dirty, and emits another dirty event.
@@ -2884,7 +2913,7 @@ mod tests {
 
             // TODO - currently, after restoring the buffer to its
             // previously-saved state, the is still considered dirty.
-            buffer.edit(vec![1..3], "", cx).unwrap();
+            buffer.edit(vec![1..3], "", cx);
             assert!(buffer.text() == "ac");
             assert!(buffer.is_dirty(cx.as_ref()));
         });
@@ -2932,7 +2961,7 @@ mod tests {
 
         tree.flush_fs_events(&cx).await;
         buffer3.update(&mut cx, |buffer, cx| {
-            buffer.edit(Some(0..0), "x", cx).unwrap();
+            buffer.edit(Some(0..0), "x", cx);
         });
         events.borrow_mut().clear();
         fs::remove_file(dir.path().join("file3")).unwrap();
@@ -2960,7 +2989,7 @@ mod tests {
             .unwrap();
 
         // Add a cursor at the start of each row.
-        let (selection_set_id, _) = buffer.update(&mut cx, |buffer, cx| {
+        let selection_set_id = buffer.update(&mut cx, |buffer, cx| {
             assert!(!buffer.is_dirty(cx.as_ref()));
             buffer.add_selection_set(
                 (0..3)
@@ -3016,7 +3045,7 @@ mod tests {
 
         // Modify the buffer
         buffer.update(&mut cx, |buffer, cx| {
-            buffer.edit(vec![0..0], " ", cx).unwrap();
+            buffer.edit(vec![0..0], " ", cx);
             assert!(buffer.is_dirty(cx.as_ref()));
         });
 
@@ -3051,30 +3080,42 @@ mod tests {
         cx.add_model(|cx| {
             let mut buffer = Buffer::new(0, "1234", cx);
 
-            let edit1 = buffer.edit(vec![1..1], "abx", cx).unwrap();
-            let edit2 = buffer.edit(vec![3..4], "yzef", cx).unwrap();
-            let edit3 = buffer.edit(vec![3..5], "cd", cx).unwrap();
+            let operations = Arc::new(Mutex::new(Vec::new()));
+            buffer.on_operation({
+                let edits = operations.clone();
+                move |operation, _| {
+                    edits.lock().push(operation);
+                }
+            });
+
+            buffer.edit(vec![1..1], "abx", cx);
+            buffer.edit(vec![3..4], "yzef", cx);
+            buffer.edit(vec![3..5], "cd", cx);
             assert_eq!(buffer.text(), "1abcdef234");
 
-            buffer.undo_or_redo(edit1.edit_id().unwrap()).unwrap();
+            let edit1 = operations.lock()[0].clone();
+            let edit2 = operations.lock()[1].clone();
+            let edit3 = operations.lock()[2].clone();
+
+            buffer.undo_or_redo(edit1.edit_id().unwrap(), cx).unwrap();
             assert_eq!(buffer.text(), "1cdef234");
-            buffer.undo_or_redo(edit1.edit_id().unwrap()).unwrap();
+            buffer.undo_or_redo(edit1.edit_id().unwrap(), cx).unwrap();
             assert_eq!(buffer.text(), "1abcdef234");
 
-            buffer.undo_or_redo(edit2.edit_id().unwrap()).unwrap();
+            buffer.undo_or_redo(edit2.edit_id().unwrap(), cx).unwrap();
             assert_eq!(buffer.text(), "1abcdx234");
-            buffer.undo_or_redo(edit3.edit_id().unwrap()).unwrap();
+            buffer.undo_or_redo(edit3.edit_id().unwrap(), cx).unwrap();
             assert_eq!(buffer.text(), "1abx234");
-            buffer.undo_or_redo(edit2.edit_id().unwrap()).unwrap();
+            buffer.undo_or_redo(edit2.edit_id().unwrap(), cx).unwrap();
             assert_eq!(buffer.text(), "1abyzef234");
-            buffer.undo_or_redo(edit3.edit_id().unwrap()).unwrap();
+            buffer.undo_or_redo(edit3.edit_id().unwrap(), cx).unwrap();
             assert_eq!(buffer.text(), "1abcdef234");
 
-            buffer.undo_or_redo(edit3.edit_id().unwrap()).unwrap();
+            buffer.undo_or_redo(edit3.edit_id().unwrap(), cx).unwrap();
             assert_eq!(buffer.text(), "1abyzef234");
-            buffer.undo_or_redo(edit1.edit_id().unwrap()).unwrap();
+            buffer.undo_or_redo(edit1.edit_id().unwrap(), cx).unwrap();
             assert_eq!(buffer.text(), "1yzef234");
-            buffer.undo_or_redo(edit2.edit_id().unwrap()).unwrap();
+            buffer.undo_or_redo(edit2.edit_id().unwrap(), cx).unwrap();
             assert_eq!(buffer.text(), "1234");
 
             buffer
@@ -3087,38 +3128,34 @@ mod tests {
             let mut now = Instant::now();
             let mut buffer = Buffer::new(0, "123456", cx);
 
-            let (set_id, _) =
+            let set_id =
                 buffer.add_selection_set(buffer.selections_from_ranges(vec![4..4]).unwrap(), cx);
             buffer.start_transaction_at(Some(set_id), now, cx).unwrap();
-            buffer.edit(vec![2..4], "cd", cx).unwrap();
+            buffer.edit(vec![2..4], "cd", cx);
             buffer.end_transaction_at(Some(set_id), now, cx).unwrap();
             assert_eq!(buffer.text(), "12cd56");
             assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![4..4]);
 
             buffer.start_transaction_at(Some(set_id), now, cx).unwrap();
-            buffer
-                .update_selection_set(
-                    set_id,
-                    buffer.selections_from_ranges(vec![1..3]).unwrap(),
-                    cx,
-                )
-                .unwrap();
-            buffer.edit(vec![4..5], "e", cx).unwrap();
+            buffer.update_selection_set(
+                set_id,
+                buffer.selections_from_ranges(vec![1..3]).unwrap(),
+                cx,
+            );
+            buffer.edit(vec![4..5], "e", cx);
             buffer.end_transaction_at(Some(set_id), now, cx).unwrap();
             assert_eq!(buffer.text(), "12cde6");
             assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![1..3]);
 
             now += UNDO_GROUP_INTERVAL + Duration::from_millis(1);
             buffer.start_transaction_at(Some(set_id), now, cx).unwrap();
-            buffer
-                .update_selection_set(
-                    set_id,
-                    buffer.selections_from_ranges(vec![2..2]).unwrap(),
-                    cx,
-                )
-                .unwrap();
-            buffer.edit(vec![0..1], "a", cx).unwrap();
-            buffer.edit(vec![1..1], "b", cx).unwrap();
+            buffer.update_selection_set(
+                set_id,
+                buffer.selections_from_ranges(vec![2..2]).unwrap(),
+                cx,
+            );
+            buffer.edit(vec![0..1], "a", cx);
+            buffer.edit(vec![1..1], "b", cx);
             buffer.end_transaction_at(Some(set_id), now, cx).unwrap();
             assert_eq!(buffer.text(), "ab2cde6");
             assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![3..3]);
@@ -3157,22 +3194,40 @@ mod tests {
         let buffer2 = cx.add_model(|cx| Buffer::new(2, text, cx));
         let buffer3 = cx.add_model(|cx| Buffer::new(3, text, cx));
 
-        let buf1_op = buffer1.update(cx, |buffer, cx| {
-            let op = buffer.edit(vec![1..2], "12", cx).unwrap();
+        let ops = Arc::new(Mutex::new(Vec::new()));
+
+        buffer1.update(cx, |buffer, cx| {
+            buffer.on_operation({
+                let ops = ops.clone();
+                move |operation, _| ops.lock().push(operation)
+            });
+
+            buffer.edit(vec![1..2], "12", cx);
             assert_eq!(buffer.text(), "a12cdef");
-            op
         });
-        let buf2_op = buffer2.update(cx, |buffer, cx| {
-            let op = buffer.edit(vec![3..4], "34", cx).unwrap();
+        buffer2.update(cx, |buffer, cx| {
+            buffer.on_operation({
+                let ops = ops.clone();
+                move |operation, _| ops.lock().push(operation)
+            });
+
+            buffer.edit(vec![3..4], "34", cx);
             assert_eq!(buffer.text(), "abc34ef");
-            op
         });
-        let buf3_op = buffer3.update(cx, |buffer, cx| {
-            let op = buffer.edit(vec![5..6], "56", cx).unwrap();
+        buffer3.update(cx, |buffer, cx| {
+            buffer.on_operation({
+                let ops = ops.clone();
+                move |operation, _| ops.lock().push(operation)
+            });
+
+            buffer.edit(vec![5..6], "56", cx);
             assert_eq!(buffer.text(), "abcde56");
-            op
         });
 
+        let buf1_op = ops.lock()[0].clone();
+        let buf2_op = ops.lock()[1].clone();
+        let buf3_op = ops.lock()[2].clone();
+
         buffer1.update(cx, |buffer, _| {
             buffer.apply_op(buf2_op.clone()).unwrap();
             buffer.apply_op(buf3_op.clone()).unwrap();
@@ -3209,7 +3264,8 @@ mod tests {
 
         for seed in start_seed..start_seed + iterations {
             dbg!(seed);
-            let mut rng = &mut StdRng::seed_from_u64(seed);
+            let mut rng = StdRng::seed_from_u64(seed);
+            let network = Arc::new(Mutex::new(Network::new(StdRng::seed_from_u64(seed))));
 
             let base_text_len = rng.gen_range(0..10);
             let base_text = RandomCharIter::new(&mut rng)
@@ -3217,12 +3273,22 @@ mod tests {
                 .collect::<String>();
             let mut replica_ids = Vec::new();
             let mut buffers = Vec::new();
-            let mut network = Network::new();
             for i in 0..peers {
-                let buffer = cx.add_model(|cx| Buffer::new(i as ReplicaId, base_text.as_str(), cx));
+                let buffer = cx.add_model(|cx| {
+                    let replica_id = i as ReplicaId;
+                    let mut buffer = Buffer::new(replica_id, base_text.as_str(), cx);
+                    buffer.on_operation({
+                        let network = network.clone();
+                        move |op, _| {
+                            network.lock().broadcast(replica_id, vec![op]);
+                        }
+                    });
+                    buffer
+                });
+
                 buffers.push(buffer);
                 replica_ids.push(i as u16);
-                network.add_peer(i as u16);
+                network.lock().add_peer(i as u16);
             }
 
             log::info!("initial text: {:?}", base_text);
@@ -3233,18 +3299,16 @@ mod tests {
                 let replica_id = replica_ids[replica_index];
                 buffers[replica_index].update(cx, |buffer, cx| match rng.gen_range(0..=100) {
                     0..=50 if mutation_count != 0 => {
-                        let (_, _, ops) = buffer.randomly_mutate(&mut rng, cx);
+                        buffer.randomly_mutate(&mut rng, cx);
                         log::info!("buffer {} text: {:?}", buffer.replica_id, buffer.text());
-                        network.broadcast(replica_id, ops, &mut rng);
                         mutation_count -= 1;
                     }
                     51..=70 if mutation_count != 0 => {
-                        let ops = buffer.randomly_undo_redo(&mut rng);
-                        network.broadcast(replica_id, ops, &mut rng);
+                        buffer.randomly_undo_redo(&mut rng, cx);
                         mutation_count -= 1;
                     }
-                    71..=100 if network.has_unreceived(replica_id) => {
-                        let ops = network.receive(replica_id, &mut rng);
+                    71..=100 if network.lock().has_unreceived(replica_id) => {
+                        let ops = network.lock().receive(replica_id);
                         if !ops.is_empty() {
                             log::info!(
                                 "peer {} applying {} ops from the network.",
@@ -3257,7 +3321,7 @@ mod tests {
                     _ => {}
                 });
 
-                if mutation_count == 0 && network.is_idle() {
+                if mutation_count == 0 && network.lock().is_idle() {
                     break;
                 }
             }
@@ -3318,11 +3382,11 @@ mod tests {
             buf.start_transaction(None, cx).unwrap();
 
             let offset = buf.text().find(")").unwrap();
-            buf.edit(vec![offset..offset], "b: C", cx).unwrap();
+            buf.edit(vec![offset..offset], "b: C", cx);
             assert!(!buf.is_parsing());
 
             let offset = buf.text().find("}").unwrap();
-            buf.edit(vec![offset..offset], " d; ", cx).unwrap();
+            buf.edit(vec![offset..offset], " d; ", cx);
             assert!(!buf.is_parsing());
 
             buf.end_transaction(None, cx).unwrap();
@@ -3347,19 +3411,19 @@ mod tests {
         // * add a turbofish to the method call
         buffer.update(&mut cx, |buf, cx| {
             let offset = buf.text().find(";").unwrap();
-            buf.edit(vec![offset..offset], ".e", cx).unwrap();
+            buf.edit(vec![offset..offset], ".e", cx);
             assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
             assert!(buf.is_parsing());
         });
         buffer.update(&mut cx, |buf, cx| {
             let offset = buf.text().find(";").unwrap();
-            buf.edit(vec![offset..offset], "(f)", cx).unwrap();
+            buf.edit(vec![offset..offset], "(f)", cx);
             assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
             assert!(buf.is_parsing());
         });
         buffer.update(&mut cx, |buf, cx| {
             let offset = buf.text().find("(f)").unwrap();
-            buf.edit(vec![offset..offset], "::<G>", cx).unwrap();
+            buf.edit(vec![offset..offset], "::<G>", cx);
             assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
             assert!(buf.is_parsing());
         });
@@ -3484,7 +3548,7 @@ mod tests {
             rng: &mut T,
             old_range_count: usize,
             cx: &mut ModelContext<Self>,
-        ) -> (Vec<Range<usize>>, String, Option<Operation>)
+        ) -> (Vec<Range<usize>>, String)
         where
             T: Rng,
         {
@@ -3504,20 +3568,19 @@ mod tests {
                 old_ranges,
                 new_text
             );
-            let operation = self.edit(old_ranges.iter().cloned(), new_text.as_str(), cx);
-            (old_ranges, new_text, operation)
+            self.edit(old_ranges.iter().cloned(), new_text.as_str(), cx);
+            (old_ranges, new_text)
         }
 
         pub fn randomly_mutate<T>(
             &mut self,
             rng: &mut T,
             cx: &mut ModelContext<Self>,
-        ) -> (Vec<Range<usize>>, String, Vec<Operation>)
+        ) -> (Vec<Range<usize>>, String)
         where
             T: Rng,
         {
-            let (old_ranges, new_text, operation) = self.randomly_edit(rng, 5, cx);
-            let mut operations = Vec::from_iter(operation);
+            let (old_ranges, new_text) = self.randomly_edit(rng, 5, cx);
 
             // Randomly add, remove or mutate selection sets.
             let replica_selection_sets = &self
@@ -3527,8 +3590,7 @@ mod tests {
                 .collect::<Vec<_>>();
             let set_id = replica_selection_sets.choose(rng);
             if set_id.is_some() && rng.gen_bool(1.0 / 6.0) {
-                let op = self.remove_selection_set(*set_id.unwrap(), cx).unwrap();
-                operations.push(op);
+                self.remove_selection_set(*set_id.unwrap(), cx).unwrap();
             } else {
                 let mut ranges = Vec::new();
                 for _ in 0..5 {
@@ -3536,27 +3598,23 @@ mod tests {
                 }
                 let new_selections = self.selections_from_ranges(ranges).unwrap();
 
-                let op = if set_id.is_none() || rng.gen_bool(1.0 / 5.0) {
-                    self.add_selection_set(new_selections, cx).1
+                if set_id.is_none() || rng.gen_bool(1.0 / 5.0) {
+                    self.add_selection_set(new_selections, cx);
                 } else {
-                    self.update_selection_set(*set_id.unwrap(), new_selections, cx)
-                        .unwrap()
-                };
-                operations.push(op);
+                    self.update_selection_set(*set_id.unwrap(), new_selections, cx);
+                }
             }
 
-            (old_ranges, new_text, operations)
+            (old_ranges, new_text)
         }
 
-        pub fn randomly_undo_redo(&mut self, rng: &mut impl Rng) -> Vec<Operation> {
-            let mut ops = Vec::new();
+        pub fn randomly_undo_redo(&mut self, rng: &mut impl Rng, cx: &mut ModelContext<Self>) {
             for _ in 0..rng.gen_range(1..=5) {
                 if let Some(edit_id) = self.history.ops.keys().choose(rng).copied() {
                     log::info!("undoing buffer {} operation {:?}", self.replica_id, edit_id);
-                    ops.push(self.undo_or_redo(edit_id).unwrap());
+                    self.undo_or_redo(edit_id, cx).unwrap();
                 }
             }
-            ops
         }
 
         fn selections_from_ranges<I>(&self, ranges: I) -> Result<Vec<Selection>>

zed/src/editor/display_map.rs 🔗

@@ -466,19 +466,17 @@ mod tests {
         let text = sample_text(6, 6);
         let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
         let map = DisplayMap::new(buffer.clone(), 4, cx.as_ref());
-        buffer
-            .update(cx, |buffer, cx| {
-                buffer.edit(
-                    vec![
-                        Point::new(1, 0)..Point::new(1, 0),
-                        Point::new(1, 1)..Point::new(1, 1),
-                        Point::new(2, 1)..Point::new(2, 1),
-                    ],
-                    "\t",
-                    cx,
-                )
-            })
-            .unwrap();
+        buffer.update(cx, |buffer, cx| {
+            buffer.edit(
+                vec![
+                    Point::new(1, 0)..Point::new(1, 0),
+                    Point::new(1, 1)..Point::new(1, 1),
+                    Point::new(2, 1)..Point::new(2, 1),
+                ],
+                "\t",
+                cx,
+            )
+        });
 
         assert_eq!(
             &map.snapshot(cx.as_ref())

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

@@ -873,24 +873,20 @@ mod tests {
         assert_eq!(map.text(cx.as_ref()), "aa…cc…eeeee");
 
         buffer.update(cx, |buffer, cx| {
-            buffer
-                .edit(
-                    vec![
-                        Point::new(0, 0)..Point::new(0, 1),
-                        Point::new(2, 3)..Point::new(2, 3),
-                    ],
-                    "123",
-                    cx,
-                )
-                .unwrap();
+            buffer.edit(
+                vec![
+                    Point::new(0, 0)..Point::new(0, 1),
+                    Point::new(2, 3)..Point::new(2, 3),
+                ],
+                "123",
+                cx,
+            );
         });
         assert_eq!(map.text(cx.as_ref()), "123a…c123c…eeeee");
 
         buffer.update(cx, |buffer, cx| {
             let start_version = buffer.version.clone();
-            buffer
-                .edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", cx)
-                .unwrap();
+            buffer.edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", cx);
             buffer.edits_since(start_version).collect::<Vec<_>>()
         });
         assert_eq!(map.text(cx.as_ref()), "123a…c123456eee");
@@ -932,7 +928,7 @@ mod tests {
             // Edit within one of the folds.
             buffer.update(cx, |buffer, cx| {
                 let version = buffer.version();
-                buffer.edit(vec![0..1], "12345", cx).unwrap();
+                buffer.edit(vec![0..1], "12345", cx);
                 buffer.edits_since(version).collect::<Vec<_>>()
             });
             map.check_invariants(cx.as_ref());
@@ -971,9 +967,7 @@ mod tests {
         assert_eq!(map.text(cx.as_ref()), "aa…cccc\nd…eeeee");
 
         buffer.update(cx, |buffer, cx| {
-            buffer
-                .edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", cx)
-                .unwrap();
+            buffer.edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", cx)
         });
         assert_eq!(map.text(cx.as_ref()), "aa…eeeee");
     }

zed/src/test.rs 🔗

@@ -19,17 +19,19 @@ struct Envelope<T: Clone> {
 }
 
 #[cfg(test)]
-pub(crate) struct Network<T: Clone> {
+pub(crate) struct Network<T: Clone, R: rand::Rng> {
     inboxes: std::collections::BTreeMap<ReplicaId, Vec<Envelope<T>>>,
     all_messages: Vec<T>,
+    rng: R,
 }
 
 #[cfg(test)]
-impl<T: Clone> Network<T> {
-    pub fn new() -> Self {
+impl<T: Clone, R: rand::Rng> Network<T, R> {
+    pub fn new(rng: R) -> Self {
         Network {
             inboxes: Default::default(),
             all_messages: Vec::new(),
+            rng,
         }
     }
 
@@ -41,7 +43,7 @@ impl<T: Clone> Network<T> {
         self.inboxes.values().all(|i| i.is_empty())
     }
 
-    pub fn broadcast<R: rand::Rng>(&mut self, sender: ReplicaId, messages: Vec<T>, rng: &mut R) {
+    pub fn broadcast(&mut self, sender: ReplicaId, messages: Vec<T>) {
         for (replica, inbox) in self.inboxes.iter_mut() {
             if *replica != sender {
                 for message in &messages {
@@ -60,8 +62,8 @@ impl<T: Clone> Network<T> {
 
                     // Insert one or more duplicates of this message *after* the previous
                     // message delivered by this replica.
-                    for _ in 0..rng.gen_range(1..4) {
-                        let insertion_index = rng.gen_range(min_index..inbox.len() + 1);
+                    for _ in 0..self.rng.gen_range(1..4) {
+                        let insertion_index = self.rng.gen_range(min_index..inbox.len() + 1);
                         inbox.insert(
                             insertion_index,
                             Envelope {
@@ -80,9 +82,9 @@ impl<T: Clone> Network<T> {
         !self.inboxes[&receiver].is_empty()
     }
 
-    pub fn receive<R: rand::Rng>(&mut self, receiver: ReplicaId, rng: &mut R) -> Vec<T> {
+    pub fn receive(&mut self, receiver: ReplicaId) -> Vec<T> {
         let inbox = self.inboxes.get_mut(&receiver).unwrap();
-        let count = rng.gen_range(0..inbox.len() + 1);
+        let count = self.rng.gen_range(0..inbox.len() + 1);
         inbox
             .drain(0..count)
             .map(|envelope| envelope.message)

zed/src/worktree.rs 🔗

@@ -298,7 +298,22 @@ impl LocalWorktree {
                 let language = language_registry.select_language(&path).cloned();
                 let file = File::new(handle, path.into());
                 let buffer = cx.add_model(|cx| {
-                    Buffer::from_history(0, History::new(contents.into()), Some(file), language, cx)
+                    let mut buffer = Buffer::from_history(
+                        0,
+                        History::new(contents.into()),
+                        Some(file),
+                        language,
+                        cx,
+                    );
+                    buffer.on_operation({
+                        let worktree = handle.clone();
+                        move |operation, cx| {
+                            worktree.update(cx, |tree, cx| {
+                                // tree.buffer_changed(cx.model_id(), operation)
+                            });
+                        }
+                    });
+                    buffer
                 });
                 this.update(&mut cx, |this, _| {
                     let this = this.as_local_mut().unwrap();