mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
config ot invert detal
This commit is contained in:
parent
958918e0a0
commit
80a94880fc
@ -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;
|
|
||||||
// }
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 }
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
68
rust-lib/flowy-ot/tests/invert_test.rs
Normal file
68
rust-lib/flowy-ot/tests/invert_test.rs
Normal 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()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
@ -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]
|
||||||
|
Loading…
Reference in New Issue
Block a user