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
// { "k": PLAIN } -> {}
pub fn remove_plain(&mut self) {
match self {
Attributes::Follow => {},
@ -36,6 +37,14 @@ impl Attributes {
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 {
@ -131,7 +140,7 @@ impl AttrsBuilder {
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 {
None => None,
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 {
let result = right
.iter()
@ -207,29 +247,3 @@ fn transform_attribute_data(left: AttributesData, right: AttributesData) -> Attr
});
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::{
core::{attributes::*, operation::*},
core::{attributes::*, operation::*, Interval},
errors::OTError,
};
use bytecount::num_chars;
@ -70,7 +70,7 @@ impl Delta {
match op {
Operation::Delete(i) => self.delete(i),
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() {
[.., Operation::Insert(insert)] => {
//
merge_insert_or_new_op(insert, s, attrs)
insert.merge_or_new_op(s, attrs)
},
[.., 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(_)] => {
let new_last = op_last.clone();
@ -123,9 +123,8 @@ impl Delta {
self.target_len += n as usize;
if let Some(Operation::Retain(retain)) = self.ops.last_mut() {
match merge_retain_or_new_op(retain, n, attrs) {
None => {},
Some(new_op) => self.ops.push(new_op),
if let Some(new_op) = retain.merge_or_new_op(n, attrs) {
self.ops.push(new_op);
}
} else {
self.ops
@ -175,24 +174,24 @@ impl Delta {
let composed_attrs = compose_attributes(&next_op1, &next_op2);
log::debug!(
"[retain:{} - retain:{}]: {:?}",
retain.num,
o_retain.num,
retain.n,
o_retain.n,
composed_attrs
);
match retain.cmp(&o_retain) {
Ordering::Less => {
new_delta.retain(retain.num, composed_attrs);
next_op2 = Some(OpBuilder::retain(o_retain.num - retain.num).build());
new_delta.retain(retain.n, composed_attrs);
next_op2 = Some(OpBuilder::retain(o_retain.n - retain.n).build());
next_op1 = ops1.next();
},
std::cmp::Ordering::Equal => {
new_delta.retain(retain.num, composed_attrs);
new_delta.retain(retain.n, composed_attrs);
next_op1 = ops1.next();
next_op2 = ops2.next();
},
std::cmp::Ordering::Greater => {
new_delta.retain(o_retain.num, composed_attrs);
next_op1 = Some(OpBuilder::retain(retain.num - o_retain.num).build());
new_delta.retain(o_retain.n, composed_attrs);
next_op1 = Some(OpBuilder::retain(retain.n - o_retain.n).build());
next_op2 = ops2.next();
},
}
@ -227,14 +226,14 @@ impl Delta {
log::debug!(
"[insert:{} - retain:{}]: {:?}",
insert.s,
o_retain.num,
o_retain.n,
composed_attrs
);
match (insert.num_chars()).cmp(o_retain) {
Ordering::Less => {
new_delta.insert(&insert.s, composed_attrs.clone());
next_op2 = Some(
OpBuilder::retain(o_retain.num - insert.num_chars())
OpBuilder::retain(o_retain.n - insert.num_chars())
.attributes(composed_attrs.clone())
.build(),
);
@ -248,7 +247,7 @@ impl Delta {
Ordering::Greater => {
let chars = &mut insert.chars();
new_delta.insert(
&chars.take(o_retain.num as usize).collect::<String>(),
&chars.take(o_retain.n as usize).collect::<String>(),
composed_attrs,
);
next_op1 = Some(
@ -263,8 +262,8 @@ impl Delta {
(Some(Operation::Retain(retain)), Some(Operation::Delete(o_num))) => {
match retain.cmp(&o_num) {
Ordering::Less => {
new_delta.delete(retain.num);
next_op2 = Some(OpBuilder::delete(*o_num - retain.num).build());
new_delta.delete(retain.n);
next_op2 = Some(OpBuilder::delete(*o_num - retain.n).build());
next_op1 = ops1.next();
},
Ordering::Equal => {
@ -274,7 +273,7 @@ impl Delta {
},
Ordering::Greater => {
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();
},
}
@ -331,21 +330,21 @@ impl Delta {
let composed_attrs = transform_attributes(&next_op1, &next_op2, true);
match retain.cmp(&o_retain) {
Ordering::Less => {
a_prime.retain(retain.num, composed_attrs.clone());
b_prime.retain(retain.num, composed_attrs.clone());
next_op2 = Some(OpBuilder::retain(o_retain.num - retain.num).build());
a_prime.retain(retain.n, composed_attrs.clone());
b_prime.retain(retain.n, composed_attrs.clone());
next_op2 = Some(OpBuilder::retain(o_retain.n - retain.n).build());
next_op1 = ops1.next();
},
Ordering::Equal => {
a_prime.retain(retain.num, composed_attrs.clone());
b_prime.retain(retain.num, composed_attrs.clone());
a_prime.retain(retain.n, composed_attrs.clone());
b_prime.retain(retain.n, composed_attrs.clone());
next_op1 = ops1.next();
next_op2 = ops2.next();
},
Ordering::Greater => {
a_prime.retain(o_retain.num, composed_attrs.clone());
b_prime.retain(o_retain.num, composed_attrs.clone());
next_op1 = Some(OpBuilder::retain(retain.num - o_retain.num).build());
a_prime.retain(o_retain.n, composed_attrs.clone());
b_prime.retain(o_retain.n, composed_attrs.clone());
next_op1 = Some(OpBuilder::retain(retain.n - o_retain.n).build());
next_op2 = ops2.next();
},
};
@ -368,7 +367,7 @@ impl Delta {
match i.cmp(&o_retain) {
Ordering::Less => {
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();
},
Ordering::Equal => {
@ -377,8 +376,8 @@ impl Delta {
next_op2 = ops2.next();
},
Ordering::Greater => {
a_prime.delete(o_retain.num);
next_op1 = Some(OpBuilder::delete(*i - o_retain.num).build());
a_prime.delete(o_retain.n);
next_op1 = Some(OpBuilder::delete(*i - o_retain.n).build());
next_op2 = ops2.next();
},
};
@ -386,18 +385,18 @@ impl Delta {
(Some(Operation::Retain(retain)), Some(Operation::Delete(j))) => {
match retain.cmp(&j) {
Ordering::Less => {
b_prime.delete(retain.num);
next_op2 = Some(OpBuilder::delete(*j - retain.num).build());
b_prime.delete(retain.n);
next_op2 = Some(OpBuilder::delete(*j - retain.n).build());
next_op1 = ops1.next();
},
Ordering::Equal => {
b_prime.delete(retain.num);
b_prime.delete(retain.n);
next_op1 = ops1.next();
next_op2 = ops2.next();
},
Ordering::Greater => {
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();
},
};
@ -423,7 +422,7 @@ impl Delta {
for op in &self.ops {
match &op {
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);
}
},
@ -448,8 +447,11 @@ impl Delta {
for op in &self.ops {
match &op {
Operation::Retain(retain) => {
inverted.retain(retain.num, Attributes::Follow);
for _ in 0..retain.num {
inverted.retain(retain.n, Attributes::Follow);
// TODO: use advance_by instead, but it's unstable now
// chars.advance_by(retain.num)
for _ in 0..retain.n {
chars.next();
}
},
@ -467,6 +469,54 @@ impl Delta {
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.
#[inline]
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() }
}
fn merge_insert_or_new_op(
insert: &mut Insert,
s: &str,
attributes: Attributes,
) -> Option<Operation> {
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())
pub fn ops_in_interval(&self, interval: Interval) -> Vec<Operation> {
let mut ops: Vec<Operation> = Vec::with_capacity(self.ops.len());
let mut offset: usize = 0;
let mut ops_iter = self.ops.iter();
let mut op = ops_iter.next();
while offset < interval.end && op.is_some() {
if let Some(op) = op {
if offset < interval.start {
offset += op.length() as usize;
} else {
ops.push(op.clone());
offset += op.length() as usize;
}
}
},
}
}
fn merge_retain_or_new_op(
retain: &mut Retain,
n: u64,
attributes: Attributes,
) -> Option<Operation> {
log::debug!(
"merge_retain_or_new_op: {:?}, {:?}",
retain.attributes,
attributes
);
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())
}
},
op = ops_iter.next();
}
ops
}
pub fn attributes_in_interval(&self, interval: Interval) -> Attributes {
let mut attributes_data = AttributesData::new();
let mut offset: usize = 0;
self.ops.iter().for_each(|op| match op {
Operation::Delete(_n) => {},
Operation::Retain(_retain) => {
unimplemented!()
// if interval.contains(retain.n 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.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_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_empty(&self) -> bool { self.end <= self.start }

View File

@ -73,10 +73,11 @@ impl Operation {
pub fn length(&self) -> u64 {
match self {
Operation::Delete(n) => *n,
Operation::Retain(r) => r.num,
Operation::Retain(r) => r.n,
Operation::Insert(i) => i.num_chars(),
}
}
pub fn is_empty(&self) -> bool { self.length() == 0 }
}
@ -89,7 +90,7 @@ impl fmt::Display for Operation {
Operation::Retain(r) => {
f.write_fmt(format_args!(
"retain: {}, attributes: {}",
r.num, r.attributes
r.n, r.attributes
))?;
},
Operation::Insert(i) => {
@ -141,15 +142,40 @@ impl OpBuilder {
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Retain {
#[serde(rename(serialize = "retain", deserialize = "retain"))]
pub num: u64,
pub n: u64,
#[serde(skip_serializing_if = "is_empty")]
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 {
fn from(n: u64) -> Self {
Retain {
num: n,
n,
attributes: Attributes::default(),
}
}
@ -158,11 +184,11 @@ impl std::convert::From<u64> for Retain {
impl Deref for Retain {
type Target = u64;
fn deref(&self) -> &Self::Target { &self.num }
fn deref(&self) -> &Self::Target { &self.n }
}
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)]
@ -180,6 +206,23 @@ impl Insert {
pub fn chars(&self) -> Chars<'_> { self.s.chars() }
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 {

View File

@ -1,6 +1,6 @@
pub mod helper;
use crate::helper::{MergeTestOp::*, *};
use crate::helper::{TestOp::*, *};
use flowy_ot::core::Interval;
#[test]
@ -10,7 +10,7 @@ fn delta_insert_text() {
Insert(0, "456", 3),
AssertOpsJson(0, r#"[{"insert":"123456"}]"#),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -20,7 +20,7 @@ fn delta_insert_text_at_head() {
Insert(0, "456", 0),
AssertOpsJson(0, r#"[{"insert":"456123"}]"#),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -30,7 +30,7 @@ fn delta_insert_text_at_middle() {
Insert(0, "456", 1),
AssertOpsJson(0, r#"[{"insert":"145623"}]"#),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -49,7 +49,7 @@ fn delta_insert_text_with_attr() {
r#"[{"insert":"1abc2","attributes":{"bold":"true"}},{"insert":"345"}]"#,
),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -61,7 +61,7 @@ fn delta_add_bold_and_invert_all() {
Bold(0, Interval::new(0, 3), false),
AssertOpsJson(0, r#"[{"insert":"123"}]"#),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -76,7 +76,7 @@ fn delta_add_bold_and_invert_partial_suffix() {
r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#,
),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -93,7 +93,7 @@ fn delta_add_bold_and_invert_partial_suffix2() {
Bold(0, Interval::new(2, 4), true),
AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -108,7 +108,7 @@ fn delta_add_bold_and_invert_partial_prefix() {
r#"[{"insert":"12"},{"insert":"34","attributes":{"bold":"true"}}]"#,
),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -126,14 +126,14 @@ fn delta_add_bold_consecutive() {
r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#,
),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
#[should_panic]
fn delta_add_bold_empty_str() {
let ops = vec![Bold(0, Interval::new(0, 4), true)];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[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"}}]"#,
),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -178,7 +178,7 @@ fn delta_add_bold_italic2() {
),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -203,7 +203,7 @@ fn delta_add_bold_italic3() {
),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -225,7 +225,7 @@ fn delta_add_bold_italic_delete() {
),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -236,7 +236,7 @@ fn delta_merge_inserted_text_with_same_attribute() {
InsertBold(0, "456", Interval::new(3, 6)),
AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -251,7 +251,7 @@ fn delta_compose_attr_delta_with_attr_delta_test() {
AssertOpsJson(1, r#"[{"insert":"1234567","attributes":{"bold":"true"}}]"#),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[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]
@ -293,7 +293,7 @@ fn delta_compose_attr_delta_with_no_attr_delta_test() {
AssertOpsJson(0, expected),
AssertOpsJson(1, expected),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -305,7 +305,7 @@ fn delta_delete_heading() {
AssertOpsJson(0, r#"[{"insert":"3456","attributes":{"bold":"true"}}]"#),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -317,7 +317,7 @@ fn delta_delete_trailing() {
AssertOpsJson(0, r#"[{"insert":"12345","attributes":{"bold":"true"}}]"#),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -331,7 +331,7 @@ fn delta_delete_middle() {
AssertOpsJson(0, r#"[{"insert":"34","attributes":{"bold":"true"}}]"#),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]
@ -343,5 +343,5 @@ fn delta_delete_all() {
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;
#[derive(Clone, Debug)]
pub enum MergeTestOp {
pub enum TestOp {
Insert(usize, &'static str, usize),
// delta_i, s, start, length,
InsertBold(usize, &'static str, Interval),
@ -16,11 +16,11 @@ pub enum MergeTestOp {
AssertOpsJson(usize, &'static str),
}
pub struct MergeTest {
pub struct OpTester {
deltas: Vec<Delta>,
}
impl MergeTest {
impl OpTester {
pub fn new() -> Self {
static INIT: Once = Once::new();
INIT.call_once(|| {
@ -36,29 +36,29 @@ impl MergeTest {
Self { deltas }
}
pub fn run_op(&mut self, op: &MergeTestOp) {
pub fn run_op(&mut self, op: &TestOp) {
match op {
MergeTestOp::Insert(delta_i, s, index) => {
TestOp::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);
},
MergeTestOp::InsertBold(delta_i, s, _interval) => {
TestOp::InsertBold(delta_i, s, _interval) => {
let attrs = AttrsBuilder::new().bold(true).build();
let delta = &mut self.deltas[*delta_i];
delta.insert(s, attrs);
},
MergeTestOp::Bold(delta_i, interval, enable) => {
TestOp::Bold(delta_i, interval, enable) => {
let attrs = AttrsBuilder::new().bold(*enable).build();
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();
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_b = &self.deltas[*delta_b_i];
@ -70,12 +70,12 @@ impl MergeTest {
self.deltas[*delta_a_i] = new_delta_a;
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();
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 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() {
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) {
let old_delta = &mut self.deltas[delta_index];
let target_interval = Interval::new(0, old_delta.target_len);
@ -103,7 +105,7 @@ impl MergeTest {
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 {
attributes = Attributes::Follow;
}
@ -123,8 +125,8 @@ impl MergeTest {
.attributes(attributes)
.build();
let attrs = attributes_in_delta(old_delta, &interval);
retain.extend_attributes(attrs);
let attributes = old_delta.attributes_in_interval(*interval);
retain.extend_attributes(attributes);
let new_delta = new_delta_with_op(old_delta, retain, *interval);
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) {
let old_delta = &self.deltas[delta_index];
let mut delete = OpBuilder::delete(interval.size() as u64).build();
let attrs = attributes_in_delta(old_delta, &interval);
delete.extend_attributes(attrs);
let attributes = old_delta.attributes_in_interval(*interval);
delete.extend_attributes(attributes);
let new_delta = new_delta_with_op(old_delta, delete, *interval);
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 {
let intervals = split_interval_with_delta(delta, &prefix);
intervals.into_iter().for_each(|interval| {
let attrs = attributes_in_delta(delta, &interval);
new_delta.retain(interval.size() as u64, attrs);
let attributes = delta.attributes_in_interval(interval);
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 {
let intervals = split_interval_with_delta(delta, &suffix);
intervals.into_iter().for_each(|interval| {
let attrs = attributes_in_delta(delta, &interval);
new_delta.retain(interval.size() as u64, attrs);
let attributes = delta.attributes_in_interval(interval);
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) {
log::debug!("😁 {}", 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)
}
eprintln!("😁 {}", serde_json::to_string(delta).unwrap());
}
pub struct Rng(StdRng);
@ -259,7 +221,7 @@ impl Rng {
pub fn gen_delta(&mut self, s: &str) -> Delta {
let mut delta = Delta::default();
loop {
let left = s.chars().count() - delta.base_len();
let left = s.chars().count() - delta.base_len;
if left == 0 {
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;
use crate::helper::MergeTestOp::*;
use crate::helper::TestOp::*;
use bytecount::num_chars;
use flowy_ot::core::*;
use helper::*;
@ -196,7 +196,7 @@ fn transform2() {
AssertOpsJson(0, r#"[{"insert":"123456"}]"#),
AssertOpsJson(1, r#"[{"insert":"123456"}]"#),
];
MergeTest::new().run_script(ops);
OpTester::new().run_script(ops);
}
#[test]