فهرست منبع

add undo test

appflowy 3 سال پیش
والد
کامیت
3d837c51f4

+ 27 - 28
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);

+ 16 - 1
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<Delta>,
     redos: Vec<Delta>,
+    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<Delta> {
         if !self.can_undo() {
             return None;

+ 24 - 16
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();

+ 22 - 0
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);
+}