Make Buffer::apply_ops infallible (#18089)

This PR makes the `Buffer::apply_ops` method infallible for
`text::Buffer` and `language::Buffer`.

We discovered that `text::Buffer::apply_ops` was only fallible due to
`apply_undo`, which didn't actually need to be fallible.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2024-09-19 13:14:15 -04:00 committed by GitHub
parent 8074fba76b
commit 1fc391f696
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 85 additions and 108 deletions

View file

@ -683,7 +683,7 @@ impl Context {
buffer.set_text(saved_context.text.as_str(), cx) buffer.set_text(saved_context.text.as_str(), cx)
}); });
let operations = saved_context.into_ops(&this.buffer, cx); let operations = saved_context.into_ops(&this.buffer, cx);
this.apply_ops(operations, cx).unwrap(); this.apply_ops(operations, cx);
this this
} }
@ -756,7 +756,7 @@ impl Context {
&mut self, &mut self,
ops: impl IntoIterator<Item = ContextOperation>, ops: impl IntoIterator<Item = ContextOperation>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Result<()> { ) {
let mut buffer_ops = Vec::new(); let mut buffer_ops = Vec::new();
for op in ops { for op in ops {
match op { match op {
@ -765,10 +765,8 @@ impl Context {
} }
} }
self.buffer self.buffer
.update(cx, |buffer, cx| buffer.apply_ops(buffer_ops, cx))?; .update(cx, |buffer, cx| buffer.apply_ops(buffer_ops, cx));
self.flush_ops(cx); self.flush_ops(cx);
Ok(())
} }
fn flush_ops(&mut self, cx: &mut ModelContext<Context>) { fn flush_ops(&mut self, cx: &mut ModelContext<Context>) {

View file

@ -1166,9 +1166,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
); );
network.lock().broadcast(replica_id, ops_to_send); network.lock().broadcast(replica_id, ops_to_send);
context context.update(cx, |context, cx| context.apply_ops(ops_to_receive, cx));
.update(cx, |context, cx| context.apply_ops(ops_to_receive, cx))
.unwrap();
} else if rng.gen_bool(0.1) && replica_id != 0 { } else if rng.gen_bool(0.1) && replica_id != 0 {
log::info!("Context {}: disconnecting", context_index); log::info!("Context {}: disconnecting", context_index);
network.lock().disconnect_peer(replica_id); network.lock().disconnect_peer(replica_id);
@ -1180,9 +1178,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
.map(ContextOperation::from_proto) .map(ContextOperation::from_proto)
.collect::<Result<Vec<_>>>() .collect::<Result<Vec<_>>>()
.unwrap(); .unwrap();
context context.update(cx, |context, cx| context.apply_ops(ops, cx));
.update(cx, |context, cx| context.apply_ops(ops, cx))
.unwrap();
} }
} }
} }

View file

@ -223,7 +223,7 @@ impl ContextStore {
if let Some(context) = this.loaded_context_for_id(&context_id, cx) { if let Some(context) = this.loaded_context_for_id(&context_id, cx) {
let operation_proto = envelope.payload.operation.context("invalid operation")?; let operation_proto = envelope.payload.operation.context("invalid operation")?;
let operation = ContextOperation::from_proto(operation_proto)?; let operation = ContextOperation::from_proto(operation_proto)?;
context.update(cx, |context, cx| context.apply_ops([operation], cx))?; context.update(cx, |context, cx| context.apply_ops([operation], cx));
} }
Ok(()) Ok(())
})? })?
@ -394,7 +394,7 @@ impl ContextStore {
.collect::<Result<Vec<_>>>() .collect::<Result<Vec<_>>>()
}) })
.await?; .await?;
context.update(&mut cx, |context, cx| context.apply_ops(operations, cx))??; context.update(&mut cx, |context, cx| context.apply_ops(operations, cx))?;
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) { if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) {
existing_context existing_context
@ -531,7 +531,7 @@ impl ContextStore {
.collect::<Result<Vec<_>>>() .collect::<Result<Vec<_>>>()
}) })
.await?; .await?;
context.update(&mut cx, |context, cx| context.apply_ops(operations, cx))??; context.update(&mut cx, |context, cx| context.apply_ops(operations, cx))?;
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) { if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) {
existing_context existing_context

View file

@ -66,7 +66,7 @@ impl ChannelBuffer {
let capability = channel_store.read(cx).channel_capability(channel.id); let capability = channel_store.read(cx).channel_capability(channel.id);
language::Buffer::remote(buffer_id, response.replica_id as u16, capability, base_text) language::Buffer::remote(buffer_id, response.replica_id as u16, capability, base_text)
})?; })?;
buffer.update(&mut cx, |buffer, cx| buffer.apply_ops(operations, cx))??; buffer.update(&mut cx, |buffer, cx| buffer.apply_ops(operations, cx))?;
let subscription = client.subscribe_to_entity(channel.id.0)?; let subscription = client.subscribe_to_entity(channel.id.0)?;
@ -151,7 +151,7 @@ impl ChannelBuffer {
cx.notify(); cx.notify();
this.buffer this.buffer
.update(cx, |buffer, cx| buffer.apply_ops(ops, cx)) .update(cx, |buffer, cx| buffer.apply_ops(ops, cx))
})??; })?;
Ok(()) Ok(())
} }

View file

@ -1007,7 +1007,7 @@ impl ChannelStore {
.into_iter() .into_iter()
.map(language::proto::deserialize_operation) .map(language::proto::deserialize_operation)
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
buffer.apply_ops(incoming_operations, cx)?; buffer.apply_ops(incoming_operations, cx);
anyhow::Ok(outgoing_operations) anyhow::Ok(outgoing_operations)
}) })
.log_err(); .log_err();

View file

@ -689,9 +689,7 @@ impl Database {
} }
let mut text_buffer = text::Buffer::new(0, text::BufferId::new(1).unwrap(), base_text); let mut text_buffer = text::Buffer::new(0, text::BufferId::new(1).unwrap(), base_text);
text_buffer text_buffer.apply_ops(operations.into_iter().filter_map(operation_from_wire));
.apply_ops(operations.into_iter().filter_map(operation_from_wire))
.unwrap();
let base_text = text_buffer.text(); let base_text = text_buffer.text();
let epoch = buffer.epoch + 1; let epoch = buffer.epoch + 1;

View file

@ -96,16 +96,14 @@ async fn test_channel_buffers(db: &Arc<Database>) {
text::BufferId::new(1).unwrap(), text::BufferId::new(1).unwrap(),
buffer_response_b.base_text, buffer_response_b.base_text,
); );
buffer_b buffer_b.apply_ops(buffer_response_b.operations.into_iter().map(|operation| {
.apply_ops(buffer_response_b.operations.into_iter().map(|operation| { let operation = proto::deserialize_operation(operation).unwrap();
let operation = proto::deserialize_operation(operation).unwrap(); if let language::Operation::Buffer(operation) = operation {
if let language::Operation::Buffer(operation) = operation { operation
operation } else {
} else { unreachable!()
unreachable!() }
} }));
}))
.unwrap();
assert_eq!(buffer_b.text(), "hello, cruel world"); assert_eq!(buffer_b.text(), "hello, cruel world");

View file

@ -1972,7 +1972,7 @@ impl Buffer {
&mut self, &mut self,
ops: I, ops: I,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Result<()> { ) {
self.pending_autoindent.take(); self.pending_autoindent.take();
let was_dirty = self.is_dirty(); let was_dirty = self.is_dirty();
let old_version = self.version.clone(); let old_version = self.version.clone();
@ -1991,14 +1991,13 @@ impl Buffer {
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
self.text.apply_ops(buffer_ops)?; self.text.apply_ops(buffer_ops);
self.deferred_ops.insert(deferred_ops); self.deferred_ops.insert(deferred_ops);
self.flush_deferred_ops(cx); self.flush_deferred_ops(cx);
self.did_edit(&old_version, was_dirty, cx); self.did_edit(&old_version, was_dirty, cx);
// Notify independently of whether the buffer was edited as the operations could include a // Notify independently of whether the buffer was edited as the operations could include a
// selection update. // selection update.
cx.notify(); cx.notify();
Ok(())
} }
fn flush_deferred_ops(&mut self, cx: &mut ModelContext<Self>) { fn flush_deferred_ops(&mut self, cx: &mut ModelContext<Self>) {

View file

@ -308,7 +308,7 @@ fn test_edit_events(cx: &mut gpui::AppContext) {
// 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 dirty changed event. // followed by a dirty changed event.
buffer2.update(cx, |buffer, cx| { buffer2.update(cx, |buffer, cx| {
buffer.apply_ops(buffer1_ops.lock().drain(..), cx).unwrap(); buffer.apply_ops(buffer1_ops.lock().drain(..), cx);
}); });
assert_eq!( assert_eq!(
mem::take(&mut *buffer_1_events.lock()), mem::take(&mut *buffer_1_events.lock()),
@ -332,7 +332,7 @@ fn test_edit_events(cx: &mut gpui::AppContext) {
// Incorporating the remote ops again emits a single edited event, // Incorporating the remote ops again emits a single edited event,
// followed by a dirty changed event. // followed by a dirty changed event.
buffer2.update(cx, |buffer, cx| { buffer2.update(cx, |buffer, cx| {
buffer.apply_ops(buffer1_ops.lock().drain(..), cx).unwrap(); buffer.apply_ops(buffer1_ops.lock().drain(..), cx);
}); });
assert_eq!( assert_eq!(
mem::take(&mut *buffer_1_events.lock()), mem::take(&mut *buffer_1_events.lock()),
@ -2274,13 +2274,11 @@ fn test_serialization(cx: &mut gpui::AppContext) {
.block(buffer1.read(cx).serialize_ops(None, cx)); .block(buffer1.read(cx).serialize_ops(None, cx));
let buffer2 = cx.new_model(|cx| { let buffer2 = cx.new_model(|cx| {
let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap(); let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
buffer buffer.apply_ops(
.apply_ops( ops.into_iter()
ops.into_iter() .map(|op| proto::deserialize_operation(op).unwrap()),
.map(|op| proto::deserialize_operation(op).unwrap()), cx,
cx, );
)
.unwrap();
buffer buffer
}); });
assert_eq!(buffer2.read(cx).text(), "abcDF"); assert_eq!(buffer2.read(cx).text(), "abcDF");
@ -2401,13 +2399,11 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
.block(base_buffer.read(cx).serialize_ops(None, cx)); .block(base_buffer.read(cx).serialize_ops(None, cx));
let mut buffer = let mut buffer =
Buffer::from_proto(i as ReplicaId, Capability::ReadWrite, state, None).unwrap(); Buffer::from_proto(i as ReplicaId, Capability::ReadWrite, state, None).unwrap();
buffer buffer.apply_ops(
.apply_ops( ops.into_iter()
ops.into_iter() .map(|op| proto::deserialize_operation(op).unwrap()),
.map(|op| proto::deserialize_operation(op).unwrap()), cx,
cx, );
)
.unwrap();
buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200))); buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
let network = network.clone(); let network = network.clone();
cx.subscribe(&cx.handle(), move |buffer, _, event, _| { cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
@ -2523,14 +2519,12 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
None, None,
) )
.unwrap(); .unwrap();
new_buffer new_buffer.apply_ops(
.apply_ops( old_buffer_ops
old_buffer_ops .into_iter()
.into_iter() .map(|op| deserialize_operation(op).unwrap()),
.map(|op| deserialize_operation(op).unwrap()), cx,
cx, );
)
.unwrap();
log::info!( log::info!(
"New replica {} text: {:?}", "New replica {} text: {:?}",
new_buffer.replica_id(), new_buffer.replica_id(),
@ -2570,7 +2564,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
ops ops
); );
new_buffer.update(cx, |new_buffer, cx| { new_buffer.update(cx, |new_buffer, cx| {
new_buffer.apply_ops(ops, cx).unwrap(); new_buffer.apply_ops(ops, cx);
}); });
} }
} }
@ -2598,7 +2592,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
ops.len(), ops.len(),
ops ops
); );
buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap()); buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx));
} }
} }
_ => {} _ => {}

View file

@ -5019,13 +5019,11 @@ mod tests {
.background_executor() .background_executor()
.block(host_buffer.read(cx).serialize_ops(None, cx)); .block(host_buffer.read(cx).serialize_ops(None, cx));
let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap(); let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
buffer buffer.apply_ops(
.apply_ops( ops.into_iter()
ops.into_iter() .map(|op| language::proto::deserialize_operation(op).unwrap()),
.map(|op| language::proto::deserialize_operation(op).unwrap()), cx,
cx, );
)
.unwrap();
buffer buffer
}); });
let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx)); let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx));

View file

@ -644,7 +644,7 @@ impl BufferStore {
} }
hash_map::Entry::Occupied(mut entry) => { hash_map::Entry::Occupied(mut entry) => {
if let OpenBuffer::Operations(operations) = entry.get_mut() { if let OpenBuffer::Operations(operations) = entry.get_mut() {
buffer.update(cx, |b, cx| b.apply_ops(operations.drain(..), cx))?; buffer.update(cx, |b, cx| b.apply_ops(operations.drain(..), cx));
} else if entry.get().upgrade().is_some() { } else if entry.get().upgrade().is_some() {
if is_remote { if is_remote {
return Ok(()); return Ok(());
@ -1051,12 +1051,12 @@ impl BufferStore {
match this.opened_buffers.entry(buffer_id) { match this.opened_buffers.entry(buffer_id) {
hash_map::Entry::Occupied(mut e) => match e.get_mut() { hash_map::Entry::Occupied(mut e) => match e.get_mut() {
OpenBuffer::Strong(buffer) => { OpenBuffer::Strong(buffer) => {
buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?; buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx));
} }
OpenBuffer::Operations(operations) => operations.extend_from_slice(&ops), OpenBuffer::Operations(operations) => operations.extend_from_slice(&ops),
OpenBuffer::Weak(buffer) => { OpenBuffer::Weak(buffer) => {
if let Some(buffer) = buffer.upgrade() { if let Some(buffer) = buffer.upgrade() {
buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?; buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx));
} }
} }
}, },
@ -1217,7 +1217,8 @@ impl BufferStore {
.into_iter() .into_iter()
.map(language::proto::deserialize_operation) .map(language::proto::deserialize_operation)
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx)) buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx));
anyhow::Ok(())
}); });
if let Err(error) = result { if let Err(error) = result {

View file

@ -515,25 +515,25 @@ fn test_undo_redo() {
let entries = buffer.history.undo_stack.clone(); let entries = buffer.history.undo_stack.clone();
assert_eq!(entries.len(), 3); assert_eq!(entries.len(), 3);
buffer.undo_or_redo(entries[0].transaction.clone()).unwrap(); buffer.undo_or_redo(entries[0].transaction.clone());
assert_eq!(buffer.text(), "1cdef234"); assert_eq!(buffer.text(), "1cdef234");
buffer.undo_or_redo(entries[0].transaction.clone()).unwrap(); buffer.undo_or_redo(entries[0].transaction.clone());
assert_eq!(buffer.text(), "1abcdef234"); assert_eq!(buffer.text(), "1abcdef234");
buffer.undo_or_redo(entries[1].transaction.clone()).unwrap(); buffer.undo_or_redo(entries[1].transaction.clone());
assert_eq!(buffer.text(), "1abcdx234"); assert_eq!(buffer.text(), "1abcdx234");
buffer.undo_or_redo(entries[2].transaction.clone()).unwrap(); buffer.undo_or_redo(entries[2].transaction.clone());
assert_eq!(buffer.text(), "1abx234"); assert_eq!(buffer.text(), "1abx234");
buffer.undo_or_redo(entries[1].transaction.clone()).unwrap(); buffer.undo_or_redo(entries[1].transaction.clone());
assert_eq!(buffer.text(), "1abyzef234"); assert_eq!(buffer.text(), "1abyzef234");
buffer.undo_or_redo(entries[2].transaction.clone()).unwrap(); buffer.undo_or_redo(entries[2].transaction.clone());
assert_eq!(buffer.text(), "1abcdef234"); assert_eq!(buffer.text(), "1abcdef234");
buffer.undo_or_redo(entries[2].transaction.clone()).unwrap(); buffer.undo_or_redo(entries[2].transaction.clone());
assert_eq!(buffer.text(), "1abyzef234"); assert_eq!(buffer.text(), "1abyzef234");
buffer.undo_or_redo(entries[0].transaction.clone()).unwrap(); buffer.undo_or_redo(entries[0].transaction.clone());
assert_eq!(buffer.text(), "1yzef234"); assert_eq!(buffer.text(), "1yzef234");
buffer.undo_or_redo(entries[1].transaction.clone()).unwrap(); buffer.undo_or_redo(entries[1].transaction.clone());
assert_eq!(buffer.text(), "1234"); assert_eq!(buffer.text(), "1234");
} }
@ -692,12 +692,12 @@ fn test_concurrent_edits() {
let buf3_op = buffer3.edit([(5..6, "56")]); let buf3_op = buffer3.edit([(5..6, "56")]);
assert_eq!(buffer3.text(), "abcde56"); assert_eq!(buffer3.text(), "abcde56");
buffer1.apply_op(buf2_op.clone()).unwrap(); buffer1.apply_op(buf2_op.clone());
buffer1.apply_op(buf3_op.clone()).unwrap(); buffer1.apply_op(buf3_op.clone());
buffer2.apply_op(buf1_op.clone()).unwrap(); buffer2.apply_op(buf1_op.clone());
buffer2.apply_op(buf3_op).unwrap(); buffer2.apply_op(buf3_op);
buffer3.apply_op(buf1_op).unwrap(); buffer3.apply_op(buf1_op);
buffer3.apply_op(buf2_op).unwrap(); buffer3.apply_op(buf2_op);
assert_eq!(buffer1.text(), "a12c34e56"); assert_eq!(buffer1.text(), "a12c34e56");
assert_eq!(buffer2.text(), "a12c34e56"); assert_eq!(buffer2.text(), "a12c34e56");
@ -756,7 +756,7 @@ fn test_random_concurrent_edits(mut rng: StdRng) {
replica_id, replica_id,
ops.len() ops.len()
); );
buffer.apply_ops(ops).unwrap(); buffer.apply_ops(ops);
} }
} }
_ => {} _ => {}

View file

@ -38,7 +38,6 @@ pub use subscription::*;
pub use sum_tree::Bias; pub use sum_tree::Bias;
use sum_tree::{FilterCursor, SumTree, TreeMap}; use sum_tree::{FilterCursor, SumTree, TreeMap};
use undo_map::UndoMap; use undo_map::UndoMap;
use util::ResultExt;
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
use util::RandomCharIter; use util::RandomCharIter;
@ -927,23 +926,22 @@ impl Buffer {
self.snapshot.line_ending = line_ending; self.snapshot.line_ending = line_ending;
} }
pub fn apply_ops<I: IntoIterator<Item = Operation>>(&mut self, ops: I) -> Result<()> { pub fn apply_ops<I: IntoIterator<Item = Operation>>(&mut self, ops: I) {
let mut deferred_ops = Vec::new(); let mut deferred_ops = Vec::new();
for op in ops { for op in ops {
self.history.push(op.clone()); self.history.push(op.clone());
if self.can_apply_op(&op) { if self.can_apply_op(&op) {
self.apply_op(op)?; self.apply_op(op);
} else { } else {
self.deferred_replicas.insert(op.replica_id()); self.deferred_replicas.insert(op.replica_id());
deferred_ops.push(op); deferred_ops.push(op);
} }
} }
self.deferred_ops.insert(deferred_ops); self.deferred_ops.insert(deferred_ops);
self.flush_deferred_ops()?; self.flush_deferred_ops();
Ok(())
} }
fn apply_op(&mut self, op: Operation) -> Result<()> { fn apply_op(&mut self, op: Operation) {
match op { match op {
Operation::Edit(edit) => { Operation::Edit(edit) => {
if !self.version.observed(edit.timestamp) { if !self.version.observed(edit.timestamp) {
@ -960,7 +958,7 @@ impl Buffer {
} }
Operation::Undo(undo) => { Operation::Undo(undo) => {
if !self.version.observed(undo.timestamp) { if !self.version.observed(undo.timestamp) {
self.apply_undo(&undo)?; self.apply_undo(&undo);
self.snapshot.version.observe(undo.timestamp); self.snapshot.version.observe(undo.timestamp);
self.lamport_clock.observe(undo.timestamp); self.lamport_clock.observe(undo.timestamp);
} }
@ -974,7 +972,6 @@ impl Buffer {
true true
} }
}); });
Ok(())
} }
fn apply_remote_edit( fn apply_remote_edit(
@ -1217,7 +1214,7 @@ impl Buffer {
fragment_ids fragment_ids
} }
fn apply_undo(&mut self, undo: &UndoOperation) -> Result<()> { fn apply_undo(&mut self, undo: &UndoOperation) {
self.snapshot.undo_map.insert(undo); self.snapshot.undo_map.insert(undo);
let mut edits = Patch::default(); let mut edits = Patch::default();
@ -1268,22 +1265,20 @@ impl Buffer {
self.snapshot.visible_text = visible_text; self.snapshot.visible_text = visible_text;
self.snapshot.deleted_text = deleted_text; self.snapshot.deleted_text = deleted_text;
self.subscriptions.publish_mut(&edits); self.subscriptions.publish_mut(&edits);
Ok(())
} }
fn flush_deferred_ops(&mut self) -> Result<()> { fn flush_deferred_ops(&mut self) {
self.deferred_replicas.clear(); self.deferred_replicas.clear();
let mut deferred_ops = Vec::new(); let mut deferred_ops = Vec::new();
for op in self.deferred_ops.drain().iter().cloned() { for op in self.deferred_ops.drain().iter().cloned() {
if self.can_apply_op(&op) { if self.can_apply_op(&op) {
self.apply_op(op)?; self.apply_op(op);
} else { } else {
self.deferred_replicas.insert(op.replica_id()); self.deferred_replicas.insert(op.replica_id());
deferred_ops.push(op); deferred_ops.push(op);
} }
} }
self.deferred_ops.insert(deferred_ops); self.deferred_ops.insert(deferred_ops);
Ok(())
} }
fn can_apply_op(&self, op: &Operation) -> bool { fn can_apply_op(&self, op: &Operation) -> bool {
@ -1352,7 +1347,7 @@ impl Buffer {
if let Some(entry) = self.history.pop_undo() { if let Some(entry) = self.history.pop_undo() {
let transaction = entry.transaction.clone(); let transaction = entry.transaction.clone();
let transaction_id = transaction.id; let transaction_id = transaction.id;
let op = self.undo_or_redo(transaction).unwrap(); let op = self.undo_or_redo(transaction);
Some((transaction_id, op)) Some((transaction_id, op))
} else { } else {
None None
@ -1365,7 +1360,7 @@ impl Buffer {
.remove_from_undo(transaction_id)? .remove_from_undo(transaction_id)?
.transaction .transaction
.clone(); .clone();
self.undo_or_redo(transaction).log_err() Some(self.undo_or_redo(transaction))
} }
pub fn undo_to_transaction(&mut self, transaction_id: TransactionId) -> Vec<Operation> { pub fn undo_to_transaction(&mut self, transaction_id: TransactionId) -> Vec<Operation> {
@ -1378,7 +1373,7 @@ impl Buffer {
transactions transactions
.into_iter() .into_iter()
.map(|transaction| self.undo_or_redo(transaction).unwrap()) .map(|transaction| self.undo_or_redo(transaction))
.collect() .collect()
} }
@ -1394,7 +1389,7 @@ impl Buffer {
if let Some(entry) = self.history.pop_redo() { if let Some(entry) = self.history.pop_redo() {
let transaction = entry.transaction.clone(); let transaction = entry.transaction.clone();
let transaction_id = transaction.id; let transaction_id = transaction.id;
let op = self.undo_or_redo(transaction).unwrap(); let op = self.undo_or_redo(transaction);
Some((transaction_id, op)) Some((transaction_id, op))
} else { } else {
None None
@ -1411,11 +1406,11 @@ impl Buffer {
transactions transactions
.into_iter() .into_iter()
.map(|transaction| self.undo_or_redo(transaction).unwrap()) .map(|transaction| self.undo_or_redo(transaction))
.collect() .collect()
} }
fn undo_or_redo(&mut self, transaction: Transaction) -> Result<Operation> { fn undo_or_redo(&mut self, transaction: Transaction) -> Operation {
let mut counts = HashMap::default(); let mut counts = HashMap::default();
for edit_id in transaction.edit_ids { for edit_id in transaction.edit_ids {
counts.insert(edit_id, self.undo_map.undo_count(edit_id) + 1); counts.insert(edit_id, self.undo_map.undo_count(edit_id) + 1);
@ -1426,11 +1421,11 @@ impl Buffer {
version: self.version(), version: self.version(),
counts, counts,
}; };
self.apply_undo(&undo)?; self.apply_undo(&undo);
self.snapshot.version.observe(undo.timestamp); self.snapshot.version.observe(undo.timestamp);
let operation = Operation::Undo(undo); let operation = Operation::Undo(undo);
self.history.push(operation.clone()); self.history.push(operation.clone());
Ok(operation) operation
} }
pub fn push_transaction(&mut self, transaction: Transaction, now: Instant) { pub fn push_transaction(&mut self, transaction: Transaction, now: Instant) {
@ -1762,7 +1757,7 @@ impl Buffer {
self.replica_id, self.replica_id,
transaction transaction
); );
ops.push(self.undo_or_redo(transaction).unwrap()); ops.push(self.undo_or_redo(transaction));
} }
} }
ops ops