Start work on sending buffer operations

This commit is contained in:
Max Brunsfeld 2021-06-25 13:46:36 -07:00
parent 7ee0862b99
commit 04c80578bc
7 changed files with 312 additions and 237 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -298,7 +298,22 @@ impl LocalWorktree {
let language = language_registry.select_language(&path).cloned(); let language = language_registry.select_language(&path).cloned();
let file = File::new(handle, path.into()); let file = File::new(handle, path.into());
let buffer = cx.add_model(|cx| { 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, _| { this.update(&mut cx, |this, _| {
let this = this.as_local_mut().unwrap(); let this = this.as_local_mut().unwrap();