Преглед на файлове

attribute: follow, empty, custom

appflowy преди 3 години
родител
ревизия
3770c648c3

+ 133 - 84
rust-lib/flowy-ot/src/attributes.rs

@@ -1,58 +1,97 @@
 use crate::operation::Operation;
-use std::collections::{hash_map::RandomState, HashMap};
+use std::collections::HashMap;
+
+const PLAIN: &'static str = "";
+fn is_plain(s: &str) -> bool { s == PLAIN }
+
+#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
+#[serde(untagged)]
+pub enum Attributes {
+    #[serde(skip)]
+    Follow,
+    Custom(AttributesData),
+    #[serde(skip)]
+    Empty,
+}
+
+impl Attributes {
+    pub fn merge(&self, other: Option<Attributes>) -> Attributes {
+        let other = other.unwrap_or(Attributes::Empty);
+        match (self, &other) {
+            (Attributes::Custom(data), Attributes::Custom(o_data)) => {
+                let mut data = data.clone();
+                data.extend(o_data.clone());
+                Attributes::Custom(data)
+            },
+            _ => other,
+        }
+    }
+    // remove attribute if the value is PLAIN
+    pub fn remove_plain(&mut self) {
+        match self {
+            Attributes::Follow => {},
+            Attributes::Custom(data) => {
+                data.remove_plain();
+            },
+            Attributes::Empty => {},
+        }
+    }
+}
+
+impl std::default::Default for Attributes {
+    fn default() -> Self { Attributes::Empty }
+}
 
 #[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
-pub struct Attributes {
+pub struct AttributesData {
     #[serde(skip_serializing_if = "HashMap::is_empty")]
     #[serde(flatten)]
     inner: HashMap<String, String>,
 }
 
-impl Attributes {
+impl AttributesData {
     pub fn new() -> Self {
-        Attributes {
+        AttributesData {
             inner: HashMap::new(),
         }
     }
 
-    pub fn remove_empty(&mut self) { self.inner.retain(|_, v| v.is_empty() == false); }
+    pub fn remove_plain(&mut self) { self.inner.retain(|_, v| !is_plain(v)); }
 
-    pub fn extend(&mut self, other: Attributes) { self.inner.extend(other.inner); }
+    pub fn extend(&mut self, other: AttributesData) { self.inner.extend(other.inner); }
 
-    pub fn is_empty(&self) -> bool { self.inner.is_empty() }
+    pub fn is_plain(&self) -> bool { self.inner.values().filter(|v| !is_plain(v)).count() == 0 }
 }
 
-impl std::convert::From<HashMap<String, String>> for Attributes {
-    fn from(attributes: HashMap<String, String, RandomState>) -> Self {
-        Attributes { inner: attributes }
-    }
+impl std::convert::From<HashMap<String, String>> for AttributesData {
+    fn from(attributes: HashMap<String, String>) -> Self { AttributesData { inner: attributes } }
 }
 
-impl std::ops::Deref for Attributes {
+impl std::ops::Deref for AttributesData {
     type Target = HashMap<String, String>;
 
     fn deref(&self) -> &Self::Target { &self.inner }
 }
 
-impl std::ops::DerefMut for Attributes {
+impl std::ops::DerefMut for AttributesData {
     fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
 }
 
 pub struct AttrsBuilder {
-    inner: Attributes,
+    inner: AttributesData,
 }
 
 impl AttrsBuilder {
     pub fn new() -> Self {
         Self {
-            inner: Attributes::default(),
+            inner: AttributesData::default(),
         }
     }
 
     pub fn bold(mut self, bold: bool) -> Self {
         let val = match bold {
             true => "true",
-            false => "",
+            false => PLAIN,
         };
         self.inner.insert("bold".to_owned(), val.to_owned());
         self
@@ -61,7 +100,7 @@ impl AttrsBuilder {
     pub fn italic(mut self, italic: bool) -> Self {
         let val = match italic {
             true => "true",
-            false => "",
+            false => PLAIN,
         };
         self.inner.insert("italic".to_owned(), val.to_owned());
         self
@@ -72,98 +111,108 @@ impl AttrsBuilder {
         self
     }
 
-    pub fn build(self) -> Attributes { self.inner }
+    pub fn build(self) -> Attributes { Attributes::Custom(self.inner) }
 }
 
 pub fn attributes_from(operation: &Option<Operation>) -> Option<Attributes> {
     match operation {
         None => None,
-        Some(operation) => operation.get_attributes(),
+        Some(operation) => Some(operation.get_attributes()),
     }
 }
 
-pub fn compose_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() && b.is_none() {
-        return None;
+pub fn compose_attributes(left: &Option<Operation>, right: &Option<Operation>) -> Attributes {
+    if left.is_none() && right.is_none() {
+        return Attributes::Empty;
     }
+    let attr_l = attributes_from(left);
+    let attr_r = attributes_from(right);
+    log::trace!("compose_attributes: a: {:?}, b: {:?}", attr_l, attr_r);
 
-    let mut attrs_a = a.unwrap_or(Attributes::default());
-    let attrs_b = b.unwrap_or(Attributes::default());
-
-    log::trace!(
-        "before compose_attributes: a: {:?}, b: {:?}",
-        attrs_a,
-        attrs_b
-    );
-    attrs_a.extend(attrs_b);
-    log::trace!("after compose_attributes: a: {:?}", attrs_a);
-    if !keep_empty {
-        attrs_a.remove_empty()
-    }
+    let mut attr = match (&attr_l, &attr_r) {
+        (_, Some(Attributes::Custom(_))) => match &attr_l {
+            None => attr_r.unwrap(),
+            Some(_) => attr_l.unwrap().merge(attr_r.clone()),
+        },
+        (Some(Attributes::Custom(_)), _) => attr_l.unwrap().merge(attr_r),
+        _ => Attributes::Empty,
+    };
+
+    log::trace!("composed_attributes: a: {:?}", attr);
 
-    Some(attrs_a)
+    // remove the attribute if the value is PLAIN
+    attr.remove_plain();
+
+    attr
 }
 
 pub fn transform_attributes(
-    op1: &Option<Operation>,
-    op2: &Option<Operation>,
+    left: &Option<Operation>,
+    right: &Option<Operation>,
     priority: bool,
-) -> Option<Attributes> {
-    let a = attributes_from(op1);
-    let b = attributes_from(op2);
+) -> Attributes {
+    let attr_l = attributes_from(left);
+    let attr_r = attributes_from(right);
 
-    if a.is_none() {
-        return b;
-    }
+    if attr_l.is_none() {
+        if attr_r.is_none() {
+            return Attributes::Empty;
+        }
 
-    if b.is_none() {
-        return None;
+        return match attr_r.as_ref().unwrap() {
+            Attributes::Follow => Attributes::Empty,
+            Attributes::Custom(_) => attr_r.unwrap(),
+            Attributes::Empty => Attributes::Empty,
+        };
     }
 
     if !priority {
-        return b;
+        return attr_r.unwrap();
     }
 
-    let attrs_a = a.unwrap_or(Attributes::default());
-    let attrs_b = b.unwrap_or(Attributes::default());
-
-    let result = attrs_b
-        .iter()
-        .fold(Attributes::new(), |mut attributes, (k, v)| {
-            if attrs_a.contains_key(k) == false {
-                attributes.insert(k.clone(), v.clone());
-            }
-            attributes
-        });
-    Some(result)
+    match (attr_l.unwrap(), attr_r.unwrap()) {
+        (Attributes::Custom(attr_data_l), Attributes::Custom(attr_data_r)) => {
+            let result = transform_attribute_data(attr_data_l, attr_data_r);
+            Attributes::Custom(result)
+        },
+        _ => Attributes::Empty,
+    }
 }
 
-pub fn invert_attributes(attr: Option<Attributes>, base: Option<Attributes>) -> Attributes {
-    let attr = attr.unwrap_or(Attributes::new());
-    let base = base.unwrap_or(Attributes::new());
-
-    let base_inverted = base
+fn transform_attribute_data(left: AttributesData, right: AttributesData) -> AttributesData {
+    let result = right
         .iter()
-        .fold(Attributes::new(), |mut attributes, (k, v)| {
-            if base.get(k) != attr.get(k) && attr.contains_key(k) {
-                attributes.insert(k.clone(), v.clone());
+        .fold(AttributesData::new(), |mut new_attr_data, (k, v)| {
+            if !left.contains_key(k) {
+                new_attr_data.insert(k.clone(), v.clone());
             }
-            attributes
+            new_attr_data
         });
-
-    let inverted = attr.iter().fold(base_inverted, |mut attributes, (k, _)| {
-        if base.get(k) != attr.get(k) && !base.contains_key(k) {
-            attributes.insert(k.clone(), "".to_owned());
-        }
-        attributes
-    });
-
-    return inverted;
+    result
 }
+
+// pub fn invert_attributes(
+//     attr: Option<AttributesData>,
+//     base: Option<AttributesData>,
+// ) -> AttributesData {
+//     let attr = attr.unwrap_or(AttributesData::new());
+//     let base = base.unwrap_or(AttributesData::new());
+//
+//     let base_inverted = base
+//         .iter()
+//         .fold(AttributesData::new(), |mut attributes, (k, v)| {
+//             if base.get(k) != attr.get(k) && attr.contains_key(k) {
+//                 attributes.insert(k.clone(), v.clone());
+//             }
+//             attributes
+//         });
+//
+//     let inverted = attr.iter().fold(base_inverted, |mut attributes, (k, _)| {
+//         if base.get(k) != attr.get(k) && !base.contains_key(k) {
+//             attributes.insert(k.clone(), "".to_owned());
+//         }
+//         attributes
+//     });
+//
+//     return inverted;
+// }

+ 46 - 32
rust-lib/flowy-ot/src/delta.rs

@@ -1,6 +1,6 @@
 use crate::{attributes::*, errors::OTError, operation::*};
 use bytecount::num_chars;
-use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator, str::FromStr};
+use std::{cmp::Ordering, iter::FromIterator, str::FromStr};
 
 #[derive(Clone, Debug, PartialEq)]
 pub struct Delta {
@@ -73,7 +73,7 @@ impl Delta {
         }
     }
 
-    pub fn insert(&mut self, s: &str, attrs: Option<Attributes>) {
+    pub fn insert(&mut self, s: &str, attrs: Attributes) {
         if s.is_empty() {
             return;
         }
@@ -102,7 +102,7 @@ impl Delta {
         }
     }
 
-    pub fn retain(&mut self, n: u64, attrs: Option<Attributes>) {
+    pub fn retain(&mut self, n: u64, attrs: Attributes) {
         if n == 0 {
             return;
         }
@@ -149,14 +149,17 @@ impl Delta {
                     next_op1 = ops1.next();
                 },
                 (_, Some(Operation::Insert(o_insert))) => {
-                    new_delta.insert(&o_insert.s, attributes_from(&next_op2));
+                    new_delta.insert(
+                        &o_insert.s,
+                        attributes_from(&next_op2).unwrap_or(Attributes::Empty),
+                    );
                     next_op2 = ops2.next();
                 },
                 (None, _) | (_, None) => {
                     return Err(OTError);
                 },
                 (Some(Operation::Retain(retain)), Some(Operation::Retain(o_retain))) => {
-                    let composed_attrs = compose_attributes(&next_op1, &next_op2, false);
+                    let composed_attrs = compose_attributes(&next_op1, &next_op2);
                     log::debug!(
                         "[retain:{} - retain:{}]: {:?}",
                         retain.num,
@@ -186,6 +189,7 @@ impl Delta {
                         Ordering::Less => {
                             next_op2 = Some(
                                 OpBuilder::delete(*o_num - num_chars(insert.as_bytes()) as u64)
+                                    .attributes(insert.attributes.clone())
                                     .build(),
                             );
                             next_op1 = ops1.next();
@@ -206,7 +210,7 @@ impl Delta {
                     }
                 },
                 (Some(Operation::Insert(insert)), Some(Operation::Retain(o_retain))) => {
-                    let composed_attrs = compose_attributes(&next_op1, &next_op2, false);
+                    let composed_attrs = compose_attributes(&next_op1, &next_op2);
                     log::debug!(
                         "[insert:{} - retain:{}]: {:?}",
                         insert.s,
@@ -234,7 +238,11 @@ impl Delta {
                                 &chars.take(o_retain.num as usize).collect::<String>(),
                                 composed_attrs,
                             );
-                            next_op1 = Some(OpBuilder::insert(&chars.collect::<String>()).build());
+                            next_op1 = Some(
+                                OpBuilder::insert(&chars.collect::<String>())
+                                    .attributes(Attributes::Empty)
+                                    .build(),
+                            );
                             next_op2 = ops2.next();
                         },
                     }
@@ -297,7 +305,7 @@ impl Delta {
                 (_, Some(Operation::Insert(o_insert))) => {
                     let composed_attrs = transform_attributes(&next_op1, &next_op2, true);
                     a_prime.retain(o_insert.num_chars(), composed_attrs.clone());
-                    b_prime.insert(&o_insert.s, composed_attrs.clone());
+                    b_prime.insert(&o_insert.s, composed_attrs);
                     next_op2 = ops2.next();
                 },
                 (None, _) => {
@@ -427,7 +435,7 @@ impl Delta {
         for op in &self.ops {
             match &op {
                 Operation::Retain(retain) => {
-                    inverted.retain(retain.num, None);
+                    inverted.retain(retain.num, Attributes::Follow);
                     for _ in 0..retain.num {
                         chars.next();
                     }
@@ -475,41 +483,47 @@ impl Delta {
 fn merge_insert_or_new_op(
     insert: &mut Insert,
     s: &str,
-    attributes: Option<Attributes>,
+    attributes: Attributes,
 ) -> Option<Operation> {
-    if attributes.is_none() {
-        insert.s += s;
-        return None;
-    }
-
-    if insert.attributes == attributes {
-        insert.s += s;
-        None
-    } else {
-        Some(OpBuilder::insert(s).attributes(attributes).build())
+    match &attributes {
+        Attributes::Follow => {
+            insert.s += s;
+            return None;
+        },
+        Attributes::Custom(_) | Attributes::Empty => {
+            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>,
+    attributes: Attributes,
 ) -> Option<Operation> {
-    log::trace!(
+    log::debug!(
         "merge_retain_or_new_op: {:?}, {:?}",
         retain.attributes,
         attributes
     );
 
-    if attributes.is_none() {
-        retain.num += n;
-        return None;
-    }
-
-    if retain.attributes == attributes {
-        retain.num += n;
-        None
-    } else {
-        Some(OpBuilder::retain(n).attributes(attributes).build())
+    match &attributes {
+        Attributes::Follow => {
+            retain.num += n;
+            None
+        },
+        Attributes::Custom(_) | Attributes::Empty => {
+            if retain.attributes == attributes {
+                retain.num += n;
+                None
+            } else {
+                Some(OpBuilder::retain(n).attributes(attributes).build())
+            }
+        },
     }
 }

+ 1 - 1
rust-lib/flowy-ot/src/interval.rs

@@ -1,7 +1,7 @@
 use std::{
     cmp::{max, min},
     fmt,
-    ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
+    ops::{Range, RangeInclusive, RangeTo, RangeToInclusive},
 };
 
 /// Representing a closed-open range;

+ 34 - 31
rust-lib/flowy-ot/src/operation.rs

@@ -1,8 +1,6 @@
 use crate::attributes::Attributes;
 use bytecount::num_chars;
 use std::{
-    cmp::Ordering,
-    collections::{hash_map::RandomState, HashMap},
     ops::{Deref, DerefMut},
     str::Chars,
 };
@@ -29,17 +27,19 @@ impl Operation {
         }
     }
 
-    pub fn get_attributes(&self) -> Option<Attributes> {
+    pub fn get_attributes(&self) -> Attributes {
         match self {
-            Operation::Delete(_) => None,
+            Operation::Delete(_) => Attributes::Empty,
             Operation::Retain(retain) => retain.attributes.clone(),
             Operation::Insert(insert) => insert.attributes.clone(),
         }
     }
 
-    pub fn set_attributes(&mut self, attributes: Option<Attributes>) {
+    pub fn set_attributes(&mut self, attributes: Attributes) {
         match self {
-            Operation::Delete(_) => {},
+            Operation::Delete(_) => {
+                log::error!("Delete should not contains attributes");
+            },
             Operation::Retain(retain) => {
                 retain.attributes = attributes;
             },
@@ -49,7 +49,13 @@ impl Operation {
         }
     }
 
-    pub fn is_plain(&self) -> bool { self.get_attributes().is_none() }
+    pub fn is_plain(&self) -> bool {
+        match self.get_attributes() {
+            Attributes::Follow => true,
+            Attributes::Custom(_) => false,
+            Attributes::Empty => true,
+        }
+    }
 
     pub fn length(&self) -> u64 {
         match self {
@@ -63,11 +69,16 @@ impl Operation {
 
 pub struct OpBuilder {
     ty: Operation,
-    attrs: Option<Attributes>,
+    attrs: Attributes,
 }
 
 impl OpBuilder {
-    pub fn new(ty: Operation) -> OpBuilder { OpBuilder { ty, attrs: None } }
+    pub fn new(ty: Operation) -> OpBuilder {
+        OpBuilder {
+            ty,
+            attrs: Attributes::Empty,
+        }
+    }
 
     pub fn retain(n: u64) -> OpBuilder { OpBuilder::new(Operation::Retain(n.into())) }
 
@@ -75,15 +86,8 @@ impl OpBuilder {
 
     pub fn insert(s: &str) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) }
 
-    pub fn attributes(mut self, attrs: Option<Attributes>) -> OpBuilder {
-        match attrs {
-            None => self.attrs = attrs,
-            Some(attrs) => match attrs.is_empty() {
-                true => self.attrs = None,
-                false => self.attrs = Some(attrs),
-            },
-        }
-
+    pub fn attributes(mut self, attrs: Attributes) -> OpBuilder {
+        self.attrs = attrs;
         self
     }
 
@@ -103,14 +107,14 @@ pub struct Retain {
     #[serde(rename(serialize = "retain", deserialize = "retain"))]
     pub num: u64,
     #[serde(skip_serializing_if = "is_empty")]
-    pub attributes: Option<Attributes>,
+    pub attributes: Attributes,
 }
 
 impl std::convert::From<u64> for Retain {
     fn from(n: u64) -> Self {
         Retain {
             num: n,
-            attributes: None,
+            attributes: Attributes::default(),
         }
     }
 }
@@ -131,7 +135,7 @@ pub struct Insert {
     pub s: String,
 
     #[serde(skip_serializing_if = "is_empty")]
-    pub attributes: Option<Attributes>,
+    pub attributes: Attributes,
 }
 
 impl Insert {
@@ -146,23 +150,22 @@ impl std::convert::From<String> for Insert {
     fn from(s: String) -> Self {
         Insert {
             s,
-            attributes: None,
+            attributes: Attributes::default(),
         }
     }
 }
 
 impl std::convert::From<&str> for Insert {
-    fn from(s: &str) -> Self {
-        Insert {
-            s: s.to_owned(),
-            attributes: None,
-        }
-    }
+    fn from(s: &str) -> Self { Insert::from(s.to_owned()) }
 }
 
-fn is_empty(attributes: &Option<Attributes>) -> bool {
+fn is_empty(attributes: &Attributes) -> bool {
     match attributes {
-        None => true,
-        Some(attributes) => attributes.is_empty(),
+        Attributes::Follow => true,
+        Attributes::Custom(data) => {
+            let is_empty = data.is_plain();
+            is_empty
+        },
+        Attributes::Empty => true,
     }
 }

+ 2 - 2
rust-lib/flowy-ot/src/operation_serde.rs

@@ -8,7 +8,7 @@ use serde::{
     Serialize,
     Serializer,
 };
-use std::{collections::HashMap, fmt};
+use std::fmt;
 
 impl Serialize for Operation {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -82,7 +82,7 @@ impl<'de> Deserialize<'de> for Operation {
                 match operation {
                     None => Err(de::Error::missing_field("operation")),
                     Some(mut operation) => {
-                        operation.set_attributes(attributes);
+                        operation.set_attributes(attributes.unwrap_or(Attributes::Empty));
                         Ok(operation)
                     },
                 }

+ 94 - 24
rust-lib/flowy-ot/tests/attribute_test.rs

@@ -1,18 +1,13 @@
 pub mod helper;
 
-use crate::{
-    helper::{MergeTestOp::*, *},
-    MergeTestOp::*,
-};
+use crate::helper::{MergeTestOp::*, *};
 use flowy_ot::{
-    attributes::{Attributes, AttrsBuilder},
-    delta::Delta,
     interval::Interval,
     operation::{OpBuilder, Operation, Retain},
 };
 
 #[test]
-fn delta_add_bold_attr1() {
+fn delta_add_bold_and_invert_all() {
     let ops = vec![
         Insert(0, "123"),
         Bold(0, Interval::new(0, 3), true),
@@ -24,7 +19,7 @@ fn delta_add_bold_attr1() {
 }
 
 #[test]
-fn delta_add_bold_attr2() {
+fn delta_add_bold_and_invert_partial_suffix() {
     let ops = vec![
         Insert(0, "1234"),
         Bold(0, Interval::new(0, 4), true),
@@ -39,57 +34,82 @@ fn delta_add_bold_attr2() {
 }
 
 #[test]
-fn delta_add_bold_attr3() {
+fn delta_add_bold_and_invert_partial_suffix2() {
     let ops = vec![
         Insert(0, "1234"),
         Bold(0, Interval::new(0, 4), true),
         AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
-        Bold(0, Interval::new(0, 2), false),
+        Bold(0, Interval::new(2, 4), false),
         AssertOpsJson(
             0,
-            r#"[{"insert":"12"},{"insert":"34","attributes":{"bold":"true"}}]"#,
+            r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#,
         ),
+        Bold(0, Interval::new(2, 4), true),
+        AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
     ];
     MergeTest::new().run_script(ops);
 }
 
 #[test]
-fn delta_add_bold_italic() {
+fn delta_add_bold_and_invert_partial_prefix() {
     let ops = vec![
         Insert(0, "1234"),
         Bold(0, Interval::new(0, 4), true),
-        Italic(0, Interval::new(0, 4), true),
+        AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
+        Bold(0, Interval::new(0, 2), false),
         AssertOpsJson(
             0,
-            r#"[{"insert":"1234","attributes":{"italic":"true","bold":"true"}}]"#,
+            r#"[{"insert":"12"},{"insert":"34","attributes":{"bold":"true"}}]"#,
         ),
-        Insert(0, "5678"),
+    ];
+    MergeTest::new().run_script(ops);
+}
+
+#[test]
+fn delta_add_bold_consecutive() {
+    let ops = vec![
+        Insert(0, "1234"),
+        Bold(0, Interval::new(0, 1), true),
         AssertOpsJson(
             0,
-            r#"[{"insert":"12345678","attributes":{"italic":"true","bold":"true"}}]"#,
+            r#"[{"insert":"1","attributes":{"bold":"true"}},{"insert":"234"}]"#,
         ),
-        Italic(0, Interval::new(4, 6), false),
+        Bold(0, Interval::new(1, 2), true),
         AssertOpsJson(
             0,
-            r#"[{"insert":"1234","attributes":{"italic":"true","bold":"true"}},{"insert":"56"},{"insert":"78","attributes":{"bold":"true","italic":"true"}}]"#,
+            r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#,
         ),
     ];
     MergeTest::new().run_script(ops);
 }
 
 #[test]
-fn delta_add_bold_attr_and_invert() {
+#[should_panic]
+fn delta_add_bold_empty_str() {
+    let ops = vec![Bold(0, Interval::new(0, 4), true)];
+    MergeTest::new().run_script(ops);
+}
+
+#[test]
+fn delta_add_bold_italic() {
     let ops = vec![
         Insert(0, "1234"),
         Bold(0, Interval::new(0, 4), true),
-        AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
-        Bold(0, Interval::new(2, 4), false),
+        Italic(0, Interval::new(0, 4), true),
         AssertOpsJson(
             0,
-            r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#,
+            r#"[{"insert":"1234","attributes":{"italic":"true","bold":"true"}}]"#,
+        ),
+        Insert(0, "5678"),
+        AssertOpsJson(
+            0,
+            r#"[{"insert":"12345678","attributes":{"italic":"true","bold":"true"}}]"#,
+        ),
+        Italic(0, Interval::new(4, 6), false),
+        AssertOpsJson(
+            0,
+            r#"[{"insert":"1234","attributes":{"italic":"true","bold":"true"}},{"insert":"56"},{"insert":"78","attributes":{"bold":"true","italic":"true"}}]"#,
         ),
-        Bold(0, Interval::new(2, 4), true),
-        AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
     ];
     MergeTest::new().run_script(ops);
 }
@@ -134,3 +154,53 @@ fn delta_compose_attr_delta_with_attr_delta_test() {
 
     MergeTest::new().run_script(ops);
 }
+
+#[test]
+fn delta_delete_heading() {
+    let ops = vec![
+        InsertBold(0, "123456", Interval::new(0, 6)),
+        AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
+        Delete(0, Interval::new(0, 2)),
+        AssertOpsJson(0, r#"[{"insert":"3456","attributes":{"bold":"true"}}]"#),
+    ];
+
+    MergeTest::new().run_script(ops);
+}
+
+#[test]
+fn delta_delete_trailing() {
+    let ops = vec![
+        InsertBold(0, "123456", Interval::new(0, 6)),
+        AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
+        Delete(0, Interval::new(5, 6)),
+        AssertOpsJson(0, r#"[{"insert":"12345","attributes":{"bold":"true"}}]"#),
+    ];
+
+    MergeTest::new().run_script(ops);
+}
+
+#[test]
+fn delta_delete_middle() {
+    let ops = vec![
+        InsertBold(0, "123456", Interval::new(0, 6)),
+        AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
+        Delete(0, Interval::new(0, 2)),
+        AssertOpsJson(0, r#"[{"insert":"3456","attributes":{"bold":"true"}}]"#),
+        Delete(0, Interval::new(2, 4)),
+        AssertOpsJson(0, r#"[{"insert":"34","attributes":{"bold":"true"}}]"#),
+    ];
+
+    MergeTest::new().run_script(ops);
+}
+
+#[test]
+fn delta_delete_all() {
+    let ops = vec![
+        InsertBold(0, "123456", Interval::new(0, 6)),
+        AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
+        Delete(0, Interval::new(0, 6)),
+        AssertOpsJson(0, r#"[]"#),
+    ];
+
+    MergeTest::new().run_script(ops);
+}

+ 109 - 77
rust-lib/flowy-ot/tests/helper/mod.rs

@@ -1,5 +1,5 @@
 use flowy_ot::{
-    attributes::{Attributes, AttrsBuilder},
+    attributes::{Attributes, AttributesData, AttrsBuilder},
     delta::Delta,
     interval::Interval,
     operation::{OpBuilder, Operation},
@@ -7,50 +7,6 @@ use flowy_ot::{
 use rand::{prelude::*, Rng as WrappedRng};
 use std::sync::Once;
 
-pub struct Rng(StdRng);
-
-impl Default for Rng {
-    fn default() -> Self { Rng(StdRng::from_rng(thread_rng()).unwrap()) }
-}
-
-impl Rng {
-    pub fn from_seed(seed: [u8; 32]) -> Self { Rng(StdRng::from_seed(seed)) }
-
-    pub fn gen_string(&mut self, len: usize) -> String {
-        (0..len).map(|_| self.0.gen::<char>()).collect()
-    }
-
-    pub fn gen_delta(&mut self, s: &str) -> Delta {
-        let mut delta = Delta::default();
-        loop {
-            let left = s.chars().count() - delta.base_len();
-            if left == 0 {
-                break;
-            }
-            let i = if left == 1 {
-                1
-            } else {
-                1 + self.0.gen_range(0, std::cmp::min(left - 1, 20))
-            };
-            match self.0.gen_range(0.0, 1.0) {
-                f if f < 0.2 => {
-                    delta.insert(&self.gen_string(i), None);
-                },
-                f if f < 0.4 => {
-                    delta.delete(i as u64);
-                },
-                _ => {
-                    delta.retain(i as u64, None);
-                },
-            }
-        }
-        if self.0.gen_range(0.0, 1.0) < 0.3 {
-            delta.insert(&("1".to_owned() + &self.gen_string(10)), None);
-        }
-        delta
-    }
-}
-
 #[derive(Clone, Debug)]
 pub enum MergeTestOp {
     Insert(usize, &'static str),
@@ -58,6 +14,7 @@ pub enum MergeTestOp {
     InsertBold(usize, &'static str, Interval),
     // delta_i, start, length, enable
     Bold(usize, Interval, bool),
+    Delete(usize, Interval),
     Italic(usize, Interval, bool),
     Transform(usize, usize),
     AssertStr(usize, &'static str),
@@ -88,26 +45,31 @@ impl MergeTest {
         match op {
             MergeTestOp::Insert(delta_i, s) => {
                 let delta = &mut self.deltas[*delta_i];
-                delta.insert(s, None);
+                delta.insert(s, Attributes::Follow);
             },
-            MergeTestOp::InsertBold(delta_i, s, interval) => {
+            MergeTestOp::Delete(delta_i, interval) => {
+                //
+                self.update_delta_with_delete(*delta_i, interval);
+            },
+            MergeTestOp::InsertBold(delta_i, s, _interval) => {
                 let attrs = AttrsBuilder::new().bold(true).build();
                 let delta = &mut self.deltas[*delta_i];
-                delta.insert(s, Some(attrs));
+                delta.insert(s, attrs);
             },
             MergeTestOp::Bold(delta_i, interval, enable) => {
                 let attrs = AttrsBuilder::new().bold(*enable).build();
-                self.replace_delta(*delta_i, attrs, interval);
+                self.update_delta_with_attribute(*delta_i, attrs, interval);
             },
             MergeTestOp::Italic(delta_i, interval, enable) => {
                 let attrs = AttrsBuilder::new().italic(*enable).build();
-                self.replace_delta(*delta_i, attrs, interval);
+                self.update_delta_with_attribute(*delta_i, attrs, interval);
             },
             MergeTestOp::Transform(delta_a_i, delta_b_i) => {
                 let delta_a = &self.deltas[*delta_a_i];
                 let delta_b = &self.deltas[*delta_b_i];
 
                 let (a_prime, b_prime) = delta_a.transform(delta_b).unwrap();
+                log::trace!("a:{:?},b:{:?}", a_prime, b_prime);
                 let new_delta_a = delta_a.compose(&b_prime).unwrap();
                 let new_delta_b = delta_b.compose(&a_prime).unwrap();
 
@@ -120,54 +82,62 @@ impl MergeTest {
             },
 
             MergeTestOp::AssertOpsJson(delta_i, expected) => {
-                let expected_delta: Delta = serde_json::from_str(expected).unwrap();
-
                 let delta_i_json = serde_json::to_string(&self.deltas[*delta_i]).unwrap();
-                let delta: Delta = serde_json::from_str(&delta_i_json).unwrap();
 
-                if expected_delta != delta {
+                let expected_delta: Delta = serde_json::from_str(expected).unwrap();
+                let target_delta: Delta = serde_json::from_str(&delta_i_json).unwrap();
+
+                if expected_delta != target_delta {
                     log::error!("✅ {}", expected);
                     log::error!("❌ {}", delta_i_json);
                 }
-                assert_eq!(delta, expected_delta);
+                assert_eq!(target_delta, expected_delta);
             },
         }
     }
 
     pub fn run_script(&mut self, script: Vec<MergeTestOp>) {
-        for (i, op) in script.iter().enumerate() {
+        for (_i, op) in script.iter().enumerate() {
             self.run_op(op);
         }
     }
 
-    pub fn replace_delta(
+    pub fn update_delta_with_attribute(
         &mut self,
         delta_index: usize,
         attributes: Attributes,
         interval: &Interval,
     ) {
         let old_delta = &self.deltas[delta_index];
-        let new_delta = delta_with_attribute(old_delta, attributes, interval);
+        let retain = OpBuilder::retain(interval.size() as u64)
+            .attributes(attributes)
+            .build();
+        let new_delta = make_delta_with_op(old_delta, retain, interval);
         self.deltas[delta_index] = new_delta;
     }
-}
 
-pub fn delta_with_attribute(delta: &Delta, attributes: Attributes, interval: &Interval) -> Delta {
-    let delta_interval = Interval::new(0, delta.target_len);
+    pub fn update_delta_with_delete(&mut self, delta_index: usize, interval: &Interval) {
+        let old_delta = &self.deltas[delta_index];
+        let delete = OpBuilder::delete(interval.size() as u64).build();
+        let new_delta = make_delta_with_op(old_delta, delete, interval);
+        self.deltas[delta_index] = new_delta;
+    }
+}
 
+pub fn make_delta_with_op(delta: &Delta, op: Operation, interval: &Interval) -> Delta {
     let mut new_delta = Delta::default();
-    let prefix = delta_interval.prefix(*interval);
+    let (prefix, suffix) = length_split_with_interval(delta.target_len, interval);
+
+    // prefix
     if prefix.is_empty() == false && prefix != *interval {
         let size = prefix.size();
         let attrs = attributes_in_interval(delta, &prefix);
         new_delta.retain(size as u64, attrs);
     }
 
-    let size = interval.size();
-    log::debug!("Apply attribute {:?} to {}", attributes, interval);
-    new_delta.retain(size as u64, Some(attributes));
+    new_delta.add(op);
 
-    let suffix = delta_interval.suffix(*interval);
+    // suffix
     if suffix.is_empty() == false {
         let size = suffix.size();
         let attrs = attributes_in_interval(delta, &suffix);
@@ -177,31 +147,93 @@ pub fn delta_with_attribute(delta: &Delta, attributes: Attributes, interval: &In
     delta.compose(&new_delta).unwrap()
 }
 
+pub fn length_split_with_interval(length: usize, interval: &Interval) -> (Interval, Interval) {
+    let original_interval = Interval::new(0, length);
+    let prefix = original_interval.prefix(*interval);
+    let suffix = original_interval.suffix(*interval);
+    (prefix, suffix)
+}
+
 pub fn debug_print_delta(delta: &Delta) {
     log::debug!("😁 {}", serde_json::to_string(delta).unwrap());
 }
 
-pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Option<Attributes> {
-    let mut attributes = Attributes::new();
+pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Attributes {
+    let mut attributes_data = AttributesData::new();
     let mut offset = 0;
 
     delta.ops.iter().for_each(|op| match op {
-        Operation::Delete(n) => {},
+        Operation::Delete(_n) => {},
         Operation::Retain(retain) => {
-            if retain.attributes.is_some() {
-                if interval.contains(retain.num as usize) {
-                    attributes.extend(retain.attributes.as_ref().unwrap().clone());
+            if interval.contains(retain.num as usize) {
+                match &retain.attributes {
+                    Attributes::Follow => {},
+                    Attributes::Custom(data) => {
+                        attributes_data.extend(data.clone());
+                    },
+                    Attributes::Empty => {},
                 }
             }
         },
-        Operation::Insert(insert) => {
-            if insert.attributes.is_some() {
+        Operation::Insert(insert) => match &insert.attributes {
+            Attributes::Follow => {},
+            Attributes::Custom(data) => {
                 if interval.start >= offset && insert.num_chars() > (interval.end as u64 - 1) {
-                    attributes.extend(insert.attributes.as_ref().unwrap().clone());
+                    attributes_data.extend(data.clone());
                 }
                 offset += insert.num_chars() as usize;
-            }
+            },
+            Attributes::Empty => {},
         },
     });
-    Some(attributes)
+
+    if attributes_data.is_plain() {
+        Attributes::Empty
+    } else {
+        Attributes::Custom(attributes_data)
+    }
+}
+
+pub struct Rng(StdRng);
+
+impl Default for Rng {
+    fn default() -> Self { Rng(StdRng::from_rng(thread_rng()).unwrap()) }
+}
+
+impl Rng {
+    pub fn from_seed(seed: [u8; 32]) -> Self { Rng(StdRng::from_seed(seed)) }
+
+    pub fn gen_string(&mut self, len: usize) -> String {
+        (0..len).map(|_| self.0.gen::<char>()).collect()
+    }
+
+    pub fn gen_delta(&mut self, s: &str) -> Delta {
+        let mut delta = Delta::default();
+        loop {
+            let left = s.chars().count() - delta.base_len();
+            if left == 0 {
+                break;
+            }
+            let i = if left == 1 {
+                1
+            } else {
+                1 + self.0.gen_range(0, std::cmp::min(left - 1, 20))
+            };
+            match self.0.gen_range(0.0, 1.0) {
+                f if f < 0.2 => {
+                    delta.insert(&self.gen_string(i), Attributes::Empty);
+                },
+                f if f < 0.4 => {
+                    delta.delete(i as u64);
+                },
+                _ => {
+                    delta.retain(i as u64, Attributes::Empty);
+                },
+            }
+        }
+        if self.0.gen_range(0.0, 1.0) < 0.3 {
+            delta.insert(&("1".to_owned() + &self.gen_string(10)), Attributes::Empty);
+        }
+        delta
+    }
 }

+ 36 - 41
rust-lib/flowy-ot/tests/op_test.rs

@@ -2,26 +2,21 @@ pub mod helper;
 
 use crate::helper::MergeTestOp::*;
 use bytecount::num_chars;
-use flowy_ot::{
-    attributes::*,
-    delta::Delta,
-    operation::{OpBuilder, Operation},
-};
+use flowy_ot::{attributes::*, delta::Delta, operation::OpBuilder};
 use helper::*;
-use std::str::FromStr;
 
 #[test]
 fn lengths() {
     let mut delta = Delta::default();
     assert_eq!(delta.base_len, 0);
     assert_eq!(delta.target_len, 0);
-    delta.retain(5, None);
+    delta.retain(5, Attributes::Empty);
     assert_eq!(delta.base_len, 5);
     assert_eq!(delta.target_len, 5);
-    delta.insert("abc", None);
+    delta.insert("abc", Attributes::Empty);
     assert_eq!(delta.base_len, 5);
     assert_eq!(delta.target_len, 8);
-    delta.retain(2, None);
+    delta.retain(2, Attributes::Empty);
     assert_eq!(delta.base_len, 7);
     assert_eq!(delta.target_len, 10);
     delta.delete(2);
@@ -31,10 +26,10 @@ fn lengths() {
 #[test]
 fn sequence() {
     let mut delta = Delta::default();
-    delta.retain(5, None);
-    delta.retain(0, None);
-    delta.insert("appflowy", None);
-    delta.insert("", None);
+    delta.retain(5, Attributes::Empty);
+    delta.retain(0, Attributes::Empty);
+    delta.insert("appflowy", Attributes::Empty);
+    delta.insert("", Attributes::Empty);
     delta.delete(3);
     delta.delete(0);
     assert_eq!(delta.ops.len(), 3);
@@ -55,11 +50,11 @@ fn apply_1000() {
 fn apply() {
     let s = "hello world,".to_owned();
     let mut delta_a = Delta::default();
-    delta_a.insert(&s, None);
+    delta_a.insert(&s, Attributes::Empty);
 
     let mut delta_b = Delta::default();
-    delta_b.retain(s.len() as u64, None);
-    delta_b.insert("appflowy", None);
+    delta_b.retain(s.len() as u64, Attributes::Empty);
+    delta_b.insert("appflowy", Attributes::Empty);
 
     let after_a = delta_a.apply("").unwrap();
     let after_b = delta_b.apply(&after_a).unwrap();
@@ -69,15 +64,15 @@ fn apply() {
 #[test]
 fn base_len_test() {
     let mut delta_a = Delta::default();
-    delta_a.insert("a", None);
-    delta_a.insert("b", None);
-    delta_a.insert("c", None);
+    delta_a.insert("a", Attributes::Empty);
+    delta_a.insert("b", Attributes::Empty);
+    delta_a.insert("c", Attributes::Empty);
 
     let s = "hello world,".to_owned();
     delta_a.delete(s.len() as u64);
     let after_a = delta_a.apply(&s).unwrap();
 
-    delta_a.insert("d", None);
+    delta_a.insert("d", Attributes::Empty);
     assert_eq!("abc", &after_a);
 }
 
@@ -97,8 +92,8 @@ fn invert() {
 #[test]
 fn empty_ops() {
     let mut delta = Delta::default();
-    delta.retain(0, None);
-    delta.insert("", None);
+    delta.retain(0, Attributes::Empty);
+    delta.insert("", Attributes::Empty);
     delta.delete(0);
     assert_eq!(delta.ops.len(), 0);
 }
@@ -106,33 +101,33 @@ fn empty_ops() {
 fn eq() {
     let mut delta_a = Delta::default();
     delta_a.delete(1);
-    delta_a.insert("lo", None);
-    delta_a.retain(2, None);
-    delta_a.retain(3, None);
+    delta_a.insert("lo", Attributes::Empty);
+    delta_a.retain(2, Attributes::Empty);
+    delta_a.retain(3, Attributes::Empty);
     let mut delta_b = Delta::default();
     delta_b.delete(1);
-    delta_b.insert("l", None);
-    delta_b.insert("o", None);
-    delta_b.retain(5, None);
+    delta_b.insert("l", Attributes::Empty);
+    delta_b.insert("o", Attributes::Empty);
+    delta_b.retain(5, Attributes::Empty);
     assert_eq!(delta_a, delta_b);
     delta_a.delete(1);
-    delta_b.retain(1, None);
+    delta_b.retain(1, Attributes::Empty);
     assert_ne!(delta_a, delta_b);
 }
 #[test]
 fn ops_merging() {
     let mut delta = Delta::default();
     assert_eq!(delta.ops.len(), 0);
-    delta.retain(2, None);
+    delta.retain(2, Attributes::Empty);
     assert_eq!(delta.ops.len(), 1);
     assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(2).build()));
-    delta.retain(3, None);
+    delta.retain(3, Attributes::Empty);
     assert_eq!(delta.ops.len(), 1);
     assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(5).build()));
-    delta.insert("abc", None);
+    delta.insert("abc", Attributes::Empty);
     assert_eq!(delta.ops.len(), 2);
     assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abc").build()));
-    delta.insert("xyz", None);
+    delta.insert("xyz", Attributes::Empty);
     assert_eq!(delta.ops.len(), 2);
     assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abcxyz").build()));
     delta.delete(1);
@@ -146,11 +141,11 @@ fn ops_merging() {
 fn is_noop() {
     let mut delta = Delta::default();
     assert!(delta.is_noop());
-    delta.retain(5, None);
+    delta.retain(5, Attributes::Empty);
     assert!(delta.is_noop());
-    delta.retain(3, None);
+    delta.retain(3, Attributes::Empty);
     assert!(delta.is_noop());
-    delta.insert("lorem", None);
+    delta.insert("lorem", Attributes::Empty);
     assert!(!delta.is_noop());
 }
 #[test]
@@ -184,9 +179,9 @@ fn transform() {
         let ba_prime = b.compose(&a_prime).unwrap();
         assert_eq!(ab_prime, ba_prime);
 
-        // let after_ab_prime = ab_prime.apply(&s).unwrap();
-        // let after_ba_prime = ba_prime.apply(&s).unwrap();
-        // assert_eq!(after_ab_prime, after_ba_prime);
+        let after_ab_prime = ab_prime.apply(&s).unwrap();
+        let after_ba_prime = ba_prime.apply(&s).unwrap();
+        assert_eq!(after_ab_prime, after_ba_prime);
     }
 }
 
@@ -208,13 +203,13 @@ fn transform2() {
 fn delta_transform_test() {
     let mut a = Delta::default();
     let mut a_s = String::new();
-    a.insert("123", Some(AttrsBuilder::new().bold(true).build()));
+    a.insert("123", AttrsBuilder::new().bold(true).build());
     a_s = a.apply(&a_s).unwrap();
     assert_eq!(&a_s, "123");
 
     let mut b = Delta::default();
     let mut b_s = String::new();
-    b.insert("456", None);
+    b.insert("456", Attributes::Empty);
     b_s = b.apply(&b_s).unwrap();
     assert_eq!(&b_s, "456");
 

+ 3 - 7
rust-lib/flowy-ot/tests/serde_test.rs

@@ -1,5 +1,5 @@
 use flowy_ot::{
-    attributes::{Attributes, AttrsBuilder},
+    attributes::AttrsBuilder,
     delta::Delta,
     operation::{OpBuilder, Operation, Retain},
 };
@@ -7,9 +7,7 @@ use flowy_ot::{
 #[test]
 fn operation_insert_serialize_test() {
     let attributes = AttrsBuilder::new().bold(true).italic(true).build();
-    let operation = OpBuilder::insert("123")
-        .attributes(Some(attributes))
-        .build();
+    let operation = OpBuilder::insert("123").attributes(attributes).build();
     let json = serde_json::to_string(&operation).unwrap();
     eprintln!("{}", json);
 
@@ -39,9 +37,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(Some(attributes))
-        .build();
+    let retain = OpBuilder::insert("123").attributes(attributes).build();
 
     delta.add(retain);
     delta.add(Operation::Retain(5.into()));