config ot invert detal

This commit is contained in:
appflowy 2021-08-04 15:09:04 +08:00
parent 958918e0a0
commit 80a94880fc
8 changed files with 357 additions and 218 deletions

View File

@ -27,6 +27,7 @@ impl Attributes {
} }
} }
// remove attribute if the value is PLAIN // remove attribute if the value is PLAIN
// { "k": PLAIN } -> {}
pub fn remove_plain(&mut self) { pub fn remove_plain(&mut self) {
match self { match self {
Attributes::Follow => {}, Attributes::Follow => {},
@ -36,6 +37,14 @@ impl Attributes {
Attributes::Empty => {}, Attributes::Empty => {},
} }
} }
pub fn get_attributes_data(&self) -> Option<AttributesData> {
match self {
Attributes::Follow => None,
Attributes::Custom(data) => Some(data.clone()),
Attributes::Empty => None,
}
}
} }
impl std::default::Default for Attributes { impl std::default::Default for Attributes {
@ -131,7 +140,7 @@ impl AttrsBuilder {
pub fn build(self) -> Attributes { Attributes::Custom(self.inner) } pub fn build(self) -> Attributes { Attributes::Custom(self.inner) }
} }
pub fn attributes_from(operation: &Option<Operation>) -> Option<Attributes> { pub(crate) fn attributes_from(operation: &Option<Operation>) -> Option<Attributes> {
match operation { match operation {
None => None, None => None,
Some(operation) => Some(operation.get_attributes()), Some(operation) => Some(operation.get_attributes()),
@ -196,6 +205,37 @@ pub fn transform_attributes(
} }
} }
pub fn invert_attributes(attr: Attributes, base: Attributes) -> Attributes {
let attr = attr.get_attributes_data();
let base = base.get_attributes_data();
if attr.is_none() && base.is_none() {
return Attributes::Empty;
}
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.remove(k);
}
attributes
});
return Attributes::Custom(inverted);
}
fn transform_attribute_data(left: AttributesData, right: AttributesData) -> AttributesData { fn transform_attribute_data(left: AttributesData, right: AttributesData) -> AttributesData {
let result = right let result = right
.iter() .iter()
@ -207,29 +247,3 @@ fn transform_attribute_data(left: AttributesData, right: AttributesData) -> Attr
}); });
result 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;
// }

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
core::{attributes::*, operation::*}, core::{attributes::*, operation::*, Interval},
errors::OTError, errors::OTError,
}; };
use bytecount::num_chars; use bytecount::num_chars;
@ -70,7 +70,7 @@ impl Delta {
match op { match op {
Operation::Delete(i) => self.delete(i), Operation::Delete(i) => self.delete(i),
Operation::Insert(i) => self.insert(&i.s, i.attributes), Operation::Insert(i) => self.insert(&i.s, i.attributes),
Operation::Retain(r) => self.retain(r.num, r.attributes), Operation::Retain(r) => self.retain(r.n, r.attributes),
} }
} }
@ -95,11 +95,11 @@ impl Delta {
let new_last = match self.ops.as_mut_slice() { let new_last = match self.ops.as_mut_slice() {
[.., Operation::Insert(insert)] => { [.., Operation::Insert(insert)] => {
// //
merge_insert_or_new_op(insert, s, attrs) insert.merge_or_new_op(s, attrs)
}, },
[.., Operation::Insert(pre_insert), Operation::Delete(_)] => { [.., Operation::Insert(pre_insert), Operation::Delete(_)] => {
// //
merge_insert_or_new_op(pre_insert, s, attrs) pre_insert.merge_or_new_op(s, attrs)
}, },
[.., op_last @ Operation::Delete(_)] => { [.., op_last @ Operation::Delete(_)] => {
let new_last = op_last.clone(); let new_last = op_last.clone();
@ -123,9 +123,8 @@ impl Delta {
self.target_len += n as usize; self.target_len += n as usize;
if let Some(Operation::Retain(retain)) = self.ops.last_mut() { if let Some(Operation::Retain(retain)) = self.ops.last_mut() {
match merge_retain_or_new_op(retain, n, attrs) { if let Some(new_op) = retain.merge_or_new_op(n, attrs) {
None => {}, self.ops.push(new_op);
Some(new_op) => self.ops.push(new_op),
} }
} else { } else {
self.ops self.ops
@ -175,24 +174,24 @@ impl Delta {
let composed_attrs = compose_attributes(&next_op1, &next_op2); let composed_attrs = compose_attributes(&next_op1, &next_op2);
log::debug!( log::debug!(
"[retain:{} - retain:{}]: {:?}", "[retain:{} - retain:{}]: {:?}",
retain.num, retain.n,
o_retain.num, o_retain.n,
composed_attrs composed_attrs
); );
match retain.cmp(&o_retain) { match retain.cmp(&o_retain) {
Ordering::Less => { Ordering::Less => {
new_delta.retain(retain.num, composed_attrs); new_delta.retain(retain.n, composed_attrs);
next_op2 = Some(OpBuilder::retain(o_retain.num - retain.num).build()); next_op2 = Some(OpBuilder::retain(o_retain.n - retain.n).build());
next_op1 = ops1.next(); next_op1 = ops1.next();
}, },
std::cmp::Ordering::Equal => { std::cmp::Ordering::Equal => {
new_delta.retain(retain.num, composed_attrs); new_delta.retain(retain.n, composed_attrs);
next_op1 = ops1.next(); next_op1 = ops1.next();
next_op2 = ops2.next(); next_op2 = ops2.next();
}, },
std::cmp::Ordering::Greater => { std::cmp::Ordering::Greater => {
new_delta.retain(o_retain.num, composed_attrs); new_delta.retain(o_retain.n, composed_attrs);
next_op1 = Some(OpBuilder::retain(retain.num - o_retain.num).build()); next_op1 = Some(OpBuilder::retain(retain.n - o_retain.n).build());
next_op2 = ops2.next(); next_op2 = ops2.next();
}, },
} }
@ -227,14 +226,14 @@ impl Delta {
log::debug!( log::debug!(
"[insert:{} - retain:{}]: {:?}", "[insert:{} - retain:{}]: {:?}",
insert.s, insert.s,
o_retain.num, o_retain.n,
composed_attrs composed_attrs
); );
match (insert.num_chars()).cmp(o_retain) { match (insert.num_chars()).cmp(o_retain) {
Ordering::Less => { Ordering::Less => {
new_delta.insert(&insert.s, composed_attrs.clone()); new_delta.insert(&insert.s, composed_attrs.clone());
next_op2 = Some( next_op2 = Some(
OpBuilder::retain(o_retain.num - insert.num_chars()) OpBuilder::retain(o_retain.n - insert.num_chars())
.attributes(composed_attrs.clone()) .attributes(composed_attrs.clone())
.build(), .build(),
); );
@ -248,7 +247,7 @@ impl Delta {
Ordering::Greater => { Ordering::Greater => {
let chars = &mut insert.chars(); let chars = &mut insert.chars();
new_delta.insert( new_delta.insert(
&chars.take(o_retain.num as usize).collect::<String>(), &chars.take(o_retain.n as usize).collect::<String>(),
composed_attrs, composed_attrs,
); );
next_op1 = Some( next_op1 = Some(
@ -263,8 +262,8 @@ impl Delta {
(Some(Operation::Retain(retain)), Some(Operation::Delete(o_num))) => { (Some(Operation::Retain(retain)), Some(Operation::Delete(o_num))) => {
match retain.cmp(&o_num) { match retain.cmp(&o_num) {
Ordering::Less => { Ordering::Less => {
new_delta.delete(retain.num); new_delta.delete(retain.n);
next_op2 = Some(OpBuilder::delete(*o_num - retain.num).build()); next_op2 = Some(OpBuilder::delete(*o_num - retain.n).build());
next_op1 = ops1.next(); next_op1 = ops1.next();
}, },
Ordering::Equal => { Ordering::Equal => {
@ -274,7 +273,7 @@ impl Delta {
}, },
Ordering::Greater => { Ordering::Greater => {
new_delta.delete(*o_num); new_delta.delete(*o_num);
next_op1 = Some(OpBuilder::retain(retain.num - *o_num).build()); next_op1 = Some(OpBuilder::retain(retain.n - *o_num).build());
next_op2 = ops2.next(); next_op2 = ops2.next();
}, },
} }
@ -331,21 +330,21 @@ impl Delta {
let composed_attrs = transform_attributes(&next_op1, &next_op2, true); let composed_attrs = transform_attributes(&next_op1, &next_op2, true);
match retain.cmp(&o_retain) { match retain.cmp(&o_retain) {
Ordering::Less => { Ordering::Less => {
a_prime.retain(retain.num, composed_attrs.clone()); a_prime.retain(retain.n, composed_attrs.clone());
b_prime.retain(retain.num, composed_attrs.clone()); b_prime.retain(retain.n, composed_attrs.clone());
next_op2 = Some(OpBuilder::retain(o_retain.num - retain.num).build()); next_op2 = Some(OpBuilder::retain(o_retain.n - retain.n).build());
next_op1 = ops1.next(); next_op1 = ops1.next();
}, },
Ordering::Equal => { Ordering::Equal => {
a_prime.retain(retain.num, composed_attrs.clone()); a_prime.retain(retain.n, composed_attrs.clone());
b_prime.retain(retain.num, composed_attrs.clone()); b_prime.retain(retain.n, composed_attrs.clone());
next_op1 = ops1.next(); next_op1 = ops1.next();
next_op2 = ops2.next(); next_op2 = ops2.next();
}, },
Ordering::Greater => { Ordering::Greater => {
a_prime.retain(o_retain.num, composed_attrs.clone()); a_prime.retain(o_retain.n, composed_attrs.clone());
b_prime.retain(o_retain.num, composed_attrs.clone()); b_prime.retain(o_retain.n, composed_attrs.clone());
next_op1 = Some(OpBuilder::retain(retain.num - o_retain.num).build()); next_op1 = Some(OpBuilder::retain(retain.n - o_retain.n).build());
next_op2 = ops2.next(); next_op2 = ops2.next();
}, },
}; };
@ -368,7 +367,7 @@ impl Delta {
match i.cmp(&o_retain) { match i.cmp(&o_retain) {
Ordering::Less => { Ordering::Less => {
a_prime.delete(*i); a_prime.delete(*i);
next_op2 = Some(OpBuilder::retain(o_retain.num - *i).build()); next_op2 = Some(OpBuilder::retain(o_retain.n - *i).build());
next_op1 = ops1.next(); next_op1 = ops1.next();
}, },
Ordering::Equal => { Ordering::Equal => {
@ -377,8 +376,8 @@ impl Delta {
next_op2 = ops2.next(); next_op2 = ops2.next();
}, },
Ordering::Greater => { Ordering::Greater => {
a_prime.delete(o_retain.num); a_prime.delete(o_retain.n);
next_op1 = Some(OpBuilder::delete(*i - o_retain.num).build()); next_op1 = Some(OpBuilder::delete(*i - o_retain.n).build());
next_op2 = ops2.next(); next_op2 = ops2.next();
}, },
}; };
@ -386,18 +385,18 @@ impl Delta {
(Some(Operation::Retain(retain)), Some(Operation::Delete(j))) => { (Some(Operation::Retain(retain)), Some(Operation::Delete(j))) => {
match retain.cmp(&j) { match retain.cmp(&j) {
Ordering::Less => { Ordering::Less => {
b_prime.delete(retain.num); b_prime.delete(retain.n);
next_op2 = Some(OpBuilder::delete(*j - retain.num).build()); next_op2 = Some(OpBuilder::delete(*j - retain.n).build());
next_op1 = ops1.next(); next_op1 = ops1.next();
}, },
Ordering::Equal => { Ordering::Equal => {
b_prime.delete(retain.num); b_prime.delete(retain.n);
next_op1 = ops1.next(); next_op1 = ops1.next();
next_op2 = ops2.next(); next_op2 = ops2.next();
}, },
Ordering::Greater => { Ordering::Greater => {
b_prime.delete(*j); b_prime.delete(*j);
next_op1 = Some(OpBuilder::retain(retain.num - *j).build()); next_op1 = Some(OpBuilder::retain(retain.n - *j).build());
next_op2 = ops2.next(); next_op2 = ops2.next();
}, },
}; };
@ -423,7 +422,7 @@ impl Delta {
for op in &self.ops { for op in &self.ops {
match &op { match &op {
Operation::Retain(retain) => { Operation::Retain(retain) => {
for c in chars.take(retain.num as usize) { for c in chars.take(retain.n as usize) {
new_s.push(c); new_s.push(c);
} }
}, },
@ -448,8 +447,11 @@ impl Delta {
for op in &self.ops { for op in &self.ops {
match &op { match &op {
Operation::Retain(retain) => { Operation::Retain(retain) => {
inverted.retain(retain.num, Attributes::Follow); inverted.retain(retain.n, Attributes::Follow);
for _ in 0..retain.num {
// TODO: use advance_by instead, but it's unstable now
// chars.advance_by(retain.num)
for _ in 0..retain.n {
chars.next(); chars.next();
} }
}, },
@ -467,6 +469,54 @@ impl Delta {
inverted inverted
} }
pub fn invert_delta(&self, other: &Delta) -> Delta {
let mut inverted = Delta::default();
if other.is_empty() {
return inverted;
}
let a = |inverted: &mut Delta, op: &Operation, index: usize, op_len: usize| {
let ops = other.ops_in_interval(Interval::new(index, op_len));
ops.into_iter().for_each(|other_op| match op {
Operation::Delete(_) => {
inverted.add(other_op);
},
Operation::Retain(_) => {},
Operation::Insert(_) => {
if !op.is_plain() {
let inverted_attrs =
invert_attributes(op.get_attributes(), other_op.get_attributes());
inverted.retain(other_op.length(), inverted_attrs);
}
},
});
};
let mut index = 0;
for op in &self.ops {
let op_len: usize = op.length() as usize;
match op {
Operation::Delete(_) => {
a(&mut inverted, op, index, op_len);
index += op_len;
},
Operation::Retain(_) => {
if op.is_plain() {
inverted.retain(op_len as u64, Attributes::Empty);
} else {
a(&mut inverted, op, index, op_len as usize);
}
index += op_len;
},
Operation::Insert(insert) => {
inverted.delete(op_len as u64);
},
}
}
inverted
}
/// Checks if this operation has no effect. /// Checks if this operation has no effect.
#[inline] #[inline]
pub fn is_noop(&self) -> bool { pub fn is_noop(&self) -> bool {
@ -477,66 +527,64 @@ impl Delta {
} }
} }
/// Returns the length of a string these operations can be applied to
#[inline]
pub fn base_len(&self) -> usize { self.base_len }
/// Returns the length of the resulting string after the operations have
/// been applied.
#[inline]
pub fn target_len(&self) -> usize { self.target_len }
/// Returns the wrapped sequence of operations.
#[inline]
pub fn ops(&self) -> &[Operation] { &self.ops }
pub fn is_empty(&self) -> bool { self.ops.is_empty() } pub fn is_empty(&self) -> bool { self.ops.is_empty() }
}
fn merge_insert_or_new_op( pub fn ops_in_interval(&self, interval: Interval) -> Vec<Operation> {
insert: &mut Insert, let mut ops: Vec<Operation> = Vec::with_capacity(self.ops.len());
s: &str, let mut offset: usize = 0;
attributes: Attributes, let mut ops_iter = self.ops.iter();
) -> Option<Operation> { let mut op = ops_iter.next();
match &attributes {
Attributes::Follow => { while offset < interval.end && op.is_some() {
insert.s += s; if let Some(op) = op {
return None; if offset < interval.start {
}, offset += op.length() as usize;
Attributes::Custom(_) | Attributes::Empty => { } else {
if insert.attributes == attributes { ops.push(op.clone());
insert.s += s; offset += op.length() as usize;
None }
} else {
Some(OpBuilder::insert(s).attributes(attributes).build())
} }
}, op = ops_iter.next();
} }
}
ops
fn merge_retain_or_new_op( }
retain: &mut Retain,
n: u64, pub fn attributes_in_interval(&self, interval: Interval) -> Attributes {
attributes: Attributes, let mut attributes_data = AttributesData::new();
) -> Option<Operation> { let mut offset: usize = 0;
log::debug!(
"merge_retain_or_new_op: {:?}, {:?}", self.ops.iter().for_each(|op| match op {
retain.attributes, Operation::Delete(_n) => {},
attributes Operation::Retain(_retain) => {
); unimplemented!()
// if interval.contains(retain.n as usize) {
match &attributes { // match &retain.attributes {
Attributes::Follow => { // Attributes::Follow => {},
retain.num += n; // Attributes::Custom(data) => {
None // attributes_data.extend(data.clone());
}, // },
Attributes::Custom(_) | Attributes::Empty => { // Attributes::Empty => {},
if retain.attributes == attributes { // }
retain.num += n; // }
None },
} else { Operation::Insert(insert) => match &insert.attributes {
Some(OpBuilder::retain(n).attributes(attributes).build()) Attributes::Follow => {},
} Attributes::Custom(data) => {
}, let end = insert.num_chars() as usize;
if interval.contains_range(offset, offset + end) {
attributes_data.extend(data.clone());
}
offset += end;
},
Attributes::Empty => {},
},
});
if attributes_data.is_plain() {
Attributes::Empty
} else {
Attributes::Custom(attributes_data)
}
} }
} }

View File

@ -33,6 +33,10 @@ impl Interval {
pub fn contains(&self, val: usize) -> bool { self.start <= val && val < self.end } pub fn contains(&self, val: usize) -> bool { self.start <= val && val < self.end }
pub fn contains_range(&self, start: usize, end: usize) -> bool {
!self.intersect(Interval::new(start, end)).is_empty()
}
pub fn is_after(&self, val: usize) -> bool { self.start > val } pub fn is_after(&self, val: usize) -> bool { self.start > val }
pub fn is_empty(&self) -> bool { self.end <= self.start } pub fn is_empty(&self) -> bool { self.end <= self.start }

View File

@ -73,10 +73,11 @@ impl Operation {
pub fn length(&self) -> u64 { pub fn length(&self) -> u64 {
match self { match self {
Operation::Delete(n) => *n, Operation::Delete(n) => *n,
Operation::Retain(r) => r.num, Operation::Retain(r) => r.n,
Operation::Insert(i) => i.num_chars(), Operation::Insert(i) => i.num_chars(),
} }
} }
pub fn is_empty(&self) -> bool { self.length() == 0 } pub fn is_empty(&self) -> bool { self.length() == 0 }
} }
@ -89,7 +90,7 @@ impl fmt::Display for Operation {
Operation::Retain(r) => { Operation::Retain(r) => {
f.write_fmt(format_args!( f.write_fmt(format_args!(
"retain: {}, attributes: {}", "retain: {}, attributes: {}",
r.num, r.attributes r.n, r.attributes
))?; ))?;
}, },
Operation::Insert(i) => { Operation::Insert(i) => {
@ -141,15 +142,40 @@ impl OpBuilder {
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Retain { pub struct Retain {
#[serde(rename(serialize = "retain", deserialize = "retain"))] #[serde(rename(serialize = "retain", deserialize = "retain"))]
pub num: u64, pub n: u64,
#[serde(skip_serializing_if = "is_empty")] #[serde(skip_serializing_if = "is_empty")]
pub attributes: Attributes, pub attributes: Attributes,
} }
impl Retain {
pub fn merge_or_new_op(&mut self, n: u64, attributes: Attributes) -> Option<Operation> {
log::debug!(
"merge_retain_or_new_op: {:?}, {:?}",
self.attributes,
attributes
);
match &attributes {
Attributes::Follow => {
self.n += n;
None
},
Attributes::Custom(_) | Attributes::Empty => {
if self.attributes == attributes {
self.n += n;
None
} else {
Some(OpBuilder::retain(n).attributes(attributes).build())
}
},
}
}
}
impl std::convert::From<u64> for Retain { impl std::convert::From<u64> for Retain {
fn from(n: u64) -> Self { fn from(n: u64) -> Self {
Retain { Retain {
num: n, n,
attributes: Attributes::default(), attributes: Attributes::default(),
} }
} }
@ -158,11 +184,11 @@ impl std::convert::From<u64> for Retain {
impl Deref for Retain { impl Deref for Retain {
type Target = u64; type Target = u64;
fn deref(&self) -> &Self::Target { &self.num } fn deref(&self) -> &Self::Target { &self.n }
} }
impl DerefMut for Retain { impl DerefMut for Retain {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.num } fn deref_mut(&mut self) -> &mut Self::Target { &mut self.n }
} }
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
@ -180,6 +206,23 @@ impl Insert {
pub fn chars(&self) -> Chars<'_> { self.s.chars() } pub fn chars(&self) -> Chars<'_> { self.s.chars() }
pub fn num_chars(&self) -> u64 { num_chars(self.s.as_bytes()) as _ } pub fn num_chars(&self) -> u64 { num_chars(self.s.as_bytes()) as _ }
pub fn merge_or_new_op(&mut self, s: &str, attributes: Attributes) -> Option<Operation> {
match &attributes {
Attributes::Follow => {
self.s += s;
return None;
},
Attributes::Custom(_) | Attributes::Empty => {
if self.attributes == attributes {
self.s += s;
None
} else {
Some(OpBuilder::insert(s).attributes(attributes).build())
}
},
}
}
} }
impl std::convert::From<String> for Insert { impl std::convert::From<String> for Insert {

View File

@ -1,6 +1,6 @@
pub mod helper; pub mod helper;
use crate::helper::{MergeTestOp::*, *}; use crate::helper::{TestOp::*, *};
use flowy_ot::core::Interval; use flowy_ot::core::Interval;
#[test] #[test]
@ -10,7 +10,7 @@ fn delta_insert_text() {
Insert(0, "456", 3), Insert(0, "456", 3),
AssertOpsJson(0, r#"[{"insert":"123456"}]"#), AssertOpsJson(0, r#"[{"insert":"123456"}]"#),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -20,7 +20,7 @@ fn delta_insert_text_at_head() {
Insert(0, "456", 0), Insert(0, "456", 0),
AssertOpsJson(0, r#"[{"insert":"456123"}]"#), AssertOpsJson(0, r#"[{"insert":"456123"}]"#),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -30,7 +30,7 @@ fn delta_insert_text_at_middle() {
Insert(0, "456", 1), Insert(0, "456", 1),
AssertOpsJson(0, r#"[{"insert":"145623"}]"#), AssertOpsJson(0, r#"[{"insert":"145623"}]"#),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -49,7 +49,7 @@ fn delta_insert_text_with_attr() {
r#"[{"insert":"1abc2","attributes":{"bold":"true"}},{"insert":"345"}]"#, r#"[{"insert":"1abc2","attributes":{"bold":"true"}},{"insert":"345"}]"#,
), ),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -61,7 +61,7 @@ fn delta_add_bold_and_invert_all() {
Bold(0, Interval::new(0, 3), false), Bold(0, Interval::new(0, 3), false),
AssertOpsJson(0, r#"[{"insert":"123"}]"#), AssertOpsJson(0, r#"[{"insert":"123"}]"#),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -76,7 +76,7 @@ fn delta_add_bold_and_invert_partial_suffix() {
r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#, r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#,
), ),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -93,7 +93,7 @@ fn delta_add_bold_and_invert_partial_suffix2() {
Bold(0, Interval::new(2, 4), true), Bold(0, Interval::new(2, 4), true),
AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#), AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -108,7 +108,7 @@ fn delta_add_bold_and_invert_partial_prefix() {
r#"[{"insert":"12"},{"insert":"34","attributes":{"bold":"true"}}]"#, r#"[{"insert":"12"},{"insert":"34","attributes":{"bold":"true"}}]"#,
), ),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -126,14 +126,14 @@ fn delta_add_bold_consecutive() {
r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#, r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#,
), ),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn delta_add_bold_empty_str() { fn delta_add_bold_empty_str() {
let ops = vec![Bold(0, Interval::new(0, 4), true)]; let ops = vec![Bold(0, Interval::new(0, 4), true)];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -157,7 +157,7 @@ fn delta_add_bold_italic() {
r#"[{"insert":"1234","attributes":{"italic":"true","bold":"true"}},{"insert":"56"},{"insert":"78","attributes":{"bold":"true","italic":"true"}}]"#, r#"[{"insert":"1234","attributes":{"italic":"true","bold":"true"}},{"insert":"56"},{"insert":"78","attributes":{"bold":"true","italic":"true"}}]"#,
), ),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -178,7 +178,7 @@ fn delta_add_bold_italic2() {
), ),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -203,7 +203,7 @@ fn delta_add_bold_italic3() {
), ),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -225,7 +225,7 @@ fn delta_add_bold_italic_delete() {
), ),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -236,7 +236,7 @@ fn delta_merge_inserted_text_with_same_attribute() {
InsertBold(0, "456", Interval::new(3, 6)), InsertBold(0, "456", Interval::new(3, 6)),
AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#), AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -251,7 +251,7 @@ fn delta_compose_attr_delta_with_attr_delta_test() {
AssertOpsJson(1, r#"[{"insert":"1234567","attributes":{"bold":"true"}}]"#), AssertOpsJson(1, r#"[{"insert":"1234567","attributes":{"bold":"true"}}]"#),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -278,7 +278,7 @@ fn delta_compose_attr_delta_with_attr_delta_test2() {
), ),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -293,7 +293,7 @@ fn delta_compose_attr_delta_with_no_attr_delta_test() {
AssertOpsJson(0, expected), AssertOpsJson(0, expected),
AssertOpsJson(1, expected), AssertOpsJson(1, expected),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -305,7 +305,7 @@ fn delta_delete_heading() {
AssertOpsJson(0, r#"[{"insert":"3456","attributes":{"bold":"true"}}]"#), AssertOpsJson(0, r#"[{"insert":"3456","attributes":{"bold":"true"}}]"#),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -317,7 +317,7 @@ fn delta_delete_trailing() {
AssertOpsJson(0, r#"[{"insert":"12345","attributes":{"bold":"true"}}]"#), AssertOpsJson(0, r#"[{"insert":"12345","attributes":{"bold":"true"}}]"#),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -331,7 +331,7 @@ fn delta_delete_middle() {
AssertOpsJson(0, r#"[{"insert":"34","attributes":{"bold":"true"}}]"#), AssertOpsJson(0, r#"[{"insert":"34","attributes":{"bold":"true"}}]"#),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]
@ -343,5 +343,5 @@ fn delta_delete_all() {
AssertOpsJson(0, r#"[]"#), AssertOpsJson(0, r#"[]"#),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }

View File

@ -3,7 +3,7 @@ use rand::{prelude::*, Rng as WrappedRng};
use std::sync::Once; use std::sync::Once;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum MergeTestOp { pub enum TestOp {
Insert(usize, &'static str, usize), Insert(usize, &'static str, usize),
// delta_i, s, start, length, // delta_i, s, start, length,
InsertBold(usize, &'static str, Interval), InsertBold(usize, &'static str, Interval),
@ -16,11 +16,11 @@ pub enum MergeTestOp {
AssertOpsJson(usize, &'static str), AssertOpsJson(usize, &'static str),
} }
pub struct MergeTest { pub struct OpTester {
deltas: Vec<Delta>, deltas: Vec<Delta>,
} }
impl MergeTest { impl OpTester {
pub fn new() -> Self { pub fn new() -> Self {
static INIT: Once = Once::new(); static INIT: Once = Once::new();
INIT.call_once(|| { INIT.call_once(|| {
@ -36,29 +36,29 @@ impl MergeTest {
Self { deltas } Self { deltas }
} }
pub fn run_op(&mut self, op: &MergeTestOp) { pub fn run_op(&mut self, op: &TestOp) {
match op { match op {
MergeTestOp::Insert(delta_i, s, index) => { TestOp::Insert(delta_i, s, index) => {
self.update_delta_with_insert(*delta_i, s, *index); self.update_delta_with_insert(*delta_i, s, *index);
}, },
MergeTestOp::Delete(delta_i, interval) => { TestOp::Delete(delta_i, interval) => {
// //
self.update_delta_with_delete(*delta_i, interval); self.update_delta_with_delete(*delta_i, interval);
}, },
MergeTestOp::InsertBold(delta_i, s, _interval) => { TestOp::InsertBold(delta_i, s, _interval) => {
let attrs = AttrsBuilder::new().bold(true).build(); let attrs = AttrsBuilder::new().bold(true).build();
let delta = &mut self.deltas[*delta_i]; let delta = &mut self.deltas[*delta_i];
delta.insert(s, attrs); delta.insert(s, attrs);
}, },
MergeTestOp::Bold(delta_i, interval, enable) => { TestOp::Bold(delta_i, interval, enable) => {
let attrs = AttrsBuilder::new().bold(*enable).build(); let attrs = AttrsBuilder::new().bold(*enable).build();
self.update_delta_with_attribute(*delta_i, attrs, interval); self.update_delta_with_attribute(*delta_i, attrs, interval);
}, },
MergeTestOp::Italic(delta_i, interval, enable) => { TestOp::Italic(delta_i, interval, enable) => {
let attrs = AttrsBuilder::new().italic(*enable).build(); let attrs = AttrsBuilder::new().italic(*enable).build();
self.update_delta_with_attribute(*delta_i, attrs, interval); self.update_delta_with_attribute(*delta_i, attrs, interval);
}, },
MergeTestOp::Transform(delta_a_i, delta_b_i) => { TestOp::Transform(delta_a_i, delta_b_i) => {
let delta_a = &self.deltas[*delta_a_i]; let delta_a = &self.deltas[*delta_a_i];
let delta_b = &self.deltas[*delta_b_i]; let delta_b = &self.deltas[*delta_b_i];
@ -70,12 +70,12 @@ impl MergeTest {
self.deltas[*delta_a_i] = new_delta_a; self.deltas[*delta_a_i] = new_delta_a;
self.deltas[*delta_b_i] = new_delta_b; self.deltas[*delta_b_i] = new_delta_b;
}, },
MergeTestOp::AssertStr(delta_i, expected) => { TestOp::AssertStr(delta_i, expected) => {
let s = self.deltas[*delta_i].apply("").unwrap(); let s = self.deltas[*delta_i].apply("").unwrap();
assert_eq!(&s, expected); assert_eq!(&s, expected);
}, },
MergeTestOp::AssertOpsJson(delta_i, expected) => { TestOp::AssertOpsJson(delta_i, expected) => {
let delta_i_json = serde_json::to_string(&self.deltas[*delta_i]).unwrap(); let delta_i_json = serde_json::to_string(&self.deltas[*delta_i]).unwrap();
let expected_delta: Delta = serde_json::from_str(expected).unwrap(); let expected_delta: Delta = serde_json::from_str(expected).unwrap();
@ -90,12 +90,14 @@ impl MergeTest {
} }
} }
pub fn run_script(&mut self, script: Vec<MergeTestOp>) { pub fn run_script(&mut self, script: Vec<TestOp>) {
for (_i, op) in script.iter().enumerate() { for (_i, op) in script.iter().enumerate() {
self.run_op(op); self.run_op(op);
} }
} }
pub fn get_delta(&mut self, index: usize) -> &mut Delta { &mut self.deltas[index] }
pub fn update_delta_with_insert(&mut self, delta_index: usize, s: &str, index: usize) { pub fn update_delta_with_insert(&mut self, delta_index: usize, s: &str, index: usize) {
let old_delta = &mut self.deltas[delta_index]; let old_delta = &mut self.deltas[delta_index];
let target_interval = Interval::new(0, old_delta.target_len); let target_interval = Interval::new(0, old_delta.target_len);
@ -103,7 +105,7 @@ impl MergeTest {
log::error!("{} out of bounds {}", index, target_interval); log::error!("{} out of bounds {}", index, target_interval);
} }
let mut attributes = attributes_in_delta(old_delta, &Interval::new(index, index + 1)); let mut attributes = old_delta.attributes_in_interval(Interval::new(index, index + 1));
if attributes == Attributes::Empty { if attributes == Attributes::Empty {
attributes = Attributes::Follow; attributes = Attributes::Follow;
} }
@ -123,8 +125,8 @@ impl MergeTest {
.attributes(attributes) .attributes(attributes)
.build(); .build();
let attrs = attributes_in_delta(old_delta, &interval); let attributes = old_delta.attributes_in_interval(*interval);
retain.extend_attributes(attrs); retain.extend_attributes(attributes);
let new_delta = new_delta_with_op(old_delta, retain, *interval); let new_delta = new_delta_with_op(old_delta, retain, *interval);
self.deltas[delta_index] = new_delta; self.deltas[delta_index] = new_delta;
@ -133,8 +135,8 @@ impl MergeTest {
pub fn update_delta_with_delete(&mut self, delta_index: usize, interval: &Interval) { pub fn update_delta_with_delete(&mut self, delta_index: usize, interval: &Interval) {
let old_delta = &self.deltas[delta_index]; let old_delta = &self.deltas[delta_index];
let mut delete = OpBuilder::delete(interval.size() as u64).build(); let mut delete = OpBuilder::delete(interval.size() as u64).build();
let attrs = attributes_in_delta(old_delta, &interval); let attributes = old_delta.attributes_in_interval(*interval);
delete.extend_attributes(attrs); delete.extend_attributes(attributes);
let new_delta = new_delta_with_op(old_delta, delete, *interval); let new_delta = new_delta_with_op(old_delta, delete, *interval);
self.deltas[delta_index] = new_delta; self.deltas[delta_index] = new_delta;
@ -149,8 +151,8 @@ fn new_delta_with_op(delta: &Delta, op: Operation, interval: Interval) -> Delta
if prefix.is_empty() == false && prefix != interval { if prefix.is_empty() == false && prefix != interval {
let intervals = split_interval_with_delta(delta, &prefix); let intervals = split_interval_with_delta(delta, &prefix);
intervals.into_iter().for_each(|interval| { intervals.into_iter().for_each(|interval| {
let attrs = attributes_in_delta(delta, &interval); let attributes = delta.attributes_in_interval(interval);
new_delta.retain(interval.size() as u64, attrs); new_delta.retain(interval.size() as u64, attributes);
}); });
} }
@ -160,8 +162,8 @@ fn new_delta_with_op(delta: &Delta, op: Operation, interval: Interval) -> Delta
if suffix.is_empty() == false { if suffix.is_empty() == false {
let intervals = split_interval_with_delta(delta, &suffix); let intervals = split_interval_with_delta(delta, &suffix);
intervals.into_iter().for_each(|interval| { intervals.into_iter().for_each(|interval| {
let attrs = attributes_in_delta(delta, &interval); let attributes = delta.attributes_in_interval(interval);
new_delta.retain(interval.size() as u64, attrs); new_delta.retain(interval.size() as u64, attributes);
}); });
} }
@ -200,47 +202,7 @@ pub fn target_length_split_with_interval(
} }
pub fn debug_print_delta(delta: &Delta) { pub fn debug_print_delta(delta: &Delta) {
log::debug!("😁 {}", serde_json::to_string(delta).unwrap()); eprintln!("😁 {}", serde_json::to_string(delta).unwrap());
}
pub fn attributes_in_delta(delta: &Delta, interval: &Interval) -> Attributes {
let mut attributes_data = AttributesData::new();
let mut offset: usize = 0;
delta.ops.iter().for_each(|op| match op {
Operation::Delete(_n) => {},
Operation::Retain(retain) => {
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) => match &insert.attributes {
Attributes::Follow => {},
Attributes::Custom(data) => {
let end = insert.num_chars() as usize;
if !interval
.intersect(Interval::new(offset, offset + end))
.is_empty()
{
attributes_data.extend(data.clone());
}
offset += end;
},
Attributes::Empty => {},
},
});
if attributes_data.is_plain() {
Attributes::Empty
} else {
Attributes::Custom(attributes_data)
}
} }
pub struct Rng(StdRng); pub struct Rng(StdRng);
@ -259,7 +221,7 @@ impl Rng {
pub fn gen_delta(&mut self, s: &str) -> Delta { pub fn gen_delta(&mut self, s: &str) -> Delta {
let mut delta = Delta::default(); let mut delta = Delta::default();
loop { loop {
let left = s.chars().count() - delta.base_len(); let left = s.chars().count() - delta.base_len;
if left == 0 { if left == 0 {
break; break;
} }

View File

@ -0,0 +1,68 @@
pub mod helper;
use crate::helper::{TestOp::*, *};
use flowy_ot::core::{Delta, Interval, OpBuilder};
#[test]
fn delta_invert_delta_test() {
let mut delta = Delta::default();
delta.add(OpBuilder::insert("123").build());
let mut change = Delta::default();
change.add(OpBuilder::retain(3).build());
change.add(OpBuilder::insert("456").build());
let undo = change.invert_delta(&delta);
let new_delta = delta.compose(&change).unwrap();
let delta_after_undo = new_delta.compose(&undo).unwrap();
assert_eq!(delta_after_undo, delta);
}
#[test]
fn delta_get_ops_in_interval_1() {
let mut delta = Delta::default();
let insert_a = OpBuilder::insert("123").build();
let insert_b = OpBuilder::insert("4").build();
delta.add(insert_a.clone());
delta.add(insert_b.clone());
assert_eq!(
delta.ops_in_interval(Interval::new(0, 3)),
vec![delta.ops.last().unwrap().clone()]
);
}
#[test]
fn delta_get_ops_in_interval_2() {
let mut delta = Delta::default();
let insert_a = OpBuilder::insert("123").build();
let insert_b = OpBuilder::insert("4").build();
let insert_c = OpBuilder::insert("5").build();
let retain_a = OpBuilder::retain(3).build();
delta.add(insert_a.clone());
delta.add(retain_a.clone());
delta.add(insert_b.clone());
delta.add(insert_c.clone());
assert_eq!(
delta.ops_in_interval(Interval::new(0, 3)),
vec![insert_a.clone()]
);
assert_eq!(
delta.ops_in_interval(Interval::new(0, 4)),
vec![insert_a.clone(), retain_a.clone()]
);
assert_eq!(
delta.ops_in_interval(Interval::new(0, 7)),
vec![
insert_a.clone(),
retain_a.clone(),
// insert_b and insert_c will be merged into one. insert: "45"
delta.ops.last().unwrap().clone()
]
);
}

View File

@ -1,6 +1,6 @@
pub mod helper; pub mod helper;
use crate::helper::MergeTestOp::*; use crate::helper::TestOp::*;
use bytecount::num_chars; use bytecount::num_chars;
use flowy_ot::core::*; use flowy_ot::core::*;
use helper::*; use helper::*;
@ -196,7 +196,7 @@ fn transform2() {
AssertOpsJson(0, r#"[{"insert":"123456"}]"#), AssertOpsJson(0, r#"[{"insert":"123456"}]"#),
AssertOpsJson(1, r#"[{"insert":"123456"}]"#), AssertOpsJson(1, r#"[{"insert":"123456"}]"#),
]; ];
MergeTest::new().run_script(ops); OpTester::new().run_script(ops);
} }
#[test] #[test]