From a3ed1b28740c41da140453fa8e3e4297523428eb Mon Sep 17 00:00:00 2001 From: appflowy Date: Sat, 31 Jul 2021 23:22:17 +0800 Subject: [PATCH] add attributes --- .../flowy-document/tests/editor/helper.rs | 2 +- rust-lib/flowy-ot/src/attributes.rs | 92 ++++++++--- rust-lib/flowy-ot/src/delta.rs | 147 +++++++++++------- rust-lib/flowy-ot/src/lib.rs | 2 +- rust-lib/flowy-ot/src/operation.rs | 22 +-- rust-lib/flowy-ot/tests/helper/mod.rs | 14 +- rust-lib/flowy-ot/tests/op_test.rs | 65 +++++--- 7 files changed, 226 insertions(+), 118 deletions(-) diff --git a/rust-lib/flowy-document/tests/editor/helper.rs b/rust-lib/flowy-document/tests/editor/helper.rs index fd9ee245dc..40d4281850 100644 --- a/rust-lib/flowy-document/tests/editor/helper.rs +++ b/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 { diff --git a/rust-lib/flowy-ot/src/attributes.rs b/rust-lib/flowy-ot/src/attributes.rs index e6315bfeb2..ef274a7bd6 100644 --- a/rust-lib/flowy-ot/src/attributes.rs +++ b/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 fn compose_attributes( - mut a: Attributes, - b: Attributes, - keep_empty: bool, -) -> Option { - a.extend(b); - let mut result = a; - if !keep_empty { - result.remove_empty_value() +pub struct AttributesBuilder { + inner: Attributes, +} + +impl AttributesBuilder { + pub fn new() -> Self { + Self { + inner: Attributes::default(), + } } - return if result.is_empty() { + 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( + a: Option, + b: Option, + keep_empty: bool, +) -> Option { + 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 { + attrs_a.remove_empty_value() + } + + return if attrs_a.is_empty() { None } else { - Some(result) + Some(attrs_a) }; } -pub fn transform_attributes(a: Attributes, b: Attributes, priority: bool) -> Option { - if a.is_empty() { - return Some(b); +pub fn transform_attributes( + a: Option, + b: Option, + priority: bool, +) -> Option { + 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) } diff --git a/rust-lib/flowy-ot/src/delta.rs b/rust-lib/flowy-ot/src/delta.rs index 375a13a450..d7f6d501a4 100644 --- a/rust-lib/flowy-ot/src/delta.rs +++ b/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 for Delta { - fn from_iter>(ops: T) -> Self { - let mut operations = Delta::default(); - for op in ops { - operations.add(op); - } - operations - } -} +// impl FromIterator for Delta { +// fn from_iter>(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) { 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) { 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::()); + new_delta + .insert(&chars.take(*j as usize).collect::(), 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::()); + inverse.insert( + &chars.take(*delete as usize).collect::(), + op.attrs.clone(), + ); }, } } @@ -441,3 +475,10 @@ impl Delta { #[inline] pub fn ops(&self) -> &[Operation] { &self.ops } } + +pub fn operation_attrs(operation: &Option) -> Option { + match operation { + None => None, + Some(operation) => operation.attrs.clone(), + } +} diff --git a/rust-lib/flowy-ot/src/lib.rs b/rust-lib/flowy-ot/src/lib.rs index f3439fbaa7..5ab1009b4e 100644 --- a/rust-lib/flowy-ot/src/lib.rs +++ b/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; diff --git a/rust-lib/flowy-ot/src/operation.rs b/rust-lib/flowy-ot/src/operation.rs index 53a707568d..a2bb2d9408 100644 --- a/rust-lib/flowy-ot/src/operation.rs +++ b/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, } 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) { 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, } 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) -> OperationBuilder { self.attrs = attrs; self } diff --git a/rust-lib/flowy-ot/tests/helper/mod.rs b/rust-lib/flowy-ot/tests/helper/mod.rs index c587518016..450583dc6a 100644 --- a/rust-lib/flowy-ot/tests/helper/mod.rs +++ b/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 } } diff --git a/rust-lib/flowy-ot/tests/op_test.rs b/rust-lib/flowy-ot/tests/op_test.rs index 4c1d3dd9fd..d458c5fc39 100644 --- a/rust-lib/flowy-ot/tests/op_test.rs +++ b/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]