diff --git a/rust-lib/flowy-ot/src/client/document.rs b/rust-lib/flowy-ot/src/client/document.rs index 30b8847c7e..42fe7c89b8 100644 --- a/rust-lib/flowy-ot/src/client/document.rs +++ b/rust-lib/flowy-ot/src/client/document.rs @@ -1,5 +1,5 @@ use crate::{ - client::{History, RevId, Revision, UndoResult}, + client::{History, RevId, UndoResult}, core::{ Attribute, Attributes, @@ -21,14 +21,17 @@ pub struct Document { impl Document { pub fn new() -> Self { + let mut delta = Delta::new(); + delta.insert("\n", Attributes::Empty); + Document { - data: Delta::new(), + data: delta, history: History::new(), rev_id_counter: 1, } } - pub fn edit(&mut self, index: usize, text: &str) { + pub fn edit(&mut self, index: usize, text: &str) -> Result<(), OTError> { if self.data.target_len < index { log::error!( "{} out of bounds. should 0..{}", @@ -44,16 +47,21 @@ impl Document { let insert = OpBuilder::insert(text).attributes(attributes).build(); let interval = Interval::new(index, index); - self.update_with_op(insert, interval); + self.update_with_op(insert, interval) } - pub fn format(&mut self, interval: Interval, attribute: Attribute, enable: bool) { + pub fn format( + &mut self, + interval: Interval, + attribute: Attribute, + enable: bool, + ) -> Result<(), OTError> { let attributes = match enable { true => AttrsBuilder::new().add_attribute(attribute).build(), false => AttrsBuilder::new().remove_attribute(attribute).build(), }; - self.update_with_attribute(attributes, interval); + self.update_with_attribute(attributes, interval) } pub fn can_undo(&self) -> bool { self.history.can_undo() } @@ -89,9 +97,9 @@ impl Document { } } - pub fn delete(&mut self, interval: Interval) { + pub fn delete(&mut self, interval: Interval) -> Result<(), OTError> { let delete = OpBuilder::delete(interval.size() as u64).build(); - self.update_with_op(delete, interval); + self.update_with_op(delete, interval) } pub fn to_json(&self) -> String { self.data.to_json() } @@ -100,7 +108,7 @@ impl Document { pub fn set_data(&mut self, data: Delta) { self.data = data; } - fn update_with_op(&mut self, op: Operation, interval: Interval) { + fn update_with_op(&mut self, op: Operation, interval: Interval) -> Result<(), OTError> { let mut new_delta = Delta::default(); let (prefix, interval, suffix) = split_length_with_interval(self.data.target_len, interval); @@ -127,18 +135,20 @@ impl Document { }); } - let new_data = self.data.compose(&new_delta).unwrap(); - let undo_delta = new_data.invert_delta(&self.data); + let new_data = self.data.compose(&new_delta)?; + let undo_delta = new_delta.invert_delta(&self.data); self.rev_id_counter += 1; - if !undo_delta.is_empty() { - self.history.add_undo(undo_delta); - } - + self.history.record(undo_delta); self.data = new_data; + Ok(()) } - pub fn update_with_attribute(&mut self, mut attributes: Attributes, interval: Interval) { + pub fn update_with_attribute( + &mut self, + mut attributes: Attributes, + interval: Interval, + ) -> Result<(), OTError> { let old_attributes = self.data.get_attributes(interval); log::debug!( "merge attributes: {:?}, with old: {:?}", @@ -165,23 +175,12 @@ impl Document { interval ); - self.update_with_op(retain, interval); + self.update_with_op(retain, interval) } fn next_rev_id(&self) -> RevId { RevId(self.rev_id_counter) } } -pub fn transform(left: &mut Document, right: &mut Document) { - let (a_prime, b_prime) = left.data.transform(&right.data).unwrap(); - log::trace!("a:{:?},b:{:?}", a_prime, b_prime); - - let data_left = left.data.compose(&b_prime).unwrap(); - let data_right = right.data.compose(&a_prime).unwrap(); - - left.set_data(data_left); - right.set_data(data_right); -} - fn split_length_with_interval(length: usize, interval: Interval) -> (Interval, Interval, Interval) { let original_interval = Interval::new(0, length); let prefix = original_interval.prefix(interval); diff --git a/rust-lib/flowy-ot/src/client/history.rs b/rust-lib/flowy-ot/src/client/history.rs index f83fa15023..9b2dde278b 100644 --- a/rust-lib/flowy-ot/src/client/history.rs +++ b/rust-lib/flowy-ot/src/client/history.rs @@ -1,4 +1,4 @@ -use crate::core::{Delta, Interval, OpBuilder, Operation}; +use crate::core::Delta; const MAX_UNDOS: usize = 20; @@ -37,6 +37,7 @@ pub struct History { cur_undo: usize, undos: Vec, redos: Vec, + capacity: usize, } impl History { @@ -45,6 +46,7 @@ impl History { cur_undo: 1, undos: Vec::new(), redos: Vec::new(), + capacity: 20, } } @@ -56,6 +58,19 @@ impl History { pub fn add_redo(&mut self, delta: Delta) { self.redos.push(delta); } + pub fn record(&mut self, delta: Delta) { + if delta.ops.is_empty() { + return; + } + + self.redos.clear(); + self.add_undo(delta); + + if self.undos.len() > self.capacity { + self.undos.remove(0); + } + } + pub fn undo(&mut self) -> Option { if !self.can_undo() { return None; diff --git a/rust-lib/flowy-ot/tests/helper/mod.rs b/rust-lib/flowy-ot/tests/helper/mod.rs index 315bf8da1f..030b109551 100644 --- a/rust-lib/flowy-ot/tests/helper/mod.rs +++ b/rust-lib/flowy-ot/tests/helper/mod.rs @@ -1,8 +1,5 @@ use derive_more::Display; -use flowy_ot::{ - client::{transform, Document}, - core::*, -}; +use flowy_ot::{client::Document, core::*}; use rand::{prelude::*, Rng as WrappedRng}; use std::sync::Once; @@ -66,30 +63,41 @@ impl OpTester { match op { TestOp::Insert(delta_i, s, index) => { let document = &mut self.documents[*delta_i]; - document.edit(*index, s); + document.edit(*index, s).unwrap(); }, TestOp::Delete(delta_i, interval) => { let document = &mut self.documents[*delta_i]; - document.delete(*interval); + document.delete(*interval).unwrap(); }, TestOp::InsertBold(delta_i, s, interval) => { let document = &mut self.documents[*delta_i]; - document.edit(interval.start, s); - document.format(*interval, Attribute::Bold, true); + document.edit(interval.start, s).unwrap(); + document.format(*interval, Attribute::Bold, true).unwrap(); }, TestOp::Bold(delta_i, interval, enable) => { let document = &mut self.documents[*delta_i]; - document.format(*interval, Attribute::Bold, *enable); + document + .format(*interval, Attribute::Bold, *enable) + .unwrap(); }, TestOp::Italic(delta_i, interval, enable) => { let document = &mut self.documents[*delta_i]; - document.format(*interval, Attribute::Italic, *enable); + document + .format(*interval, Attribute::Italic, *enable) + .unwrap(); }, TestOp::Transform(delta_a_i, delta_b_i) => { - transform( - &mut self.documents[*delta_a_i], - &mut self.documents[*delta_b_i], - ); + let (a_prime, b_prime) = self.documents[*delta_a_i] + .data() + .transform(&self.documents[*delta_b_i].data()) + .unwrap(); + log::trace!("a:{:?},b:{:?}", a_prime, b_prime); + + let data_left = self.documents[*delta_a_i].data().compose(&b_prime).unwrap(); + let data_right = self.documents[*delta_b_i].data().compose(&a_prime).unwrap(); + + self.documents[*delta_a_i].set_data(data_left); + self.documents[*delta_b_i].set_data(data_right); }, TestOp::Invert(delta_a_i, delta_b_i) => { let delta_a = &self.documents[*delta_a_i].data(); @@ -114,10 +122,10 @@ impl OpTester { self.documents[*delta_a_i].set_data(new_delta_after_undo); }, TestOp::Undo(delta_i) => { - self.documents[*delta_i].undo(); + self.documents[*delta_i].undo().unwrap(); }, TestOp::Redo(delta_i) => { - self.documents[*delta_i].redo(); + self.documents[*delta_i].redo().unwrap(); }, TestOp::AssertOpsJson(delta_i, expected) => { let delta_i_json = self.documents[*delta_i].to_json(); diff --git a/rust-lib/flowy-ot/tests/undo_redo_test.rs b/rust-lib/flowy-ot/tests/undo_redo_test.rs index e69de29bb2..93785c9d94 100644 --- a/rust-lib/flowy-ot/tests/undo_redo_test.rs +++ b/rust-lib/flowy-ot/tests/undo_redo_test.rs @@ -0,0 +1,22 @@ +pub mod helper; + +use crate::helper::{TestOp::*, *}; + +#[test] +fn delta_undo_insert_text() { + let ops = vec![Insert(0, "123", 0), Undo(0), AssertOpsJson(0, r#"[]"#)]; + OpTester::new().run_script(ops); +} + +#[test] +fn delta_undo_insert_text2() { + let ops = vec![ + Insert(0, "123", 0), + Insert(0, "456", 0), + Undo(0), + AssertOpsJson(0, r#"[{"insert":"123\n"}]"#), + Undo(0), + AssertOpsJson(0, r#"[{"insert":"\n"}]"#), + ]; + OpTester::new().run_script(ops); +}