Browse Source

attribute insert test

appflowy 3 years ago
parent
commit
efaee88466

+ 18 - 4
rust-lib/flowy-ot/src/attributes.rs

@@ -1,3 +1,4 @@
+use crate::operation::Operation;
 use std::collections::{hash_map::RandomState, HashMap};
 
 #[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
@@ -64,11 +65,21 @@ impl AttributesBuilder {
     pub fn build(self) -> Attributes { self.inner }
 }
 
+pub fn attributes_from(operation: &Option<Operation>) -> Option<Attributes> {
+    match operation {
+        None => None,
+        Some(operation) => operation.attributes(),
+    }
+}
+
 pub fn compose_attributes(
-    a: Option<Attributes>,
-    b: Option<Attributes>,
+    op1: &Option<Operation>,
+    op2: &Option<Operation>,
     keep_empty: bool,
 ) -> Option<Attributes> {
+    let a = attributes_from(op1);
+    let b = attributes_from(op2);
+
     if a.is_none() {
         return b;
     }
@@ -93,10 +104,13 @@ pub fn compose_attributes(
 }
 
 pub fn transform_attributes(
-    a: Option<Attributes>,
-    b: Option<Attributes>,
+    op1: &Option<Operation>,
+    op2: &Option<Operation>,
     priority: bool,
 ) -> Option<Attributes> {
+    let a = attributes_from(op1);
+    let b = attributes_from(op2);
+
     if a.is_none() {
         return b;
     }

+ 56 - 30
rust-lib/flowy-ot/src/delta.rs

@@ -77,26 +77,29 @@ impl Delta {
         if s.is_empty() {
             return;
         }
-        self.target_len += num_chars(s.as_bytes());
 
+        self.target_len += num_chars(s.as_bytes());
         let new_last = match self.ops.as_mut_slice() {
             [.., Operation::Insert(s_last)] => {
-                s_last.s += &s;
-                return;
+                //
+                merge_insert_or_new_op(s_last, s, attrs)
             },
             [.., Operation::Insert(s_pre_last), Operation::Delete(_)] => {
-                s_pre_last.s += s;
-                return;
+                //
+                merge_insert_or_new_op(s_pre_last, s, attrs)
             },
             [.., op_last @ Operation::Delete(_)] => {
                 let new_last = op_last.clone();
-                *op_last = Operation::Insert(s.into());
-                new_last
+                *op_last = OpBuilder::insert(s).attributes(attrs).build();
+                Some(new_last)
             },
-            _ => Operation::Insert(s.into()),
+            _ => Some(OpBuilder::insert(s).attributes(attrs).build()),
         };
-        self.ops
-            .push(OpBuilder::new(new_last).attributes(attrs).build());
+
+        match new_last {
+            None => {},
+            Some(new_last) => self.ops.push(new_last),
+        }
     }
 
     pub fn retain(&mut self, n: u64, attrs: Option<Attributes>) {
@@ -107,8 +110,10 @@ impl Delta {
         self.target_len += n as usize;
 
         if let Some(Operation::Retain(i_last)) = self.ops.last_mut() {
-            i_last.n += n;
-            i_last.attributes = attrs;
+            match merge_retain_or_new_op(i_last, n, attrs) {
+                None => {},
+                Some(new_op) => self.ops.push(new_op),
+            }
         } else {
             self.ops
                 .push(OpBuilder::retain(n).attributes(attrs).build());
@@ -144,15 +149,14 @@ impl Delta {
                     next_op1 = ops1.next();
                 },
                 (_, Some(Operation::Insert(insert))) => {
-                    new_delta.insert(&insert.s, get_attrs(&next_op2));
+                    new_delta.insert(&insert.s, attributes_from(&next_op2));
                     next_op2 = ops2.next();
                 },
                 (None, _) | (_, None) => {
                     return Err(OTError);
                 },
                 (Some(Operation::Retain(i)), Some(Operation::Retain(j))) => {
-                    let new_attrs =
-                        compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), true);
+                    let new_attrs = compose_attributes(&next_op1, &next_op2, true);
                     match i.cmp(&j) {
                         Ordering::Less => {
                             new_delta.retain(i.n, new_attrs);
@@ -185,16 +189,17 @@ impl Delta {
                         },
                         Ordering::Greater => {
                             next_op1 = Some(
-                                OpBuilder::insert(insert.chars().skip(*j as usize).collect())
-                                    .build(),
+                                OpBuilder::insert(
+                                    &insert.chars().skip(*j as usize).collect::<String>(),
+                                )
+                                .build(),
                             );
                             next_op2 = ops2.next();
                         },
                     }
                 },
                 (Some(Operation::Insert(insert)), Some(Operation::Retain(j))) => {
-                    let new_attrs =
-                        compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), false);
+                    let new_attrs = compose_attributes(&next_op1, &next_op2, false);
                     match (insert.num_chars()).cmp(j) {
                         Ordering::Less => {
                             new_delta.insert(&insert.s, new_attrs);
@@ -210,7 +215,7 @@ impl Delta {
                             let chars = &mut insert.chars();
                             new_delta
                                 .insert(&chars.take(j.n as usize).collect::<String>(), new_attrs);
-                            next_op1 = Some(OpBuilder::insert(chars.collect()).build());
+                            next_op1 = Some(OpBuilder::insert(&chars.collect::<String>()).build());
                             next_op2 = ops2.next();
                         },
                     }
@@ -263,15 +268,13 @@ impl Delta {
             match (&next_op1, &next_op2) {
                 (None, None) => break,
                 (Some(Operation::Insert(insert)), _) => {
-                    let new_attrs =
-                        compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), true);
+                    let new_attrs = compose_attributes(&next_op1, &next_op2, true);
                     a_prime.insert(&insert.s, new_attrs.clone());
                     b_prime.retain(insert.num_chars(), new_attrs.clone());
                     next_op1 = ops1.next();
                 },
                 (_, Some(Operation::Insert(insert))) => {
-                    let new_attrs =
-                        compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), true);
+                    let new_attrs = compose_attributes(&next_op1, &next_op2, true);
                     a_prime.retain(insert.num_chars(), new_attrs.clone());
                     b_prime.insert(&insert.s, new_attrs.clone());
                     next_op2 = ops2.next();
@@ -283,8 +286,7 @@ impl Delta {
                     return Err(OTError);
                 },
                 (Some(Operation::Retain(i)), Some(Operation::Retain(j))) => {
-                    let new_attrs =
-                        compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), true);
+                    let new_attrs = compose_attributes(&next_op1, &next_op2, true);
                     match i.cmp(&j) {
                         Ordering::Less => {
                             a_prime.retain(i.n, new_attrs.clone());
@@ -449,9 +451,33 @@ impl Delta {
     pub fn is_empty(&self) -> bool { self.ops.is_empty() }
 }
 
-pub fn get_attrs(operation: &Option<Operation>) -> Option<Attributes> {
-    match operation {
-        None => None,
-        Some(operation) => operation.attributes(),
+fn merge_insert_or_new_op(
+    insert: &mut Insert,
+    s: &str,
+    attributes: Option<Attributes>,
+) -> Option<Operation> {
+    if insert.attributes == attributes {
+        insert.s += s;
+        None
+    } else {
+        Some(OpBuilder::insert(s).attributes(attributes).build())
+    }
+}
+
+fn merge_retain_or_new_op(
+    retain: &mut Retain,
+    n: u64,
+    attributes: Option<Attributes>,
+) -> Option<Operation> {
+    if attributes.is_none() {
+        retain.n += n;
+        return None;
+    }
+
+    if retain.attributes == attributes {
+        retain.n += n;
+        None
+    } else {
+        Some(OpBuilder::retain(n).attributes(attributes).build())
     }
 }

+ 2 - 1
rust-lib/flowy-ot/src/operation.rs

@@ -58,6 +58,7 @@ impl Operation {
             Operation::Insert(i) => i.num_chars(),
         }
     }
+    pub fn is_empty(&self) -> bool { self.length() == 0 }
 }
 
 pub struct OpBuilder {
@@ -72,7 +73,7 @@ impl OpBuilder {
 
     pub fn delete(n: u64) -> OpBuilder { OpBuilder::new(Operation::Delete(n)) }
 
-    pub fn insert(s: String) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) }
+    pub fn insert(s: &str) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) }
 
     pub fn attributes(mut self, attrs: Option<Attributes>) -> OpBuilder {
         self.attrs = attrs;

+ 16 - 0
rust-lib/flowy-ot/tests/attribute_test.rs

@@ -0,0 +1,16 @@
+use flowy_ot::{
+    attributes::{Attributes, AttributesBuilder},
+    delta::Delta,
+    operation::{OpBuilder, Operation, Retain},
+};
+
+#[test]
+fn attribute_insert_merge_test() {
+    let mut delta = Delta::default();
+    delta.insert("123", Some(AttributesBuilder::new().bold().build()));
+    delta.insert("456", Some(AttributesBuilder::new().bold().build()));
+    assert_eq!(
+        r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#,
+        serde_json::to_string(&delta).unwrap()
+    )
+}

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

@@ -1,2 +0,0 @@
-mod helper;
-mod serde_test;

+ 5 - 9
rust-lib/flowy-ot/tests/op_test.rs

@@ -1,10 +1,12 @@
-use crate::helper::Rng;
+pub mod helper;
+
 use bytecount::num_chars;
 use flowy_ot::{
     attributes::*,
     delta::Delta,
     operation::{OpBuilder, Operation},
 };
+use helper::*;
 
 #[test]
 fn lengths() {
@@ -126,16 +128,10 @@ fn ops_merging() {
     assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(5).build()));
     delta.insert("abc", None);
     assert_eq!(delta.ops.len(), 2);
-    assert_eq!(
-        delta.ops.last(),
-        Some(&OpBuilder::insert("abc".to_owned()).build())
-    );
+    assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abc").build()));
     delta.insert("xyz", None);
     assert_eq!(delta.ops.len(), 2);
-    assert_eq!(
-        delta.ops.last(),
-        Some(&OpBuilder::insert("abcxyz".to_owned()).build())
-    );
+    assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abcxyz").build()));
     delta.delete(1);
     assert_eq!(delta.ops.len(), 3);
     assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(1).build()));

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

@@ -7,7 +7,7 @@ use flowy_ot::{
 #[test]
 fn operation_insert_serialize_test() {
     let attributes = AttributesBuilder::new().bold().italic().build();
-    let operation = OpBuilder::insert("123".to_owned())
+    let operation = OpBuilder::insert("123")
         .attributes(Some(attributes))
         .build();
     let json = serde_json::to_string(&operation).unwrap();
@@ -39,7 +39,7 @@ fn delta_serialize_test() {
     let mut delta = Delta::default();
 
     let attributes = AttributesBuilder::new().bold().italic().build();
-    let retain = OpBuilder::insert("123".to_owned())
+    let retain = OpBuilder::insert("123")
         .attributes(Some(attributes))
         .build();