浏览代码

add attributes

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

+ 1 - 1
rust-lib/flowy-document/tests/editor/helper.rs

@@ -1,6 +1,6 @@
 use flowy_test::builder::SingleUserTestBuilder;
 
-use flowy_editor::{entities::doc::*, event::EditorEvent::*};
+use flowy_document::{entities::doc::*, event::EditorEvent::*};
 use flowy_infra::uuid;
 
 pub fn create_doc(name: &str, desc: &str, text: &str) -> DocInfo {

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

@@ -33,43 +33,91 @@ impl std::ops::DerefMut for Attributes {
     fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
 }
 
+pub struct AttributesBuilder {
+    inner: Attributes,
+}
+
+impl AttributesBuilder {
+    pub fn new() -> Self {
+        Self {
+            inner: Attributes::default(),
+        }
+    }
+
+    pub fn bold(mut self) -> Self {
+        self.inner.insert("bold".to_owned(), "true".to_owned());
+        self
+    }
+
+    pub fn italic(mut self) -> Self {
+        self.inner.insert("italic".to_owned(), "true".to_owned());
+        self
+    }
+
+    pub fn underline(mut self) -> Self {
+        self.inner.insert("underline".to_owned(), "true".to_owned());
+        self
+    }
+
+    pub fn build(self) -> Attributes { self.inner }
+}
+
 pub fn compose_attributes(
-    mut a: Attributes,
-    b: Attributes,
+    a: Option<Attributes>,
+    b: Option<Attributes>,
     keep_empty: bool,
 ) -> Option<Attributes> {
-    a.extend(b);
-    let mut result = a;
+    if a.is_none() {
+        return b;
+    }
+
+    if b.is_none() {
+        return None;
+    }
+
+    let mut attrs_a = a.unwrap_or(Attributes::default());
+    let attrs_b = b.unwrap_or(Attributes::default());
+    attrs_a.extend(attrs_b);
+
     if !keep_empty {
-        result.remove_empty_value()
+        attrs_a.remove_empty_value()
     }
 
-    return if result.is_empty() {
+    return if attrs_a.is_empty() {
         None
     } else {
-        Some(result)
+        Some(attrs_a)
     };
 }
 
-pub fn transform_attributes(a: Attributes, b: Attributes, priority: bool) -> Option<Attributes> {
-    if a.is_empty() {
-        return Some(b);
+pub fn transform_attributes(
+    a: Option<Attributes>,
+    b: Option<Attributes>,
+    priority: bool,
+) -> Option<Attributes> {
+    if a.is_none() {
+        return b;
     }
 
-    if b.is_empty() {
+    if b.is_none() {
         return None;
     }
 
     if !priority {
-        return Some(b);
+        return b;
     }
 
-    let result = b.iter().fold(Attributes::new(), |mut attributes, (k, v)| {
-        if a.contains_key(k) == false {
-            attributes.insert(k.clone(), v.clone());
-        }
-        attributes
-    });
+    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)
 }
 

+ 94 - 53
rust-lib/flowy-ot/src/delta.rs

@@ -1,4 +1,4 @@
-use crate::{errors::OTError, operation::*};
+use crate::{attributes::*, errors::OTError, operation::*};
 use bytecount::num_chars;
 use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator};
 
@@ -19,15 +19,15 @@ impl Default for Delta {
     }
 }
 
-impl FromIterator<OpType> for Delta {
-    fn from_iter<T: IntoIterator<Item = OpType>>(ops: T) -> Self {
-        let mut operations = Delta::default();
-        for op in ops {
-            operations.add(op);
-        }
-        operations
-    }
-}
+// impl FromIterator<OpType> for Delta {
+//     fn from_iter<T: IntoIterator<Item = OpType>>(ops: T) -> Self {
+//         let mut operations = Delta::default();
+//         for op in ops {
+//             operations.add(op);
+//         }
+//         operations
+//     }
+// }
 
 impl Delta {
     #[inline]
@@ -39,13 +39,13 @@ impl Delta {
         }
     }
 
-    fn add(&mut self, op: OpType) {
-        match op {
-            OpType::Delete(i) => self.delete(i),
-            OpType::Insert(s) => self.insert(&s),
-            OpType::Retain(i) => self.retain(i),
-        }
-    }
+    // fn add(&mut self, op: OpType) {
+    //     match op {
+    //         OpType::Delete(i) => self.delete(i),
+    //         OpType::Insert(s) => self.insert(&s),
+    //         OpType::Retain(i) => self.retain(i),
+    //     }
+    // }
 
     pub fn delete(&mut self, n: u64) {
         if n == 0 {
@@ -63,7 +63,7 @@ impl Delta {
         self.ops.push(OperationBuilder::delete(n).build());
     }
 
-    pub fn insert(&mut self, s: &str) {
+    pub fn insert(&mut self, s: &str, attrs: Option<Attributes>) {
         if s.is_empty() {
             return;
         }
@@ -90,10 +90,11 @@ impl Delta {
             },
             _ => OpType::Insert(s.to_owned()),
         };
-        self.ops.push(OperationBuilder::new(new_last).build());
+        self.ops
+            .push(OperationBuilder::new(new_last).with_attrs(attrs).build());
     }
 
-    pub fn retain(&mut self, n: u64) {
+    pub fn retain(&mut self, n: u64, attrs: Option<Attributes>) {
         if n == 0 {
             return;
         }
@@ -103,11 +104,13 @@ impl Delta {
         if let Some(operation) = self.ops.last_mut() {
             if operation.ty.is_retain() {
                 operation.retain(n);
+                operation.set_attrs(attrs);
                 return;
             }
         }
 
-        self.ops.push(OperationBuilder::retain(n).build());
+        self.ops
+            .push(OperationBuilder::retain(n).with_attrs(attrs).build());
     }
 
     /// Merges the operation with `other` into one operation while preserving
@@ -142,28 +145,35 @@ impl Delta {
                     maybe_op1 = ops1.next();
                 },
                 (_, Some(OpType::Insert(s))) => {
-                    new_delta.insert(s);
+                    new_delta.insert(s, operation_attrs(&maybe_op2));
                     maybe_op2 = ops2.next();
                 },
                 (None, _) | (_, None) => {
                     return Err(OTError);
                 },
-                (Some(OpType::Retain(i)), Some(OpType::Retain(j))) => match i.cmp(&j) {
-                    Ordering::Less => {
-                        new_delta.retain(*i);
-                        maybe_op2 = Some(OperationBuilder::retain(*j - *i).build());
-                        maybe_op1 = ops1.next();
-                    },
-                    std::cmp::Ordering::Equal => {
-                        new_delta.retain(*i);
-                        maybe_op1 = ops1.next();
-                        maybe_op2 = ops2.next();
-                    },
-                    std::cmp::Ordering::Greater => {
-                        new_delta.retain(*j);
-                        maybe_op1 = Some(OperationBuilder::retain(*i - *j).build());
-                        maybe_op2 = ops2.next();
-                    },
+                (Some(OpType::Retain(i)), Some(OpType::Retain(j))) => {
+                    let new_attrs = compose_attributes(
+                        operation_attrs(&maybe_op1),
+                        operation_attrs(&maybe_op2),
+                        true,
+                    );
+                    match i.cmp(&j) {
+                        Ordering::Less => {
+                            new_delta.retain(*i, new_attrs);
+                            maybe_op2 = Some(OperationBuilder::retain(*j - *i).build());
+                            maybe_op1 = ops1.next();
+                        },
+                        std::cmp::Ordering::Equal => {
+                            new_delta.retain(*i, new_attrs);
+                            maybe_op1 = ops1.next();
+                            maybe_op2 = ops2.next();
+                        },
+                        std::cmp::Ordering::Greater => {
+                            new_delta.retain(*j, new_attrs);
+                            maybe_op1 = Some(OperationBuilder::retain(*i - *j).build());
+                            maybe_op2 = ops2.next();
+                        },
+                    }
                 },
                 (Some(OpType::Insert(s)), Some(OpType::Delete(j))) => {
                     match (num_chars(s.as_bytes()) as u64).cmp(j) {
@@ -188,9 +198,14 @@ impl Delta {
                     }
                 },
                 (Some(OpType::Insert(s)), Some(OpType::Retain(j))) => {
+                    let new_attrs = compose_attributes(
+                        operation_attrs(&maybe_op1),
+                        operation_attrs(&maybe_op2),
+                        false,
+                    );
                     match (num_chars(s.as_bytes()) as u64).cmp(j) {
                         Ordering::Less => {
-                            new_delta.insert(s);
+                            new_delta.insert(s, new_attrs);
                             maybe_op2 = Some(
                                 OperationBuilder::retain(*j - num_chars(s.as_bytes()) as u64)
                                     .build(),
@@ -198,13 +213,14 @@ impl Delta {
                             maybe_op1 = ops1.next();
                         },
                         Ordering::Equal => {
-                            new_delta.insert(s);
+                            new_delta.insert(s, new_attrs);
                             maybe_op1 = ops1.next();
                             maybe_op2 = ops2.next();
                         },
                         Ordering::Greater => {
                             let chars = &mut s.chars();
-                            new_delta.insert(&chars.take(*j as usize).collect::<String>());
+                            new_delta
+                                .insert(&chars.take(*j as usize).collect::<String>(), new_attrs);
                             maybe_op1 = Some(OperationBuilder::insert(chars.collect()).build());
                             maybe_op2 = ops2.next();
                         },
@@ -261,13 +277,23 @@ impl Delta {
             ) {
                 (None, None) => break,
                 (Some(OpType::Insert(s)), _) => {
-                    a_prime.insert(s);
-                    b_prime.retain(num_chars(s.as_bytes()) as _);
+                    let new_attrs = compose_attributes(
+                        operation_attrs(&maybe_op1),
+                        operation_attrs(&maybe_op2),
+                        true,
+                    );
+                    a_prime.insert(s, new_attrs.clone());
+                    b_prime.retain(num_chars(s.as_bytes()) as _, new_attrs.clone());
                     maybe_op1 = ops1.next();
                 },
                 (_, Some(OpType::Insert(s))) => {
-                    a_prime.retain(num_chars(s.as_bytes()) as _);
-                    b_prime.insert(s);
+                    let new_attrs = compose_attributes(
+                        operation_attrs(&maybe_op1),
+                        operation_attrs(&maybe_op2),
+                        true,
+                    );
+                    a_prime.retain(num_chars(s.as_bytes()) as _, new_attrs.clone());
+                    b_prime.insert(s, new_attrs.clone());
                     maybe_op2 = ops2.next();
                 },
                 (None, _) => {
@@ -277,22 +303,27 @@ impl Delta {
                     return Err(OTError);
                 },
                 (Some(OpType::Retain(i)), Some(OpType::Retain(j))) => {
+                    let new_attrs = compose_attributes(
+                        operation_attrs(&maybe_op1),
+                        operation_attrs(&maybe_op2),
+                        true,
+                    );
                     match i.cmp(&j) {
                         Ordering::Less => {
-                            a_prime.retain(*i);
-                            b_prime.retain(*i);
+                            a_prime.retain(*i, new_attrs.clone());
+                            b_prime.retain(*i, new_attrs.clone());
                             maybe_op2 = Some(OperationBuilder::retain(*j - *i).build());
                             maybe_op1 = ops1.next();
                         },
                         Ordering::Equal => {
-                            a_prime.retain(*i);
-                            b_prime.retain(*i);
+                            a_prime.retain(*i, new_attrs.clone());
+                            b_prime.retain(*i, new_attrs.clone());
                             maybe_op1 = ops1.next();
                             maybe_op2 = ops2.next();
                         },
                         Ordering::Greater => {
-                            a_prime.retain(*j);
-                            b_prime.retain(*j);
+                            a_prime.retain(*j, new_attrs.clone());
+                            b_prime.retain(*j, new_attrs.clone());
                             maybe_op1 = Some(OperationBuilder::retain(*i - *j).build());
                             maybe_op2 = ops2.next();
                         },
@@ -396,7 +427,7 @@ impl Delta {
         for op in &self.ops {
             match &op.ty {
                 OpType::Retain(retain) => {
-                    inverse.retain(*retain);
+                    inverse.retain(*retain, op.attrs.clone());
                     for _ in 0..*retain {
                         chars.next();
                     }
@@ -405,7 +436,10 @@ impl Delta {
                     inverse.delete(num_chars(insert.as_bytes()) as u64);
                 },
                 OpType::Delete(delete) => {
-                    inverse.insert(&chars.take(*delete as usize).collect::<String>());
+                    inverse.insert(
+                        &chars.take(*delete as usize).collect::<String>(),
+                        op.attrs.clone(),
+                    );
                 },
             }
         }
@@ -441,3 +475,10 @@ impl Delta {
     #[inline]
     pub fn ops(&self) -> &[Operation] { &self.ops }
 }
+
+pub fn operation_attrs(operation: &Option<Operation>) -> Option<Attributes> {
+    match operation {
+        None => None,
+        Some(operation) => operation.attrs.clone(),
+    }
+}

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

@@ -1,4 +1,4 @@
-mod attributes;
+pub mod attributes;
 pub mod delta;
 pub mod errors;
 pub mod operation;

+ 12 - 10
rust-lib/flowy-ot/src/operation.rs

@@ -8,7 +8,7 @@ use std::{
 #[derive(Clone, Debug, PartialEq)]
 pub struct Operation {
     pub ty: OpType,
-    pub attrs: Attributes,
+    pub attrs: Option<Attributes>,
 }
 
 impl Operation {
@@ -16,7 +16,14 @@ impl Operation {
 
     pub fn retain(&mut self, n: u64) { self.ty.retain(n); }
 
-    pub fn is_plain(&self) -> bool { self.attrs.is_empty() }
+    pub fn set_attrs(&mut self, attrs: Option<Attributes>) { self.attrs = attrs; }
+
+    pub fn is_plain(&self) -> bool {
+        match self.attrs {
+            None => true,
+            Some(ref attrs) => attrs.is_empty(),
+        }
+    }
 
     pub fn is_noop(&self) -> bool {
         match self.ty {
@@ -72,16 +79,11 @@ impl OpType {
 
 pub struct OperationBuilder {
     ty: OpType,
-    attrs: Attributes,
+    attrs: Option<Attributes>,
 }
 
 impl OperationBuilder {
-    pub fn new(ty: OpType) -> OperationBuilder {
-        OperationBuilder {
-            ty,
-            attrs: Attributes::default(),
-        }
-    }
+    pub fn new(ty: OpType) -> OperationBuilder { OperationBuilder { ty, attrs: None } }
 
     pub fn retain(n: u64) -> OperationBuilder { OperationBuilder::new(OpType::Retain(n)) }
 
@@ -89,7 +91,7 @@ impl OperationBuilder {
 
     pub fn insert(s: String) -> OperationBuilder { OperationBuilder::new(OpType::Insert(s)) }
 
-    pub fn with_attrs(mut self, attrs: Attributes) -> OperationBuilder {
+    pub fn with_attrs(mut self, attrs: Option<Attributes>) -> OperationBuilder {
         self.attrs = attrs;
         self
     }

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

@@ -15,9 +15,9 @@ impl Rng {
     }
 
     pub fn gen_delta(&mut self, s: &str) -> Delta {
-        let mut op = Delta::default();
+        let mut delta = Delta::default();
         loop {
-            let left = s.chars().count() - op.base_len();
+            let left = s.chars().count() - delta.base_len();
             if left == 0 {
                 break;
             }
@@ -28,19 +28,19 @@ impl Rng {
             };
             match self.0.gen_range(0.0, 1.0) {
                 f if f < 0.2 => {
-                    op.insert(&self.gen_string(i));
+                    delta.insert(&self.gen_string(i), None);
                 },
                 f if f < 0.4 => {
-                    op.delete(i as u64);
+                    delta.delete(i as u64);
                 },
                 _ => {
-                    op.retain(i as u64);
+                    delta.retain(i as u64, None);
                 },
             }
         }
         if self.0.gen_range(0.0, 1.0) < 0.3 {
-            op.insert(&("1".to_owned() + &self.gen_string(10)));
+            delta.insert(&("1".to_owned() + &self.gen_string(10)), None);
         }
-        op
+        delta
     }
 }

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

@@ -3,6 +3,7 @@ mod helper;
 use crate::helper::Rng;
 use bytecount::num_chars;
 use flowy_ot::{
+    attributes::*,
     delta::Delta,
     operation::{OpType, OperationBuilder},
 };
@@ -12,13 +13,13 @@ fn lengths() {
     let mut delta = Delta::default();
     assert_eq!(delta.base_len, 0);
     assert_eq!(delta.target_len, 0);
-    delta.retain(5);
+    delta.retain(5, None);
     assert_eq!(delta.base_len, 5);
     assert_eq!(delta.target_len, 5);
-    delta.insert("abc");
+    delta.insert("abc", None);
     assert_eq!(delta.base_len, 5);
     assert_eq!(delta.target_len, 8);
-    delta.retain(2);
+    delta.retain(2, None);
     assert_eq!(delta.base_len, 7);
     assert_eq!(delta.target_len, 10);
     delta.delete(2);
@@ -28,17 +29,17 @@ fn lengths() {
 #[test]
 fn sequence() {
     let mut delta = Delta::default();
-    delta.retain(5);
-    delta.retain(0);
-    delta.insert("lorem");
-    delta.insert("");
+    delta.retain(5, None);
+    delta.retain(0, None);
+    delta.insert("lorem", None);
+    delta.insert("", None);
     delta.delete(3);
     delta.delete(0);
     assert_eq!(delta.ops.len(), 3);
 }
 
 #[test]
-fn apply() {
+fn apply_1000() {
     for _ in 0..1000 {
         let mut rng = Rng::default();
         let s = rng.gen_string(50);
@@ -47,6 +48,22 @@ fn apply() {
         assert_eq!(delta.apply(&s).unwrap().chars().count(), delta.target_len);
     }
 }
+
+#[test]
+fn apply() {
+    let s = "hello world,".to_owned();
+    let mut delta_a = Delta::default();
+    delta_a.insert(&s, None);
+
+    let mut delta_b = Delta::default();
+    delta_b.retain(s.len() as u64, None);
+    delta_b.insert("appflowy", None);
+
+    let after_a = delta_a.apply("").unwrap();
+    let after_b = delta_b.apply(&after_a).unwrap();
+    assert_eq!("hello world,appflowy", &after_b);
+}
+
 #[test]
 fn invert() {
     for _ in 0..1000 {
@@ -62,8 +79,8 @@ fn invert() {
 #[test]
 fn empty_ops() {
     let mut delta = Delta::default();
-    delta.retain(0);
-    delta.insert("");
+    delta.retain(0, None);
+    delta.insert("", None);
     delta.delete(0);
     assert_eq!(delta.ops.len(), 0);
 }
@@ -71,36 +88,36 @@ fn empty_ops() {
 fn eq() {
     let mut delta_a = Delta::default();
     delta_a.delete(1);
-    delta_a.insert("lo");
-    delta_a.retain(2);
-    delta_a.retain(3);
+    delta_a.insert("lo", None);
+    delta_a.retain(2, None);
+    delta_a.retain(3, None);
     let mut delta_b = Delta::default();
     delta_b.delete(1);
-    delta_b.insert("l");
-    delta_b.insert("o");
-    delta_b.retain(5);
+    delta_b.insert("l", None);
+    delta_b.insert("o", None);
+    delta_b.retain(5, None);
     assert_eq!(delta_a, delta_b);
     delta_a.delete(1);
-    delta_b.retain(1);
+    delta_b.retain(1, None);
     assert_ne!(delta_a, delta_b);
 }
 #[test]
 fn ops_merging() {
     let mut delta = Delta::default();
     assert_eq!(delta.ops.len(), 0);
-    delta.retain(2);
+    delta.retain(2, None);
     assert_eq!(delta.ops.len(), 1);
     assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(2).build()));
-    delta.retain(3);
+    delta.retain(3, None);
     assert_eq!(delta.ops.len(), 1);
     assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(5).build()));
-    delta.insert("abc");
+    delta.insert("abc", None);
     assert_eq!(delta.ops.len(), 2);
     assert_eq!(
         delta.ops.last(),
         Some(&OperationBuilder::insert("abc".to_owned()).build())
     );
-    delta.insert("xyz");
+    delta.insert("xyz", None);
     assert_eq!(delta.ops.len(), 2);
     assert_eq!(
         delta.ops.last(),
@@ -117,11 +134,11 @@ fn ops_merging() {
 fn is_noop() {
     let mut delta = Delta::default();
     assert!(delta.is_noop());
-    delta.retain(5);
+    delta.retain(5, None);
     assert!(delta.is_noop());
-    delta.retain(3);
+    delta.retain(3, None);
     assert!(delta.is_noop());
-    delta.insert("lorem");
+    delta.insert("lorem", None);
     assert!(!delta.is_noop());
 }
 #[test]