Kaynağa Gözat

add redo/undo test with delete operation

appflowy 3 yıl önce
ebeveyn
işleme
251b065dd8

+ 57 - 45
rust-lib/flowy-ot/src/core/delta.rs

@@ -482,50 +482,17 @@ impl Delta {
             return inverted;
         }
 
-        let inverted_from_other =
-            |inverted: &mut Delta, operation: &Operation, start: usize, end: usize| {
-                log::debug!("invert op: {:?} [{}:{}]", operation, start, end);
-
-                let ops = other.ops_in_interval(Interval::new(start, end));
-                ops.into_iter().for_each(|other_op| {
-                    match operation {
-                        Operation::Delete(_) => {
-                            log::debug!("add: {}", other_op);
-                            inverted.add(other_op);
-                        },
-                        Operation::Retain(_) => {
-                            log::debug!(
-                                "Start invert attributes: {:?}, {:?}",
-                                operation.get_attributes(),
-                                other_op.get_attributes()
-                            );
-                            let inverted_attrs = invert_attributes(
-                                operation.get_attributes(),
-                                other_op.get_attributes(),
-                            );
-                            log::debug!("End invert attributes: {:?}", inverted_attrs);
-                            log::debug!("invert retain: {}, {}", other_op.length(), inverted_attrs);
-                            inverted.retain(other_op.length(), inverted_attrs);
-                        },
-                        Operation::Insert(_) => {
-                            // Impossible to here
-                            panic!()
-                        },
-                    }
-                });
-            };
-
         let mut index = 0;
         for op in &self.ops {
             let len: usize = op.length() as usize;
             match op {
                 Operation::Delete(n) => {
-                    inverted_from_other(&mut inverted, op, index, index + *n);
+                    invert_from_other(&mut inverted, other, op, index, index + *n);
                     index += len;
                 },
                 Operation::Retain(_) => {
                     match op.has_attribute() {
-                        true => inverted_from_other(&mut inverted, op, index, index + len),
+                        true => invert_from_other(&mut inverted, other, op, index, index + len),
                         false => {
                             log::debug!("invert retain op: {:?}", op);
                             inverted.retain(len as usize, op.get_attributes())
@@ -533,10 +500,9 @@ impl Delta {
                     }
                     index += len;
                 },
-                Operation::Insert(insert) => {
+                Operation::Insert(_) => {
                     log::debug!("invert insert op: {:?}", op);
                     inverted.delete(len as usize);
-                    // index += insert.s.len();
                 },
             }
         }
@@ -558,7 +524,6 @@ impl Delta {
 
     pub fn ops_in_interval(&self, mut interval: Interval) -> Vec<Operation> {
         log::debug!("ops in delta: {:?}, at {:?}", self, interval);
-
         let mut ops: Vec<Operation> = Vec::with_capacity(self.ops.len());
         let mut ops_iter = self.ops.iter();
         let mut maybe_next_op = ops_iter.next();
@@ -567,27 +532,39 @@ impl Delta {
         while maybe_next_op.is_some() {
             let next_op = maybe_next_op.take().unwrap();
             if offset < interval.start {
-                if next_op.length() > interval.size() {
+                let next_op_i = Interval::new(offset, offset + next_op.length());
+                let intersect = next_op_i.intersect(interval);
+                if !intersect.is_empty() {
                     // if the op's length larger than the interval size, just shrink the op to that
-                    // interval. for example: delta: "123456", interval: [3,6).
+                    // interval Checkout the delta_get_ops_in_interval_3 test for more details.
                     // ┌──────────────┐
                     // │ 1 2 3 4 5 6  │
                     // └───────▲───▲──┘
                     //         │   │
-                    //        [3, 6)
-                    if let Some(new_op) = next_op.shrink(interval) {
+                    //        [3, 5)
+                    // op = "45"
+                    if let Some(new_op) = next_op.shrink(intersect) {
+                        offset += min(intersect.end, next_op.length());
+                        interval = Interval::new(offset, interval.end);
                         ops.push(new_op);
                     }
                 } else {
-                    // adding the op's length to offset until the offset is contained in the
-                    // interval
-                    offset += next_op.length();
+                    offset += next_op_i.size();
                 }
             } else {
                 // the interval passed in the shrink function is base on the op not the delta.
                 if let Some(new_op) = next_op.shrink(interval.translate_neg(offset)) {
                     ops.push(new_op);
                 }
+                // take the small value between interval.size() and next_op.length().
+                // for example: extract the ops from three insert ops with interval [2,5). the
+                // interval size is larger than the op. Run the
+                // delta_get_ops_in_interval_4 for more details.
+                // ┌──────┐  ┌──────┐  ┌──────┐
+                // │ 1 2  │  │ 3 4  │  │ 5 6  │
+                // └──────┘  └─▲────┘  └───▲──┘
+                //             │  [2, 5)   │
+                //
                 offset += min(interval.size(), next_op.length());
                 interval = Interval::new(offset, interval.end);
             }
@@ -635,3 +612,38 @@ impl Delta {
 
     pub fn to_json(&self) -> String { serde_json::to_string(self).unwrap_or("".to_owned()) }
 }
+
+fn invert_from_other(
+    inverted: &mut Delta,
+    other: &Delta,
+    operation: &Operation,
+    start: usize,
+    end: usize,
+) {
+    log::debug!("invert op: {:?} [{}:{}]", operation, start, end);
+    let ops = other.ops_in_interval(Interval::new(start, end));
+    ops.into_iter().for_each(|other_op| {
+        match operation {
+            Operation::Delete(_) => {
+                log::debug!("add: {}", other_op);
+                inverted.add(other_op);
+            },
+            Operation::Retain(_) => {
+                log::debug!(
+                    "Start invert attributes: {:?}, {:?}",
+                    operation.get_attributes(),
+                    other_op.get_attributes()
+                );
+                let inverted_attrs =
+                    invert_attributes(operation.get_attributes(), other_op.get_attributes());
+                log::debug!("End invert attributes: {:?}", inverted_attrs);
+                log::debug!("invert retain: {}, {}", other_op.length(), inverted_attrs);
+                inverted.retain(other_op.length(), inverted_attrs);
+            },
+            Operation::Insert(_) => {
+                // Impossible to here
+                panic!()
+            },
+        }
+    });
+}

+ 70 - 25
rust-lib/flowy-ot/tests/attribute_test.rs

@@ -154,7 +154,11 @@ fn delta_add_bold_italic() {
         Italic(0, Interval::new(4, 6), false),
         AssertOpsJson(
             0,
-            r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true"}},{"insert":"78","attributes":{"bold":"true","italic":"true"}}]"#,
+            r#"[
+            {"insert":"1234","attributes":{"bold":"true","italic":"true"}},
+            {"insert":"56","attributes":{"bold":"true"}},
+            {"insert":"78","attributes":{"bold":"true","italic":"true"}}]
+            "#,
         ),
     ];
     OpTester::new().run_script(ops);
@@ -169,12 +173,19 @@ fn delta_add_bold_italic2() {
         Italic(0, Interval::new(0, 2), true),
         AssertOpsJson(
             0,
-            r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"3456","attributes":{"bold":"true"}}]"#,
+            r#"[
+            {"insert":"12","attributes":{"italic":"true","bold":"true"}},
+            {"insert":"3456","attributes":{"bold":"true"}}]
+            "#,
         ),
         Italic(0, Interval::new(4, 6), true),
         AssertOpsJson(
             0,
-            r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"34","attributes":{"bold":"true"}},{"insert":"56","attributes":{"italic":"true","bold":"true"}}]"#,
+            r#"[
+            {"insert":"12","attributes":{"italic":"true","bold":"true"}},
+            {"insert":"34","attributes":{"bold":"true"}},
+            {"insert":"56","attributes":{"italic":"true","bold":"true"}}]
+            "#,
         ),
     ];
 
@@ -189,17 +200,29 @@ fn delta_add_bold_italic3() {
         Italic(0, Interval::new(0, 2), true),
         AssertOpsJson(
             0,
-            r#"[{"insert":"12","attributes":{"bold":"true","italic":"true"}},{"insert":"345","attributes":{"bold":"true"}},{"insert":"6789"}]"#,
+            r#"[
+            {"insert":"12","attributes":{"bold":"true","italic":"true"}},
+            {"insert":"345","attributes":{"bold":"true"}},{"insert":"6789"}]
+            "#,
         ),
         Italic(0, Interval::new(2, 4), true),
         AssertOpsJson(
             0,
-            r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"5","attributes":{"bold":"true"}},{"insert":"6789"}]"#,
+            r#"[
+            {"insert":"1234","attributes":{"bold":"true","italic":"true"}},
+            {"insert":"5","attributes":{"bold":"true"}},
+            {"insert":"6789"}]
+            "#,
         ),
         Bold(0, Interval::new(7, 9), true),
         AssertOpsJson(
             0,
-            r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"5","attributes":{"bold":"true"}},{"insert":"67"},{"insert":"89","attributes":{"bold":"true"}}]"#,
+            r#"[
+            {"insert":"1234","attributes":{"bold":"true","italic":"true"}},
+            {"insert":"5","attributes":{"bold":"true"}},
+            {"insert":"67"},
+            {"insert":"89","attributes":{"bold":"true"}}]
+            "#,
         ),
     ];
 
@@ -212,27 +235,35 @@ fn delta_add_bold_italic_delete() {
         Insert(0, "123456789", 0),
         Bold(0, Interval::new(0, 5), true),
         Italic(0, Interval::new(0, 2), true),
-        // AssertOpsJson(
-        //     0,
-        //     r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"345","
-        // attributes":{"bold":"true"}},{"insert":"6789"}]"#, ),
+        AssertOpsJson(
+            0,
+            r#"[
+            {"insert":"12","attributes":{"italic":"true","bold":"true"}},
+            {"insert":"345","attributes":{"bold":"true"}},{"insert":"6789"}]
+            "#,
+        ),
         Italic(0, Interval::new(2, 4), true),
         AssertOpsJson(
             0,
-            r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"5","attributes":{"bold":"true"}},{"insert":"6789"}]"#,
+            r#"[
+            {"insert":"1234","attributes":{"bold":"true","italic":"true"}}
+            ,{"insert":"5","attributes":{"bold":"true"}},{"insert":"6789"}]"#,
+        ),
+        Bold(0, Interval::new(7, 9), true),
+        AssertOpsJson(
+            0,
+            r#"[
+            {"insert":"1234","attributes":{"bold":"true","italic":"true"}},
+            {"insert":"5","attributes":{"bold":"true"}},{"insert":"67"},
+            {"insert":"89","attributes":{"bold":"true"}}]
+            "#,
+        ),
+        Delete(0, Interval::new(0, 5)),
+        AssertOpsJson(
+            0,
+            r#"[{"insert":"67"},{"insert":"89","attributes":{"bold":"true"}}]"#,
         ),
     ];
-    // Bold(0, Interval::new(7, 9), true),
-    // AssertOpsJson(
-    //     0,
-    //     r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"
-    // insert":"5","attributes":{"bold":"true"}},{"insert":"67"},{"insert":"89","
-    // attributes":{"bold":"true"}}]"#, ),
-    // Delete(0, Interval::new(0, 5)),
-    // AssertOpsJson(
-    //     0,
-    //     r#"[{"insert":"67"},{"insert":"89","attributes":{"bold":"true"}}]"#,
-    // ),
 
     OpTester::new().run_script(ops);
 }
@@ -272,18 +303,32 @@ fn delta_compose_attr_delta_with_attr_delta_test2() {
         Italic(0, Interval::new(4, 6), true),
         AssertOpsJson(
             0,
-            r#"[{"insert":"12","attributes":{"bold":"true","italic":"true"}},{"insert":"34","attributes":{"bold":"true"}},{"insert":"56","attributes":{"italic":"true","bold":"true"}}]"#,
+            r#"[
+            {"insert":"12","attributes":{"bold":"true","italic":"true"}},
+            {"insert":"34","attributes":{"bold":"true"}},
+            {"insert":"56","attributes":{"italic":"true","bold":"true"}}]
+            "#,
         ),
         InsertBold(1, "7", Interval::new(0, 1)),
         AssertOpsJson(1, r#"[{"insert":"7","attributes":{"bold":"true"}}]"#),
         Transform(0, 1),
         AssertOpsJson(
             0,
-            r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"34","attributes":{"bold":"true"}},{"insert":"56","attributes":{"italic":"true","bold":"true"}},{"insert":"7","attributes":{"bold":"true"}}]"#,
+            r#"[
+            {"insert":"12","attributes":{"italic":"true","bold":"true"}},
+            {"insert":"34","attributes":{"bold":"true"}},
+            {"insert":"56","attributes":{"italic":"true","bold":"true"}},
+            {"insert":"7","attributes":{"bold":"true"}}]
+            "#,
         ),
         AssertOpsJson(
             1,
-            r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"34","attributes":{"bold":"true"}},{"insert":"56","attributes":{"italic":"true","bold":"true"}},{"insert":"7","attributes":{"bold":"true"}}]"#,
+            r#"[
+            {"insert":"12","attributes":{"italic":"true","bold":"true"}},
+            {"insert":"34","attributes":{"bold":"true"}},
+            {"insert":"56","attributes":{"italic":"true","bold":"true"}},
+            {"insert":"7","attributes":{"bold":"true"}}]
+            "#,
         ),
     ];
 

+ 26 - 2
rust-lib/flowy-ot/tests/invert_test.rs

@@ -215,8 +215,8 @@ fn delta_get_ops_in_interval_3() {
     let insert_a = Builder::insert("123456").build();
     delta.add(insert_a.clone());
     assert_eq!(
-        delta.ops_in_interval(Interval::new(3, 6)),
-        vec![Builder::insert("456").build()]
+        delta.ops_in_interval(Interval::new(3, 5)),
+        vec![Builder::insert("45").build()]
     );
 }
 
@@ -240,3 +240,27 @@ fn delta_get_ops_in_interval_4() {
         vec![Builder::insert("34").build(), Builder::insert("5").build()]
     );
 }
+
+#[test]
+fn delta_get_ops_in_interval_5() {
+    let mut delta = Delta::default();
+    let insert_a = Builder::insert("123456").build();
+    let insert_b = Builder::insert("789").build();
+    delta.ops.push(insert_a.clone());
+    delta.ops.push(insert_b.clone());
+    assert_eq!(
+        delta.ops_in_interval(Interval::new(4, 8)),
+        vec![Builder::insert("56").build(), Builder::insert("78").build()]
+    );
+}
+
+#[test]
+fn delta_get_ops_in_interval_6() {
+    let mut delta = Delta::default();
+    let insert_a = Builder::insert("12345678").build();
+    delta.add(insert_a.clone());
+    assert_eq!(
+        delta.ops_in_interval(Interval::new(4, 6)),
+        vec![Builder::insert("56").build()]
+    );
+}

+ 60 - 0
rust-lib/flowy-ot/tests/undo_redo_test.rs

@@ -88,3 +88,63 @@ fn delta_redo_attributes() {
     ];
     OpTester::new().run_script(ops);
 }
+
+#[test]
+fn delta_undo_delete() {
+    let ops = vec![
+        Insert(0, "\n", 0),
+        Insert(0, "123", 0),
+        Bold(0, Interval::new(0, 3), true),
+        Delete(0, Interval::new(0, 3)),
+        AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
+        Undo(0),
+        AssertOpsJson(
+            0,
+            r#"[
+            {"insert":"123","attributes":{"bold":"true"}},
+            {"insert":"\n"}]
+            "#,
+        ),
+    ];
+    OpTester::new().run_script(ops);
+}
+
+#[test]
+fn delta_undo_delete2() {
+    let ops = vec![
+        Insert(0, "\n", 0),
+        Insert(0, "123", 0),
+        Bold(0, Interval::new(0, 3), true),
+        Delete(0, Interval::new(0, 1)),
+        AssertOpsJson(
+            0,
+            r#"[
+            {"insert":"23","attributes":{"bold":"true"}},
+            {"insert":"\n"}]
+            "#,
+        ),
+        Undo(0),
+        AssertOpsJson(
+            0,
+            r#"[
+            {"insert":"123","attributes":{"bold":"true"}},
+            {"insert":"\n"}]
+            "#,
+        ),
+    ];
+    OpTester::new().run_script(ops);
+}
+
+#[test]
+fn delta_redo_delete() {
+    let ops = vec![
+        Insert(0, "\n", 0),
+        Insert(0, "123", 0),
+        Delete(0, Interval::new(0, 3)),
+        AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
+        Undo(0),
+        Redo(0),
+        AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
+    ];
+    OpTester::new().run_script(ops);
+}