Add the ability to propose changes to a set of buffers (#18170)
This PR introduces functionality for creating *branches* of buffers that can be used to preview and edit change sets that haven't yet been applied to the buffers themselves. Release Notes: - N/A --------- Co-authored-by: Marshall Bowers <elliott.codes@gmail.com> Co-authored-by: Marshall <marshall@zed.dev>
This commit is contained in:
parent
e309fbda2a
commit
743feb98bc
20 changed files with 622 additions and 186 deletions
|
@ -21,8 +21,8 @@ use async_watch as watch;
|
|||
pub use clock::ReplicaId;
|
||||
use futures::channel::oneshot;
|
||||
use gpui::{
|
||||
AnyElement, AppContext, EventEmitter, HighlightStyle, ModelContext, Pixels, Task, TaskLabel,
|
||||
WindowContext,
|
||||
AnyElement, AppContext, Context as _, EventEmitter, HighlightStyle, Model, ModelContext,
|
||||
Pixels, Task, TaskLabel, WindowContext,
|
||||
};
|
||||
use lsp::LanguageServerId;
|
||||
use parking_lot::Mutex;
|
||||
|
@ -84,11 +84,17 @@ pub enum Capability {
|
|||
|
||||
pub type BufferRow = u32;
|
||||
|
||||
#[derive(Clone)]
|
||||
enum BufferDiffBase {
|
||||
Git(Rope),
|
||||
PastBufferVersion(Model<Buffer>, BufferSnapshot),
|
||||
}
|
||||
|
||||
/// An in-memory representation of a source code file, including its text,
|
||||
/// syntax trees, git status, and diagnostics.
|
||||
pub struct Buffer {
|
||||
text: TextBuffer,
|
||||
diff_base: Option<Rope>,
|
||||
diff_base: Option<BufferDiffBase>,
|
||||
git_diff: git::diff::BufferDiff,
|
||||
file: Option<Arc<dyn File>>,
|
||||
/// The mtime of the file when this buffer was last loaded from
|
||||
|
@ -121,6 +127,7 @@ pub struct Buffer {
|
|||
/// Memoize calls to has_changes_since(saved_version).
|
||||
/// The contents of a cell are (self.version, has_changes) at the time of a last call.
|
||||
has_unsaved_edits: Cell<(clock::Global, bool)>,
|
||||
_subscriptions: Vec<gpui::Subscription>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -308,7 +315,10 @@ pub enum Operation {
|
|||
pub enum BufferEvent {
|
||||
/// The buffer was changed in a way that must be
|
||||
/// propagated to its other replicas.
|
||||
Operation(Operation),
|
||||
Operation {
|
||||
operation: Operation,
|
||||
is_local: bool,
|
||||
},
|
||||
/// The buffer was edited.
|
||||
Edited,
|
||||
/// The buffer's `dirty` bit changed.
|
||||
|
@ -644,7 +654,7 @@ impl Buffer {
|
|||
id: self.remote_id().into(),
|
||||
file: self.file.as_ref().map(|f| f.to_proto(cx)),
|
||||
base_text: self.base_text().to_string(),
|
||||
diff_base: self.diff_base.as_ref().map(|h| h.to_string()),
|
||||
diff_base: self.diff_base().as_ref().map(|h| h.to_string()),
|
||||
line_ending: proto::serialize_line_ending(self.line_ending()) as i32,
|
||||
saved_version: proto::serialize_version(&self.saved_version),
|
||||
saved_mtime: self.saved_mtime.map(|time| time.into()),
|
||||
|
@ -734,12 +744,10 @@ impl Buffer {
|
|||
was_dirty_before_starting_transaction: None,
|
||||
has_unsaved_edits: Cell::new((buffer.version(), false)),
|
||||
text: buffer,
|
||||
diff_base: diff_base
|
||||
.map(|mut raw_diff_base| {
|
||||
LineEnding::normalize(&mut raw_diff_base);
|
||||
raw_diff_base
|
||||
})
|
||||
.map(Rope::from),
|
||||
diff_base: diff_base.map(|mut raw_diff_base| {
|
||||
LineEnding::normalize(&mut raw_diff_base);
|
||||
BufferDiffBase::Git(Rope::from(raw_diff_base))
|
||||
}),
|
||||
diff_base_version: 0,
|
||||
git_diff,
|
||||
file,
|
||||
|
@ -759,6 +767,7 @@ impl Buffer {
|
|||
completion_triggers_timestamp: Default::default(),
|
||||
deferred_ops: OperationQueue::new(),
|
||||
has_conflict: false,
|
||||
_subscriptions: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -782,6 +791,52 @@ impl Buffer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn branch(&mut self, cx: &mut ModelContext<Self>) -> Model<Self> {
|
||||
let this = cx.handle();
|
||||
cx.new_model(|cx| {
|
||||
let mut branch = Self {
|
||||
diff_base: Some(BufferDiffBase::PastBufferVersion(
|
||||
this.clone(),
|
||||
self.snapshot(),
|
||||
)),
|
||||
language: self.language.clone(),
|
||||
has_conflict: self.has_conflict,
|
||||
has_unsaved_edits: Cell::new(self.has_unsaved_edits.get_mut().clone()),
|
||||
_subscriptions: vec![cx.subscribe(&this, |branch: &mut Self, _, event, cx| {
|
||||
if let BufferEvent::Operation { operation, .. } = event {
|
||||
branch.apply_ops([operation.clone()], cx);
|
||||
branch.diff_base_version += 1;
|
||||
}
|
||||
})],
|
||||
..Self::build(
|
||||
self.text.branch(),
|
||||
None,
|
||||
self.file.clone(),
|
||||
self.capability(),
|
||||
)
|
||||
};
|
||||
if let Some(language_registry) = self.language_registry() {
|
||||
branch.set_language_registry(language_registry);
|
||||
}
|
||||
|
||||
branch
|
||||
})
|
||||
}
|
||||
|
||||
pub fn merge(&mut self, branch: &Model<Self>, cx: &mut ModelContext<Self>) {
|
||||
let branch = branch.read(cx);
|
||||
let edits = branch
|
||||
.edits_since::<usize>(&self.version)
|
||||
.map(|edit| {
|
||||
(
|
||||
edit.old,
|
||||
branch.text_for_range(edit.new).collect::<String>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
self.edit(edits, None, cx);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn as_text_snapshot(&self) -> &text::BufferSnapshot {
|
||||
&self.text
|
||||
|
@ -961,20 +1016,23 @@ impl Buffer {
|
|||
|
||||
/// Returns the current diff base, see [Buffer::set_diff_base].
|
||||
pub fn diff_base(&self) -> Option<&Rope> {
|
||||
self.diff_base.as_ref()
|
||||
match self.diff_base.as_ref()? {
|
||||
BufferDiffBase::Git(rope) => Some(rope),
|
||||
BufferDiffBase::PastBufferVersion(_, buffer_snapshot) => {
|
||||
Some(buffer_snapshot.as_rope())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the text that will be used to compute a Git diff
|
||||
/// against the buffer text.
|
||||
pub fn set_diff_base(&mut self, diff_base: Option<String>, cx: &mut ModelContext<Self>) {
|
||||
self.diff_base = diff_base
|
||||
.map(|mut raw_diff_base| {
|
||||
LineEnding::normalize(&mut raw_diff_base);
|
||||
raw_diff_base
|
||||
})
|
||||
.map(Rope::from);
|
||||
self.diff_base = diff_base.map(|mut raw_diff_base| {
|
||||
LineEnding::normalize(&mut raw_diff_base);
|
||||
BufferDiffBase::Git(Rope::from(raw_diff_base))
|
||||
});
|
||||
self.diff_base_version += 1;
|
||||
if let Some(recalc_task) = self.git_diff_recalc(cx) {
|
||||
if let Some(recalc_task) = self.recalculate_diff(cx) {
|
||||
cx.spawn(|buffer, mut cx| async move {
|
||||
recalc_task.await;
|
||||
buffer
|
||||
|
@ -992,14 +1050,21 @@ impl Buffer {
|
|||
self.diff_base_version
|
||||
}
|
||||
|
||||
/// Recomputes the Git diff status.
|
||||
pub fn git_diff_recalc(&mut self, cx: &mut ModelContext<Self>) -> Option<Task<()>> {
|
||||
let diff_base = self.diff_base.clone()?;
|
||||
/// Recomputes the diff.
|
||||
pub fn recalculate_diff(&mut self, cx: &mut ModelContext<Self>) -> Option<Task<()>> {
|
||||
let diff_base_rope = match self.diff_base.as_mut()? {
|
||||
BufferDiffBase::Git(rope) => rope.clone(),
|
||||
BufferDiffBase::PastBufferVersion(base_buffer, base_buffer_snapshot) => {
|
||||
let new_base_snapshot = base_buffer.read(cx).snapshot();
|
||||
*base_buffer_snapshot = new_base_snapshot;
|
||||
base_buffer_snapshot.as_rope().clone()
|
||||
}
|
||||
};
|
||||
let snapshot = self.snapshot();
|
||||
|
||||
let mut diff = self.git_diff.clone();
|
||||
let diff = cx.background_executor().spawn(async move {
|
||||
diff.update(&diff_base, &snapshot).await;
|
||||
diff.update(&diff_base_rope, &snapshot).await;
|
||||
diff
|
||||
});
|
||||
|
||||
|
@ -1169,7 +1234,7 @@ impl Buffer {
|
|||
lamport_timestamp,
|
||||
};
|
||||
self.apply_diagnostic_update(server_id, diagnostics, lamport_timestamp, cx);
|
||||
self.send_operation(op, cx);
|
||||
self.send_operation(op, true, cx);
|
||||
}
|
||||
|
||||
fn request_autoindent(&mut self, cx: &mut ModelContext<Self>) {
|
||||
|
@ -1743,6 +1808,7 @@ impl Buffer {
|
|||
lamport_timestamp,
|
||||
cursor_shape,
|
||||
},
|
||||
true,
|
||||
cx,
|
||||
);
|
||||
self.non_text_state_update_count += 1;
|
||||
|
@ -1889,7 +1955,7 @@ impl Buffer {
|
|||
}
|
||||
|
||||
self.end_transaction(cx);
|
||||
self.send_operation(Operation::Buffer(edit_operation), cx);
|
||||
self.send_operation(Operation::Buffer(edit_operation), true, cx);
|
||||
Some(edit_id)
|
||||
}
|
||||
|
||||
|
@ -1991,6 +2057,9 @@ impl Buffer {
|
|||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
for operation in buffer_ops.iter() {
|
||||
self.send_operation(Operation::Buffer(operation.clone()), false, cx);
|
||||
}
|
||||
self.text.apply_ops(buffer_ops);
|
||||
self.deferred_ops.insert(deferred_ops);
|
||||
self.flush_deferred_ops(cx);
|
||||
|
@ -2114,8 +2183,16 @@ impl Buffer {
|
|||
}
|
||||
}
|
||||
|
||||
fn send_operation(&mut self, operation: Operation, cx: &mut ModelContext<Self>) {
|
||||
cx.emit(BufferEvent::Operation(operation));
|
||||
fn send_operation(
|
||||
&mut self,
|
||||
operation: Operation,
|
||||
is_local: bool,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
cx.emit(BufferEvent::Operation {
|
||||
operation,
|
||||
is_local,
|
||||
});
|
||||
}
|
||||
|
||||
/// Removes the selections for a given peer.
|
||||
|
@ -2130,7 +2207,7 @@ impl Buffer {
|
|||
let old_version = self.version.clone();
|
||||
|
||||
if let Some((transaction_id, operation)) = self.text.undo() {
|
||||
self.send_operation(Operation::Buffer(operation), cx);
|
||||
self.send_operation(Operation::Buffer(operation), true, cx);
|
||||
self.did_edit(&old_version, was_dirty, cx);
|
||||
Some(transaction_id)
|
||||
} else {
|
||||
|
@ -2147,7 +2224,7 @@ impl Buffer {
|
|||
let was_dirty = self.is_dirty();
|
||||
let old_version = self.version.clone();
|
||||
if let Some(operation) = self.text.undo_transaction(transaction_id) {
|
||||
self.send_operation(Operation::Buffer(operation), cx);
|
||||
self.send_operation(Operation::Buffer(operation), true, cx);
|
||||
self.did_edit(&old_version, was_dirty, cx);
|
||||
true
|
||||
} else {
|
||||
|
@ -2167,7 +2244,7 @@ impl Buffer {
|
|||
let operations = self.text.undo_to_transaction(transaction_id);
|
||||
let undone = !operations.is_empty();
|
||||
for operation in operations {
|
||||
self.send_operation(Operation::Buffer(operation), cx);
|
||||
self.send_operation(Operation::Buffer(operation), true, cx);
|
||||
}
|
||||
if undone {
|
||||
self.did_edit(&old_version, was_dirty, cx)
|
||||
|
@ -2181,7 +2258,7 @@ impl Buffer {
|
|||
let old_version = self.version.clone();
|
||||
|
||||
if let Some((transaction_id, operation)) = self.text.redo() {
|
||||
self.send_operation(Operation::Buffer(operation), cx);
|
||||
self.send_operation(Operation::Buffer(operation), true, cx);
|
||||
self.did_edit(&old_version, was_dirty, cx);
|
||||
Some(transaction_id)
|
||||
} else {
|
||||
|
@ -2201,7 +2278,7 @@ impl Buffer {
|
|||
let operations = self.text.redo_to_transaction(transaction_id);
|
||||
let redone = !operations.is_empty();
|
||||
for operation in operations {
|
||||
self.send_operation(Operation::Buffer(operation), cx);
|
||||
self.send_operation(Operation::Buffer(operation), true, cx);
|
||||
}
|
||||
if redone {
|
||||
self.did_edit(&old_version, was_dirty, cx)
|
||||
|
@ -2218,6 +2295,7 @@ impl Buffer {
|
|||
triggers,
|
||||
lamport_timestamp: self.completion_triggers_timestamp,
|
||||
},
|
||||
true,
|
||||
cx,
|
||||
);
|
||||
cx.notify();
|
||||
|
@ -2297,7 +2375,7 @@ impl Buffer {
|
|||
let ops = self.text.randomly_undo_redo(rng);
|
||||
if !ops.is_empty() {
|
||||
for op in ops {
|
||||
self.send_operation(Operation::Buffer(op), cx);
|
||||
self.send_operation(Operation::Buffer(op), true, cx);
|
||||
self.did_edit(&old_version, was_dirty, cx);
|
||||
}
|
||||
}
|
||||
|
@ -3638,12 +3716,12 @@ impl BufferSnapshot {
|
|||
!self.git_diff.is_empty()
|
||||
}
|
||||
|
||||
/// Returns all the Git diff hunks intersecting the given
|
||||
/// row range.
|
||||
/// Returns all the Git diff hunks intersecting the given row range.
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn git_diff_hunks_in_row_range(
|
||||
&self,
|
||||
range: Range<BufferRow>,
|
||||
) -> impl '_ + Iterator<Item = git::diff::DiffHunk<u32>> {
|
||||
) -> impl '_ + Iterator<Item = git::diff::DiffHunk> {
|
||||
self.git_diff.hunks_in_row_range(range, self)
|
||||
}
|
||||
|
||||
|
@ -3652,7 +3730,7 @@ impl BufferSnapshot {
|
|||
pub fn git_diff_hunks_intersecting_range(
|
||||
&self,
|
||||
range: Range<Anchor>,
|
||||
) -> impl '_ + Iterator<Item = git::diff::DiffHunk<u32>> {
|
||||
) -> impl '_ + Iterator<Item = git::diff::DiffHunk> {
|
||||
self.git_diff.hunks_intersecting_range(range, self)
|
||||
}
|
||||
|
||||
|
@ -3661,7 +3739,7 @@ impl BufferSnapshot {
|
|||
pub fn git_diff_hunks_intersecting_range_rev(
|
||||
&self,
|
||||
range: Range<Anchor>,
|
||||
) -> impl '_ + Iterator<Item = git::diff::DiffHunk<u32>> {
|
||||
) -> impl '_ + Iterator<Item = git::diff::DiffHunk> {
|
||||
self.git_diff.hunks_intersecting_range_rev(range, self)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::Buffer;
|
|||
use clock::ReplicaId;
|
||||
use collections::BTreeMap;
|
||||
use futures::FutureExt as _;
|
||||
use git::diff::assert_hunks;
|
||||
use gpui::{AppContext, BorrowAppContext, Model};
|
||||
use gpui::{Context, TestAppContext};
|
||||
use indoc::indoc;
|
||||
|
@ -275,13 +276,19 @@ fn test_edit_events(cx: &mut gpui::AppContext) {
|
|||
|buffer, cx| {
|
||||
let buffer_1_events = buffer_1_events.clone();
|
||||
cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
|
||||
BufferEvent::Operation(op) => buffer1_ops.lock().push(op),
|
||||
BufferEvent::Operation {
|
||||
operation,
|
||||
is_local: true,
|
||||
} => buffer1_ops.lock().push(operation),
|
||||
event => buffer_1_events.lock().push(event),
|
||||
})
|
||||
.detach();
|
||||
let buffer_2_events = buffer_2_events.clone();
|
||||
cx.subscribe(&buffer2, move |_, _, event, _| {
|
||||
buffer_2_events.lock().push(event.clone())
|
||||
cx.subscribe(&buffer2, move |_, _, event, _| match event.clone() {
|
||||
BufferEvent::Operation {
|
||||
is_local: false, ..
|
||||
} => {}
|
||||
event => buffer_2_events.lock().push(event),
|
||||
})
|
||||
.detach();
|
||||
|
||||
|
@ -2370,6 +2377,118 @@ async fn test_find_matching_indent(cx: &mut TestAppContext) {
|
|||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_branch_and_merge(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| init_settings(cx, |_| {}));
|
||||
|
||||
let base_buffer = cx.new_model(|cx| Buffer::local("one\ntwo\nthree\n", cx));
|
||||
|
||||
// Create a remote replica of the base buffer.
|
||||
let base_buffer_replica = cx.new_model(|cx| {
|
||||
Buffer::from_proto(
|
||||
1,
|
||||
Capability::ReadWrite,
|
||||
base_buffer.read(cx).to_proto(cx),
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
base_buffer.update(cx, |_buffer, cx| {
|
||||
cx.subscribe(&base_buffer_replica, |this, _, event, cx| {
|
||||
if let BufferEvent::Operation {
|
||||
operation,
|
||||
is_local: true,
|
||||
} = event
|
||||
{
|
||||
this.apply_ops([operation.clone()], cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
|
||||
// Create a branch, which initially has the same state as the base buffer.
|
||||
let branch_buffer = base_buffer.update(cx, |buffer, cx| buffer.branch(cx));
|
||||
branch_buffer.read_with(cx, |buffer, _| {
|
||||
assert_eq!(buffer.text(), "one\ntwo\nthree\n");
|
||||
});
|
||||
|
||||
// Edits to the branch are not applied to the base.
|
||||
branch_buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
[(Point::new(1, 0)..Point::new(1, 0), "ONE_POINT_FIVE\n")],
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
branch_buffer.read_with(cx, |branch_buffer, cx| {
|
||||
assert_eq!(base_buffer.read(cx).text(), "one\ntwo\nthree\n");
|
||||
assert_eq!(branch_buffer.text(), "one\nONE_POINT_FIVE\ntwo\nthree\n");
|
||||
});
|
||||
|
||||
// Edits to the base are applied to the branch.
|
||||
base_buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit([(Point::new(0, 0)..Point::new(0, 0), "ZERO\n")], None, cx)
|
||||
});
|
||||
branch_buffer.read_with(cx, |branch_buffer, cx| {
|
||||
assert_eq!(base_buffer.read(cx).text(), "ZERO\none\ntwo\nthree\n");
|
||||
assert_eq!(
|
||||
branch_buffer.text(),
|
||||
"ZERO\none\nONE_POINT_FIVE\ntwo\nthree\n"
|
||||
);
|
||||
});
|
||||
|
||||
assert_diff_hunks(&branch_buffer, cx, &[(2..3, "", "ONE_POINT_FIVE\n")]);
|
||||
|
||||
// Edits to any replica of the base are applied to the branch.
|
||||
base_buffer_replica.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
[(Point::new(2, 0)..Point::new(2, 0), "TWO_POINT_FIVE\n")],
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
branch_buffer.read_with(cx, |branch_buffer, cx| {
|
||||
assert_eq!(
|
||||
base_buffer.read(cx).text(),
|
||||
"ZERO\none\ntwo\nTWO_POINT_FIVE\nthree\n"
|
||||
);
|
||||
assert_eq!(
|
||||
branch_buffer.text(),
|
||||
"ZERO\none\nONE_POINT_FIVE\ntwo\nTWO_POINT_FIVE\nthree\n"
|
||||
);
|
||||
});
|
||||
|
||||
// Merging the branch applies all of its changes to the base.
|
||||
base_buffer.update(cx, |base_buffer, cx| {
|
||||
base_buffer.merge(&branch_buffer, cx);
|
||||
assert_eq!(
|
||||
base_buffer.text(),
|
||||
"ZERO\none\nONE_POINT_FIVE\ntwo\nTWO_POINT_FIVE\nthree\n"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn assert_diff_hunks(
|
||||
buffer: &Model<Buffer>,
|
||||
cx: &mut TestAppContext,
|
||||
expected_hunks: &[(Range<u32>, &str, &str)],
|
||||
) {
|
||||
buffer
|
||||
.update(cx, |buffer, cx| buffer.recalculate_diff(cx).unwrap())
|
||||
.detach();
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
buffer.read_with(cx, |buffer, _| {
|
||||
let snapshot = buffer.snapshot();
|
||||
assert_hunks(
|
||||
snapshot.git_diff_hunks_intersecting_range(Anchor::MIN..Anchor::MAX),
|
||||
&snapshot,
|
||||
&buffer.diff_base().unwrap().to_string(),
|
||||
expected_hunks,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 100)]
|
||||
fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
|
||||
let min_peers = env::var("MIN_PEERS")
|
||||
|
@ -2407,10 +2526,15 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
|
|||
buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
|
||||
let network = network.clone();
|
||||
cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
|
||||
if let BufferEvent::Operation(op) = event {
|
||||
network
|
||||
.lock()
|
||||
.broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
|
||||
if let BufferEvent::Operation {
|
||||
operation,
|
||||
is_local: true,
|
||||
} = event
|
||||
{
|
||||
network.lock().broadcast(
|
||||
buffer.replica_id(),
|
||||
vec![proto::serialize_operation(operation)],
|
||||
);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
@ -2533,10 +2657,14 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
|
|||
new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
|
||||
let network = network.clone();
|
||||
cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
|
||||
if let BufferEvent::Operation(op) = event {
|
||||
if let BufferEvent::Operation {
|
||||
operation,
|
||||
is_local: true,
|
||||
} = event
|
||||
{
|
||||
network.lock().broadcast(
|
||||
buffer.replica_id(),
|
||||
vec![proto::serialize_operation(op)],
|
||||
vec![proto::serialize_operation(operation)],
|
||||
);
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue