appflowy %!s(int64=3) %!d(string=hai) anos
pai
achega
d78c33e194

+ 26 - 22
rust-lib/flowy-ot/src/attributes.rs

@@ -15,7 +15,7 @@ impl Attributes {
         }
     }
 
-    pub fn remove_empty_value(&mut self) { self.inner.retain(|_, v| v.is_empty() == false); }
+    pub fn remove_empty(&mut self) { self.inner.retain(|_, v| v.is_empty() == false); }
 
     pub fn extend(&mut self, other: Attributes) { self.inner.extend(other.inner); }
 
@@ -49,18 +49,21 @@ impl AttrsBuilder {
         }
     }
 
-    pub fn bold(mut self) -> Self {
-        self.inner.insert("bold".to_owned(), "true".to_owned());
+    pub fn bold(mut self, bold: bool) -> Self {
+        let val = match bold {
+            true => "true",
+            false => "",
+        };
+        self.inner.insert("bold".to_owned(), val.to_owned());
         self
     }
 
-    pub fn un_bold(mut self) -> Self {
-        self.inner.insert("bold".to_owned(), "".to_owned());
-        self
-    }
-
-    pub fn italic(mut self) -> Self {
-        self.inner.insert("italic".to_owned(), "true".to_owned());
+    pub fn italic(mut self, italic: bool) -> Self {
+        let val = match italic {
+            true => "true",
+            false => "",
+        };
+        self.inner.insert("italic".to_owned(), val.to_owned());
         self
     }
 
@@ -86,25 +89,26 @@ pub fn compose_attributes(
 ) -> Option<Attributes> {
     let a = attributes_from(op1);
     let b = attributes_from(op2);
+
+    if a.is_none() && b.is_none() {
+        return None;
+    }
+
     let mut attrs_a = a.unwrap_or(Attributes::default());
     let attrs_b = b.unwrap_or(Attributes::default());
 
-    // log::debug!(
-    //     "before compose_attributes: a: {:?}, b: {:?}",
-    //     attrs_a,
-    //     attrs_b
-    // );
+    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_value()
+        attrs_a.remove_empty()
     }
-    // log::debug!("after compose_attributes: a: {:?}", attrs_a);
 
-    return if attrs_a.is_empty() {
-        None
-    } else {
-        Some(attrs_a)
-    };
+    Some(attrs_a)
 }
 
 pub fn transform_attributes(

+ 25 - 2
rust-lib/flowy-ot/src/delta.rs

@@ -156,7 +156,13 @@ impl Delta {
                     return Err(OTError);
                 },
                 (Some(Operation::Retain(retain)), Some(Operation::Retain(o_retain))) => {
-                    let composed_attrs = compose_attributes(&next_op1, &next_op2, true);
+                    let composed_attrs = compose_attributes(&next_op1, &next_op2, false);
+                    log::debug!(
+                        "[retain:{} - retain:{}]: {:?}",
+                        retain.num,
+                        o_retain.num,
+                        composed_attrs
+                    );
                     match retain.cmp(&o_retain) {
                         Ordering::Less => {
                             new_delta.retain(retain.num, composed_attrs);
@@ -201,6 +207,12 @@ impl Delta {
                 },
                 (Some(Operation::Insert(insert)), Some(Operation::Retain(o_retain))) => {
                     let composed_attrs = compose_attributes(&next_op1, &next_op2, false);
+                    log::debug!(
+                        "[insert:{} - retain:{}]: {:?}",
+                        insert.s,
+                        o_retain.num,
+                        composed_attrs
+                    );
                     match (insert.num_chars()).cmp(o_retain) {
                         Ordering::Less => {
                             new_delta.insert(&insert.s, composed_attrs.clone());
@@ -465,6 +477,11 @@ fn merge_insert_or_new_op(
     s: &str,
     attributes: Option<Attributes>,
 ) -> Option<Operation> {
+    if attributes.is_none() {
+        insert.s += s;
+        return None;
+    }
+
     if insert.attributes == attributes {
         insert.s += s;
         None
@@ -478,11 +495,17 @@ fn merge_retain_or_new_op(
     n: u64,
     attributes: Option<Attributes>,
 ) -> Option<Operation> {
+    log::trace!(
+        "merge_retain_or_new_op: {:?}, {:?}",
+        retain.attributes,
+        attributes
+    );
+
     if attributes.is_none() {
         retain.num += n;
         return None;
     }
-    // log::debug!("merge retain: {:?}, {:?}", retain.attributes, attributes);
+
     if retain.attributes == attributes {
         retain.num += n;
         None

+ 17 - 3
rust-lib/flowy-ot/src/operation.rs

@@ -76,7 +76,14 @@ impl OpBuilder {
     pub fn insert(s: &str) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) }
 
     pub fn attributes(mut self, attrs: Option<Attributes>) -> OpBuilder {
-        self.attrs = attrs;
+        match attrs {
+            None => self.attrs = attrs,
+            Some(attrs) => match attrs.is_empty() {
+                true => self.attrs = None,
+                false => self.attrs = Some(attrs),
+            },
+        }
+
         self
     }
 
@@ -95,7 +102,7 @@ impl OpBuilder {
 pub struct Retain {
     #[serde(rename(serialize = "retain", deserialize = "retain"))]
     pub num: u64,
-    #[serde(skip_serializing_if = "Option::is_none")]
+    #[serde(skip_serializing_if = "is_empty")]
     pub attributes: Option<Attributes>,
 }
 
@@ -123,7 +130,7 @@ pub struct Insert {
     #[serde(rename(serialize = "insert", deserialize = "insert"))]
     pub s: String,
 
-    #[serde(skip_serializing_if = "Option::is_none")]
+    #[serde(skip_serializing_if = "is_empty")]
     pub attributes: Option<Attributes>,
 }
 
@@ -152,3 +159,10 @@ impl std::convert::From<&str> for Insert {
         }
     }
 }
+
+fn is_empty(attributes: &Option<Attributes>) -> bool {
+    match attributes {
+        None => true,
+        Some(attributes) => attributes.is_empty(),
+    }
+}

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

@@ -53,6 +53,30 @@ fn delta_add_bold_attr3() {
     MergeTest::new().run_script(ops);
 }
 
+#[test]
+fn delta_add_bold_italic() {
+    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":{"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"}}]"#,
+        ),
+    ];
+    MergeTest::new().run_script(ops);
+}
+
 #[test]
 fn delta_add_bold_attr_and_invert() {
     let ops = vec![

+ 57 - 39
rust-lib/flowy-ot/tests/helper/mod.rs

@@ -58,6 +58,7 @@ pub enum MergeTestOp {
     InsertBold(usize, &'static str, Interval),
     // delta_i, start, length, enable
     Bold(usize, Interval, bool),
+    Italic(usize, Interval, bool),
     Transform(usize, usize),
     AssertStr(usize, &'static str),
     AssertOpsJson(usize, &'static str),
@@ -71,7 +72,7 @@ impl MergeTest {
     pub fn new() -> Self {
         static INIT: Once = Once::new();
         INIT.call_once(|| {
-            std::env::set_var("RUST_LOG", "debug");
+            std::env::set_var("RUST_LOG", "info");
             env_logger::init();
         });
 
@@ -90,40 +91,17 @@ impl MergeTest {
                 delta.insert(s, None);
             },
             MergeTestOp::InsertBold(delta_i, s, interval) => {
-                let attrs = AttrsBuilder::new().bold().build();
+                let attrs = AttrsBuilder::new().bold(true).build();
                 let delta = &mut self.deltas[*delta_i];
                 delta.insert(s, Some(attrs));
             },
             MergeTestOp::Bold(delta_i, interval, enable) => {
-                let attrs = if *enable {
-                    AttrsBuilder::new().bold().build()
-                } else {
-                    AttrsBuilder::new().un_bold().build()
-                };
-                let delta = &mut self.deltas[*delta_i];
-                let delta_interval = Interval::new(0, delta.target_len);
-
-                let mut new_delta = Delta::default();
-                let prefix = delta_interval.prefix(*interval);
-                if prefix.is_empty() == false && prefix != *interval {
-                    let size = prefix.size();
-                    // get attr in prefix interval
-                    let attrs = attributes_in_interval(delta, &prefix);
-                    new_delta.retain(size as u64, Some(attrs));
-                }
-
-                let size = interval.size();
-                new_delta.retain(size as u64, Some(attrs));
-
-                let suffix = delta_interval.suffix(*interval);
-                if suffix.is_empty() == false {
-                    let size = suffix.size();
-                    let attrs = attributes_in_interval(delta, &suffix);
-                    new_delta.retain(size as u64, Some(attrs));
-                }
-
-                let a = delta.compose(&new_delta).unwrap();
-                self.deltas[*delta_i] = a;
+                let attrs = AttrsBuilder::new().bold(*enable).build();
+                self.replace_delta(*delta_i, attrs, interval);
+            },
+            MergeTestOp::Italic(delta_i, interval, enable) => {
+                let attrs = AttrsBuilder::new().italic(*enable).build();
+                self.replace_delta(*delta_i, attrs, interval);
             },
             MergeTestOp::Transform(delta_a_i, delta_b_i) => {
                 let delta_a = &self.deltas[*delta_a_i];
@@ -142,12 +120,16 @@ impl MergeTest {
             },
 
             MergeTestOp::AssertOpsJson(delta_i, expected) => {
-                let s = serde_json::to_string(&self.deltas[*delta_i]).unwrap();
-                if &s != expected {
-                    log::error!("{}", s);
-                }
+                let expected_delta: Delta = serde_json::from_str(expected).unwrap();
 
-                assert_eq!(&s, expected);
+                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 {
+                    log::error!("✅ {}", expected);
+                    log::error!("❌ {}", delta_i_json);
+                }
+                assert_eq!(delta, expected_delta);
             },
         }
     }
@@ -157,13 +139,49 @@ impl MergeTest {
             self.run_op(op);
         }
     }
+
+    pub fn replace_delta(
+        &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);
+        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);
+
+    let mut new_delta = Delta::default();
+    let prefix = delta_interval.prefix(*interval);
+    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));
+
+    let suffix = delta_interval.suffix(*interval);
+    if suffix.is_empty() == false {
+        let size = suffix.size();
+        let attrs = attributes_in_interval(delta, &suffix);
+        new_delta.retain(size as u64, attrs);
+    }
+
+    delta.compose(&new_delta).unwrap()
 }
 
 pub fn debug_print_delta(delta: &Delta) {
     log::debug!("😁 {}", serde_json::to_string(delta).unwrap());
 }
 
-pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Attributes {
+pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Option<Attributes> {
     let mut attributes = Attributes::new();
     let mut offset = 0;
 
@@ -178,12 +196,12 @@ pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Attributes
         },
         Operation::Insert(insert) => {
             if insert.attributes.is_some() {
-                if interval.start >= offset || insert.num_chars() > interval.end as u64 {
+                if interval.start >= offset && insert.num_chars() > (interval.end as u64 - 1) {
                     attributes.extend(insert.attributes.as_ref().unwrap().clone());
                 }
                 offset += insert.num_chars() as usize;
             }
         },
     });
-    attributes
+    Some(attributes)
 }

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

@@ -182,10 +182,11 @@ fn transform() {
         let (a_prime, b_prime) = a.transform(&b).unwrap();
         let ab_prime = a.compose(&b_prime).unwrap();
         let ba_prime = b.compose(&a_prime).unwrap();
-        let after_ab_prime = ab_prime.apply(&s).unwrap();
-        let after_ba_prime = ba_prime.apply(&s).unwrap();
         assert_eq!(ab_prime, ba_prime);
-        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);
     }
 }
 
@@ -207,13 +208,15 @@ fn transform2() {
 fn delta_transform_test() {
     let mut a = Delta::default();
     let mut a_s = String::new();
-    a.insert("123", Some(AttrsBuilder::new().bold().build()));
+    a.insert("123", Some(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_s = a.apply(&b_s).unwrap();
+    b_s = b.apply(&b_s).unwrap();
+    assert_eq!(&b_s, "456");
 
     let (a_prime, b_prime) = a.transform(&b).unwrap();
     assert_eq!(

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

@@ -6,7 +6,7 @@ use flowy_ot::{
 
 #[test]
 fn operation_insert_serialize_test() {
-    let attributes = AttrsBuilder::new().bold().italic().build();
+    let attributes = AttrsBuilder::new().bold(true).italic(true).build();
     let operation = OpBuilder::insert("123")
         .attributes(Some(attributes))
         .build();
@@ -38,7 +38,7 @@ fn operation_delete_serialize_test() {
 fn delta_serialize_test() {
     let mut delta = Delta::default();
 
-    let attributes = AttrsBuilder::new().bold().italic().build();
+    let attributes = AttrsBuilder::new().bold(true).italic(true).build();
     let retain = OpBuilder::insert("123")
         .attributes(Some(attributes))
         .build();