浏览代码

fix redo attribut bugs

appflowy 3 年之前
父节点
当前提交
efa78dd456

+ 1 - 0
rust-lib/flowy-ot/Cargo.toml

@@ -11,6 +11,7 @@ serde = { version = "1.0", features = ["derive"] }
 serde_json = {version = "1.0"}
 derive_more = {version = "0.99", features = ["display"]}
 log = "0.4"
+color-eyre = { version = "0.5", default-features = false }
 
 [dev-dependencies]
 criterion = "0.3"

+ 9 - 4
rust-lib/flowy-ot/src/client/document.rs

@@ -5,9 +5,9 @@ use crate::{
         Attributes,
         AttributesDataRule,
         AttrsBuilder,
+        Builder,
         Delta,
         Interval,
-        OpBuilder,
         Operation,
     },
     errors::{ErrorBuilder, OTError, OTErrorCode::*},
@@ -42,7 +42,7 @@ impl Document {
         if attributes == Attributes::Empty {
             attributes = Attributes::Follow;
         }
-        let insert = OpBuilder::insert(text).attributes(attributes).build();
+        let insert = Builder::insert(text).attributes(attributes).build();
         let interval = Interval::new(index, index);
 
         self.update_with_op(insert, interval)
@@ -70,8 +70,10 @@ impl Document {
         match self.history.undo() {
             None => Err(ErrorBuilder::new(UndoFail).build()),
             Some(undo_delta) => {
+                log::debug!("undo: {:?}", undo_delta);
                 let composed_delta = self.data.compose(&undo_delta)?;
                 let redo_delta = undo_delta.invert(&self.data);
+                log::debug!("computed redo: {:?}", redo_delta);
                 let result = UndoResult::success(composed_delta.target_len as usize);
                 self.data = composed_delta;
                 self.history.add_redo(redo_delta);
@@ -85,10 +87,13 @@ impl Document {
         match self.history.redo() {
             None => Err(ErrorBuilder::new(RedoFail).build()),
             Some(redo_delta) => {
+                log::debug!("redo: {:?}", redo_delta);
                 let new_delta = self.data.compose(&redo_delta)?;
                 let result = UndoResult::success(new_delta.target_len as usize);
                 let undo_delta = redo_delta.invert(&self.data);
+                log::debug!("computed undo: {:?}", undo_delta);
                 self.data = new_delta;
+
                 self.history.add_undo(undo_delta);
                 Ok(result)
             },
@@ -96,7 +101,7 @@ impl Document {
     }
 
     pub fn delete(&mut self, interval: Interval) -> Result<(), OTError> {
-        let delete = OpBuilder::delete(interval.size()).build();
+        let delete = Builder::delete(interval.size()).build();
         self.update_with_op(delete, interval)
     }
 
@@ -168,7 +173,7 @@ impl Document {
         };
 
         log::debug!("new attributes: {:?}", new_attributes);
-        let retain = OpBuilder::retain(interval.size())
+        let retain = Builder::retain(interval.size())
             .attributes(new_attributes)
             .build();
 

+ 0 - 9
rust-lib/flowy-ot/src/core/attributes/attributes.rs

@@ -19,14 +19,6 @@ impl Attributes {
             Attributes::Empty => None,
         }
     }
-
-    pub fn is_empty(&self) -> bool {
-        match self {
-            Attributes::Follow => true,
-            Attributes::Custom(data) => data.is_empty(),
-            Attributes::Empty => true,
-        }
-    }
 }
 
 impl std::default::Default for Attributes {
@@ -95,7 +87,6 @@ pub fn transform_operation(left: &Option<Operation>, right: &Option<Operation>)
 }
 
 pub fn invert_attributes(attr: Attributes, base: Attributes) -> Attributes {
-    log::info!("Invert attributes: {:?} : {:?}", attr, base);
     let attr = attr.data();
     let base = base.data();
 

+ 3 - 2
rust-lib/flowy-ot/src/core/attributes/data.rs

@@ -17,7 +17,8 @@ impl AttributesData {
             inner: HashMap::new(),
         }
     }
-    pub fn is_empty(&self) -> bool {
+
+    pub fn is_plain(&self) -> bool {
         self.inner.values().filter(|v| !should_remove(v)).count() == 0
     }
 
@@ -59,7 +60,7 @@ impl AttributesDataRule for AttributesData {
     fn into_attributes(mut self) -> Attributes {
         self.apply_rule();
 
-        if self.is_empty() {
+        if self.is_plain() {
             Attributes::Empty
         } else {
             Attributes::Custom(self)

+ 64 - 44
rust-lib/flowy-ot/src/core/delta.rs

@@ -89,7 +89,7 @@ impl Delta {
         if let Some(Operation::Delete(n_last)) = self.ops.last_mut() {
             *n_last += n;
         } else {
-            self.ops.push(OpBuilder::delete(n).build());
+            self.ops.push(Builder::delete(n).build());
         }
     }
 
@@ -110,10 +110,10 @@ impl Delta {
             },
             [.., op_last @ Operation::Delete(_)] => {
                 let new_last = op_last.clone();
-                *op_last = OpBuilder::insert(s).attributes(attrs).build();
+                *op_last = Builder::insert(s).attributes(attrs).build();
                 Some(new_last)
             },
-            _ => Some(OpBuilder::insert(s).attributes(attrs).build()),
+            _ => Some(Builder::insert(s).attributes(attrs).build()),
         };
 
         match new_last {
@@ -134,8 +134,7 @@ impl Delta {
                 self.ops.push(new_op);
             }
         } else {
-            self.ops
-                .push(OpBuilder::retain(n).attributes(attrs).build());
+            self.ops.push(Builder::retain(n).attributes(attrs).build());
         }
     }
 
@@ -185,7 +184,7 @@ impl Delta {
                     match retain.cmp(&o_retain) {
                         Ordering::Less => {
                             new_delta.retain(retain.n, composed_attrs);
-                            next_op2 = Some(OpBuilder::retain(o_retain.n - retain.n).build());
+                            next_op2 = Some(Builder::retain(o_retain.n - retain.n).build());
                             next_op1 = ops1.next();
                         },
                         std::cmp::Ordering::Equal => {
@@ -195,7 +194,7 @@ impl Delta {
                         },
                         std::cmp::Ordering::Greater => {
                             new_delta.retain(o_retain.n, composed_attrs);
-                            next_op1 = Some(OpBuilder::retain(retain.n - o_retain.n).build());
+                            next_op1 = Some(Builder::retain(retain.n - o_retain.n).build());
                             next_op2 = ops2.next();
                         },
                     }
@@ -204,7 +203,7 @@ impl Delta {
                     match (num_chars(insert.as_bytes()) as usize).cmp(o_num) {
                         Ordering::Less => {
                             next_op2 = Some(
-                                OpBuilder::delete(*o_num - num_chars(insert.as_bytes()) as usize)
+                                Builder::delete(*o_num - num_chars(insert.as_bytes()) as usize)
                                     .attributes(insert.attributes.clone())
                                     .build(),
                             );
@@ -216,7 +215,7 @@ impl Delta {
                         },
                         Ordering::Greater => {
                             next_op1 = Some(
-                                OpBuilder::insert(
+                                Builder::insert(
                                     &insert.chars().skip(*o_num as usize).collect::<String>(),
                                 )
                                 .build(),
@@ -237,7 +236,7 @@ impl Delta {
                         Ordering::Less => {
                             new_delta.insert(&insert.s, composed_attrs.clone());
                             next_op2 = Some(
-                                OpBuilder::retain(o_retain.n - insert.num_chars())
+                                Builder::retain(o_retain.n - insert.num_chars())
                                     .attributes(o_retain.attributes.clone())
                                     .build(),
                             );
@@ -255,7 +254,7 @@ impl Delta {
                                 composed_attrs,
                             );
                             next_op1 = Some(
-                                OpBuilder::insert(&chars.collect::<String>())
+                                Builder::insert(&chars.collect::<String>())
                                     // consider this situation: 
                                     // [insert:12345678 - retain:4], 
                                     //      the attributes of "5678" should be empty and the following 
@@ -271,7 +270,7 @@ impl Delta {
                     match retain.cmp(&o_num) {
                         Ordering::Less => {
                             new_delta.delete(retain.n);
-                            next_op2 = Some(OpBuilder::delete(*o_num - retain.n).build());
+                            next_op2 = Some(Builder::delete(*o_num - retain.n).build());
                             next_op1 = ops1.next();
                         },
                         Ordering::Equal => {
@@ -281,7 +280,7 @@ impl Delta {
                         },
                         Ordering::Greater => {
                             new_delta.delete(*o_num);
-                            next_op1 = Some(OpBuilder::retain(retain.n - *o_num).build());
+                            next_op1 = Some(Builder::retain(retain.n - *o_num).build());
                             next_op2 = ops2.next();
                         },
                     }
@@ -340,7 +339,7 @@ impl Delta {
                         Ordering::Less => {
                             a_prime.retain(retain.n, composed_attrs.clone());
                             b_prime.retain(retain.n, composed_attrs.clone());
-                            next_op2 = Some(OpBuilder::retain(o_retain.n - retain.n).build());
+                            next_op2 = Some(Builder::retain(o_retain.n - retain.n).build());
                             next_op1 = ops1.next();
                         },
                         Ordering::Equal => {
@@ -352,14 +351,14 @@ impl Delta {
                         Ordering::Greater => {
                             a_prime.retain(o_retain.n, composed_attrs.clone());
                             b_prime.retain(o_retain.n, composed_attrs.clone());
-                            next_op1 = Some(OpBuilder::retain(retain.n - o_retain.n).build());
+                            next_op1 = Some(Builder::retain(retain.n - o_retain.n).build());
                             next_op2 = ops2.next();
                         },
                     };
                 },
                 (Some(Operation::Delete(i)), Some(Operation::Delete(j))) => match i.cmp(&j) {
                     Ordering::Less => {
-                        next_op2 = Some(OpBuilder::delete(*j - *i).build());
+                        next_op2 = Some(Builder::delete(*j - *i).build());
                         next_op1 = ops1.next();
                     },
                     Ordering::Equal => {
@@ -367,7 +366,7 @@ impl Delta {
                         next_op2 = ops2.next();
                     },
                     Ordering::Greater => {
-                        next_op1 = Some(OpBuilder::delete(*i - *j).build());
+                        next_op1 = Some(Builder::delete(*i - *j).build());
                         next_op2 = ops2.next();
                     },
                 },
@@ -375,7 +374,7 @@ impl Delta {
                     match i.cmp(&o_retain) {
                         Ordering::Less => {
                             a_prime.delete(*i);
-                            next_op2 = Some(OpBuilder::retain(o_retain.n - *i).build());
+                            next_op2 = Some(Builder::retain(o_retain.n - *i).build());
                             next_op1 = ops1.next();
                         },
                         Ordering::Equal => {
@@ -385,7 +384,7 @@ impl Delta {
                         },
                         Ordering::Greater => {
                             a_prime.delete(o_retain.n);
-                            next_op1 = Some(OpBuilder::delete(*i - o_retain.n).build());
+                            next_op1 = Some(Builder::delete(*i - o_retain.n).build());
                             next_op2 = ops2.next();
                         },
                     };
@@ -394,7 +393,7 @@ impl Delta {
                     match retain.cmp(&j) {
                         Ordering::Less => {
                             b_prime.delete(retain.n);
-                            next_op2 = Some(OpBuilder::delete(*j - retain.n).build());
+                            next_op2 = Some(Builder::delete(*j - retain.n).build());
                             next_op1 = ops1.next();
                         },
                         Ordering::Equal => {
@@ -404,7 +403,7 @@ impl Delta {
                         },
                         Ordering::Greater => {
                             b_prime.delete(*j);
-                            next_op1 = Some(OpBuilder::retain(retain.n - *j).build());
+                            next_op1 = Some(Builder::retain(retain.n - *j).build());
                             next_op2 = ops2.next();
                         },
                     };
@@ -485,10 +484,13 @@ impl Delta {
 
         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(_) => {
@@ -502,6 +504,7 @@ impl Delta {
                                 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(_) => {
@@ -515,21 +518,25 @@ impl Delta {
         let mut index = 0;
         for op in &self.ops {
             let len: usize = op.length() as usize;
-            log::info!("{:?}", op);
             match op {
-                Operation::Delete(_) => {
-                    inverted_from_other(&mut inverted, op, index, index + len);
+                Operation::Delete(n) => {
+                    inverted_from_other(&mut inverted, op, index, index + *n);
                     index += len;
                 },
                 Operation::Retain(_) => {
                     match op.has_attribute() {
                         true => inverted_from_other(&mut inverted, op, index, index + len),
-                        false => inverted.retain(len as usize, op.get_attributes()),
+                        false => {
+                            log::debug!("invert retain op: {:?}", op);
+                            inverted.retain(len as usize, op.get_attributes())
+                        },
                     }
                     index += len;
                 },
-                Operation::Insert(_) => {
+                Operation::Insert(insert) => {
+                    log::debug!("invert insert op: {:?}", op);
                     inverted.delete(len as usize);
+                    // index += insert.s.len();
                 },
             }
         }
@@ -549,29 +556,42 @@ impl Delta {
 
     pub fn is_empty(&self) -> bool { self.ops.is_empty() }
 
-    pub fn ops_in_interval(&self, interval: Interval) -> Vec<Operation> {
+    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 offset: usize = 0;
         let mut ops_iter = self.ops.iter();
-        let mut next_op = ops_iter.next();
-
-        while offset < interval.end && next_op.is_some() {
-            let op = next_op.take().unwrap();
-            let len = offset + op.length() as usize;
-            // log::info!("{:?}", op);
-            while offset < len {
-                if offset < interval.start {
-                    offset += min(interval.start, op.length() as usize);
-                } else {
-                    if interval.contains(offset) {
-                        ops.push(op.shrink_to_interval(interval));
-                        offset += min(op.length() as usize, interval.size());
-                    } else {
-                        break;
+        let mut maybe_next_op = ops_iter.next();
+        let mut offset: usize = 0;
+
+        while maybe_next_op.is_some() {
+            let next_op = maybe_next_op.take().unwrap();
+            if offset < interval.start {
+                if next_op.length() > interval.size() {
+                    // if the op's length larger than the interval size, just shrink the op to that
+                    // interval. for example: delta: "123456", interval: [3,6).
+                    // ┌──────────────┐
+                    // │ 1 2 3 4 5 6  │
+                    // └───────▲───▲──┘
+                    //         │   │
+                    //        [3, 6)
+                    if let Some(new_op) = next_op.shrink(interval) {
+                        ops.push(new_op);
                     }
+                } else {
+                    // adding the op's length to offset until the offset is contained in the
+                    // interval
+                    offset += next_op.length();
+                }
+            } 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);
                 }
+                offset += min(interval.size(), next_op.length());
+                interval = Interval::new(offset, interval.end);
             }
-            next_op = ops_iter.next();
+            maybe_next_op = ops_iter.next();
         }
         ops
     }

+ 8 - 8
rust-lib/flowy-ot/src/core/operation/builder.rs

@@ -1,25 +1,25 @@
 use crate::core::{Attributes, Operation};
 
-pub struct OpBuilder {
+pub struct Builder {
     ty: Operation,
     attrs: Attributes,
 }
 
-impl OpBuilder {
-    pub fn new(ty: Operation) -> OpBuilder {
-        OpBuilder {
+impl Builder {
+    pub fn new(ty: Operation) -> Builder {
+        Builder {
             ty,
             attrs: Attributes::Empty,
         }
     }
 
-    pub fn retain(n: usize) -> OpBuilder { OpBuilder::new(Operation::Retain(n.into())) }
+    pub fn retain(n: usize) -> Builder { Builder::new(Operation::Retain(n.into())) }
 
-    pub fn delete(n: usize) -> OpBuilder { OpBuilder::new(Operation::Delete(n)) }
+    pub fn delete(n: usize) -> Builder { Builder::new(Operation::Delete(n)) }
 
-    pub fn insert(s: &str) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) }
+    pub fn insert(s: &str) -> Builder { Builder::new(Operation::Insert(s.into())) }
 
-    pub fn attributes(mut self, attrs: Attributes) -> OpBuilder {
+    pub fn attributes(mut self, attrs: Attributes) -> Builder {
         self.attrs = attrs;
         self
     }

+ 38 - 30
rust-lib/flowy-ot/src/core/operation/operation.rs

@@ -1,4 +1,4 @@
-use crate::core::{Attributes, Interval, OpBuilder};
+use crate::core::{Attributes, Builder, Interval};
 use bytecount::num_chars;
 use std::{
     cmp::min,
@@ -54,11 +54,20 @@ impl Operation {
     pub fn has_attribute(&self) -> bool {
         match self.get_attributes() {
             Attributes::Follow => false,
-            Attributes::Custom(data) => !data.is_empty(),
+            // Attributes::Custom(data) => !data.is_plain(),
+            Attributes::Custom(data) => true,
             Attributes::Empty => false,
         }
     }
 
+    pub fn is_plain(&self) -> bool {
+        match self.get_attributes() {
+            Attributes::Follow => true,
+            Attributes::Custom(data) => data.is_plain(),
+            Attributes::Empty => true,
+        }
+    }
+
     pub fn length(&self) -> usize {
         match self {
             Operation::Delete(n) => *n,
@@ -69,30 +78,27 @@ impl Operation {
 
     pub fn is_empty(&self) -> bool { self.length() == 0 }
 
-    pub fn shrink_to_interval(&self, interval: Interval) -> Operation {
-        match self {
-            Operation::Delete(n) => {
-                //
-                OpBuilder::delete(min(*n, interval.size())).build()
-            },
-            Operation::Retain(retain) => {
-                //
-                OpBuilder::retain(min(retain.n, interval.size()))
-                    .attributes(retain.attributes.clone())
-                    .build()
-            },
+    pub fn shrink(&self, interval: Interval) -> Option<Operation> {
+        let op = match self {
+            Operation::Delete(n) => Builder::delete(min(*n, interval.size())).build(),
+            Operation::Retain(retain) => Builder::retain(min(retain.n, interval.size()))
+                .attributes(retain.attributes.clone())
+                .build(),
             Operation::Insert(insert) => {
-                // debug_assert!(insert.s.len() <= interval.size());
                 if interval.start > insert.s.len() {
-                    return OpBuilder::insert("").build();
+                    Builder::insert("").build()
+                } else {
+                    let s = &insert.s[interval.start..min(interval.end, insert.s.len())];
+                    Builder::insert(s)
+                        .attributes(insert.attributes.clone())
+                        .build()
                 }
-
-                let end = min(interval.end, insert.s.len());
-                let s = &insert.s[interval.start..end];
-                OpBuilder::insert(s)
-                    .attributes(insert.attributes.clone())
-                    .build()
             },
+        };
+
+        match op.is_empty() {
+            true => None,
+            false => Some(op),
         }
     }
 }
@@ -130,11 +136,7 @@ pub struct Retain {
 
 impl Retain {
     pub fn merge_or_new_op(&mut self, n: usize, attributes: Attributes) -> Option<Operation> {
-        log::debug!(
-            "merge_retain_or_new_op: {:?}, {:?}",
-            self.attributes,
-            attributes
-        );
+        log::debug!("merge_retain_or_new_op: {:?}, {:?}", n, attributes);
 
         match &attributes {
             Attributes::Follow => {
@@ -149,7 +151,7 @@ impl Retain {
                     None
                 } else {
                     log::debug!("New retain op");
-                    Some(OpBuilder::retain(n).attributes(attributes).build())
+                    Some(Builder::retain(n).attributes(attributes).build())
                 }
             },
         }
@@ -202,7 +204,7 @@ impl Insert {
                     self.s += s;
                     None
                 } else {
-                    Some(OpBuilder::insert(s).attributes(attributes).build())
+                    Some(Builder::insert(s).attributes(attributes).build())
                 }
             },
         }
@@ -222,4 +224,10 @@ impl std::convert::From<&str> for Insert {
     fn from(s: &str) -> Self { Insert::from(s.to_owned()) }
 }
 
-fn is_empty(attributes: &Attributes) -> bool { attributes.is_empty() }
+fn is_empty(attributes: &Attributes) -> bool {
+    match attributes {
+        Attributes::Follow => true,
+        Attributes::Custom(data) => data.is_plain(),
+        Attributes::Empty => true,
+    }
+}

+ 2 - 1
rust-lib/flowy-ot/tests/helper/mod.rs

@@ -50,7 +50,8 @@ impl OpTester {
     pub fn new() -> Self {
         static INIT: Once = Once::new();
         INIT.call_once(|| {
-            std::env::set_var("RUST_LOG", "debug");
+            color_eyre::install().unwrap();
+            std::env::set_var("RUST_LOG", "info");
             env_logger::init();
         });
 

+ 84 - 32
rust-lib/flowy-ot/tests/invert_test.rs

@@ -1,15 +1,15 @@
 pub mod helper;
 use crate::helper::{TestOp::*, *};
-use flowy_ot::core::{Delta, Interval, OpBuilder};
+use flowy_ot::core::{Builder, Delta, Interval};
 
 #[test]
 fn delta_invert_no_attribute_delta() {
     let mut delta = Delta::default();
-    delta.add(OpBuilder::insert("123").build());
+    delta.add(Builder::insert("123").build());
 
     let mut change = Delta::default();
-    change.add(OpBuilder::retain(3).build());
-    change.add(OpBuilder::insert("456").build());
+    change.add(Builder::retain(3).build());
+    change.add(Builder::insert("456").build());
     let undo = change.invert(&delta);
 
     let new_delta = delta.compose(&change).unwrap();
@@ -48,21 +48,30 @@ fn delta_invert_attribute_delta_with_no_attribute_delta2() {
         Insert(0, "123", 0),
         Bold(0, Interval::new(0, 3), true),
         Insert(0, "456", 3),
-        AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
+        AssertOpsJson(
+            0,
+            r#"[
+            {"insert":"123456","attributes":{"bold":"true"}}]
+            "#,
+        ),
         Italic(0, Interval::new(2, 4), true),
         AssertOpsJson(
             0,
-            r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34","attributes":
-{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true"
-}}]"#,
+            r#"[
+            {"insert":"12","attributes":{"bold":"true"}}, 
+            {"insert":"34","attributes":{"bold":"true","italic":"true"}},
+            {"insert":"56","attributes":{"bold":"true"}}
+            ]"#,
         ),
         Insert(1, "abc", 0),
         Invert(0, 1),
         AssertOpsJson(
             0,
-            r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34","attributes":
-{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true"
-}}]"#,
+            r#"[
+            {"insert":"12","attributes":{"bold":"true"}},
+            {"insert":"34","attributes":{"bold":"true","italic":"true"}},
+            {"insert":"56","attributes":{"bold":"true"}}
+            ]"#,
         ),
     ];
     OpTester::new().run_script(ops);
@@ -115,9 +124,11 @@ fn delta_invert_attribute_delta_with_attribute_delta() {
         Italic(0, Interval::new(2, 4), true),
         AssertOpsJson(
             0,
-            r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34","attributes":
-{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true"
-}}]"#,
+            r#"[
+            {"insert":"12","attributes":{"bold":"true"}},
+            {"insert":"34","attributes":{"bold":"true","italic":"true"}},
+            {"insert":"56","attributes":{"bold":"true"}}
+            ]"#,
         ),
         Insert(1, "abc", 0),
         Bold(1, Interval::new(0, 3), true),
@@ -125,16 +136,20 @@ fn delta_invert_attribute_delta_with_attribute_delta() {
         Italic(1, Interval::new(1, 3), true),
         AssertOpsJson(
             1,
-            r#"[{"insert":"a","attributes":{"bold":"true"}},{"insert":"bc","attributes":
-{"bold":"true","italic":"true"}},{"insert":"d","attributes":{"bold":"true"
-}}]"#,
+            r#"[
+            {"insert":"a","attributes":{"bold":"true"}},
+            {"insert":"bc","attributes":{"bold":"true","italic":"true"}},
+            {"insert":"d","attributes":{"bold":"true"}}
+            ]"#,
         ),
         Invert(0, 1),
         AssertOpsJson(
             0,
-            r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34","attributes":
-{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true"
-}}]"#,
+            r#"[
+            {"insert":"12","attributes":{"bold":"true"}},
+            {"insert":"34","attributes":{"bold":"true","italic":"true"}},
+            {"insert":"56","attributes":{"bold":"true"}}
+            ]"#,
         ),
     ];
     OpTester::new().run_script(ops);
@@ -143,8 +158,8 @@ fn delta_invert_attribute_delta_with_attribute_delta() {
 #[test]
 fn delta_get_ops_in_interval_1() {
     let mut delta = Delta::default();
-    let insert_a = OpBuilder::insert("123").build();
-    let insert_b = OpBuilder::insert("4").build();
+    let insert_a = Builder::insert("123").build();
+    let insert_b = Builder::insert("4").build();
 
     delta.add(insert_a.clone());
     delta.add(insert_b.clone());
@@ -158,16 +173,21 @@ fn delta_get_ops_in_interval_1() {
 #[test]
 fn delta_get_ops_in_interval_2() {
     let mut delta = Delta::default();
-    let insert_a = OpBuilder::insert("123").build();
-    let insert_b = OpBuilder::insert("4").build();
-    let insert_c = OpBuilder::insert("5").build();
-    let retain_a = OpBuilder::retain(3).build();
+    let insert_a = Builder::insert("123").build();
+    let insert_b = Builder::insert("4").build();
+    let insert_c = Builder::insert("5").build();
+    let retain_a = Builder::retain(3).build();
 
     delta.add(insert_a.clone());
     delta.add(retain_a.clone());
     delta.add(insert_b.clone());
     delta.add(insert_c.clone());
 
+    assert_eq!(
+        delta.ops_in_interval(Interval::new(0, 2)),
+        vec![Builder::insert("12").build()]
+    );
+
     assert_eq!(
         delta.ops_in_interval(Interval::new(0, 3)),
         vec![insert_a.clone()]
@@ -175,16 +195,48 @@ fn delta_get_ops_in_interval_2() {
 
     assert_eq!(
         delta.ops_in_interval(Interval::new(0, 4)),
+        vec![insert_a.clone(), Builder::retain(1).build()]
+    );
+
+    assert_eq!(
+        delta.ops_in_interval(Interval::new(0, 6)),
         vec![insert_a.clone(), retain_a.clone()]
     );
 
     assert_eq!(
         delta.ops_in_interval(Interval::new(0, 7)),
-        vec![
-            insert_a.clone(),
-            retain_a.clone(),
-            // insert_b and insert_c will be merged into one. insert: "45"
-            delta.ops.last().unwrap().clone()
-        ]
+        vec![insert_a.clone(), retain_a.clone(), insert_b.clone()]
+    );
+}
+
+#[test]
+fn delta_get_ops_in_interval_3() {
+    let mut delta = Delta::default();
+    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()]
+    );
+}
+
+#[test]
+fn delta_get_ops_in_interval_4() {
+    let mut delta = Delta::default();
+    let insert_a = Builder::insert("12").build();
+    let insert_b = Builder::insert("34").build();
+    let insert_c = Builder::insert("56").build();
+
+    delta.ops.push(insert_a.clone());
+    delta.ops.push(insert_b.clone());
+    delta.ops.push(insert_c.clone());
+
+    assert_eq!(delta.ops_in_interval(Interval::new(0, 2)), vec![insert_a]);
+    assert_eq!(delta.ops_in_interval(Interval::new(2, 4)), vec![insert_b]);
+    assert_eq!(delta.ops_in_interval(Interval::new(4, 6)), vec![insert_c]);
+
+    assert_eq!(
+        delta.ops_in_interval(Interval::new(2, 5)),
+        vec![Builder::insert("34").build(), Builder::insert("5").build()]
     );
 }

+ 6 - 6
rust-lib/flowy-ot/tests/op_test.rs

@@ -120,22 +120,22 @@ fn ops_merging() {
     assert_eq!(delta.ops.len(), 0);
     delta.retain(2, Attributes::Empty);
     assert_eq!(delta.ops.len(), 1);
-    assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(2).build()));
+    assert_eq!(delta.ops.last(), Some(&Builder::retain(2).build()));
     delta.retain(3, Attributes::Empty);
     assert_eq!(delta.ops.len(), 1);
-    assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(5).build()));
+    assert_eq!(delta.ops.last(), Some(&Builder::retain(5).build()));
     delta.insert("abc", Attributes::Empty);
     assert_eq!(delta.ops.len(), 2);
-    assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abc").build()));
+    assert_eq!(delta.ops.last(), Some(&Builder::insert("abc").build()));
     delta.insert("xyz", Attributes::Empty);
     assert_eq!(delta.ops.len(), 2);
-    assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abcxyz").build()));
+    assert_eq!(delta.ops.last(), Some(&Builder::insert("abcxyz").build()));
     delta.delete(1);
     assert_eq!(delta.ops.len(), 3);
-    assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(1).build()));
+    assert_eq!(delta.ops.last(), Some(&Builder::delete(1).build()));
     delta.delete(1);
     assert_eq!(delta.ops.len(), 3);
-    assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(2).build()));
+    assert_eq!(delta.ops.last(), Some(&Builder::delete(2).build()));
 }
 #[test]
 fn is_noop() {

+ 2 - 2
rust-lib/flowy-ot/tests/serde_test.rs

@@ -3,7 +3,7 @@ use flowy_ot::core::*;
 #[test]
 fn operation_insert_serialize_test() {
     let attributes = AttrsBuilder::new().bold(true).italic(true).build();
-    let operation = OpBuilder::insert("123").attributes(attributes).build();
+    let operation = Builder::insert("123").attributes(attributes).build();
     let json = serde_json::to_string(&operation).unwrap();
     eprintln!("{}", json);
 
@@ -33,7 +33,7 @@ fn delta_serialize_test() {
     let mut delta = Delta::default();
 
     let attributes = AttrsBuilder::new().bold(true).italic(true).build();
-    let retain = OpBuilder::insert("123").attributes(attributes).build();
+    let retain = Builder::insert("123").attributes(attributes).build();
 
     delta.add(retain);
     delta.add(Operation::Retain(5.into()));

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

@@ -71,3 +71,20 @@ fn delta_undo_attributes() {
     ];
     OpTester::new().run_script(ops);
 }
+
+#[test]
+fn delta_redo_attributes() {
+    let ops = vec![
+        Insert(0, "\n", 0),
+        Insert(0, "123", 0),
+        Bold(0, Interval::new(0, 3), true),
+        Undo(0),
+        AssertOpsJson(0, r#"[{"insert":"123\n"}]"#),
+        Redo(0),
+        AssertOpsJson(
+            0,
+            r#" [{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n"}]"#,
+        ),
+    ];
+    OpTester::new().run_script(ops);
+}