fix ot bugs

This commit is contained in:
appflowy 2021-08-02 23:08:24 +08:00
parent 0b82336b6c
commit d78c33e194
7 changed files with 159 additions and 73 deletions

View File

@ -15,7 +15,7 @@ impl Attributes {
}
}
pub fn remove_empty_value(&mut self) { self.inner.retain(|_, v| v.is_empty() == false); }
pub fn remove_empty(&mut self) { self.inner.retain(|_, v| v.is_empty() == false); }
pub fn extend(&mut self, other: Attributes) { self.inner.extend(other.inner); }
@ -49,18 +49,21 @@ impl AttrsBuilder {
}
}
pub fn bold(mut self) -> Self {
self.inner.insert("bold".to_owned(), "true".to_owned());
pub fn bold(mut self, bold: bool) -> Self {
let val = match bold {
true => "true",
false => "",
};
self.inner.insert("bold".to_owned(), val.to_owned());
self
}
pub fn un_bold(mut self) -> Self {
self.inner.insert("bold".to_owned(), "".to_owned());
self
}
pub fn italic(mut self) -> Self {
self.inner.insert("italic".to_owned(), "true".to_owned());
pub fn italic(mut self, italic: bool) -> Self {
let val = match italic {
true => "true",
false => "",
};
self.inner.insert("italic".to_owned(), val.to_owned());
self
}
@ -86,25 +89,26 @@ pub fn compose_attributes(
) -> Option<Attributes> {
let a = attributes_from(op1);
let b = attributes_from(op2);
if a.is_none() && b.is_none() {
return None;
}
let mut attrs_a = a.unwrap_or(Attributes::default());
let attrs_b = b.unwrap_or(Attributes::default());
// log::debug!(
// "before compose_attributes: a: {:?}, b: {:?}",
// attrs_a,
// attrs_b
// );
log::trace!(
"before compose_attributes: a: {:?}, b: {:?}",
attrs_a,
attrs_b
);
attrs_a.extend(attrs_b);
log::trace!("after compose_attributes: a: {:?}", attrs_a);
if !keep_empty {
attrs_a.remove_empty_value()
attrs_a.remove_empty()
}
// log::debug!("after compose_attributes: a: {:?}", attrs_a);
return if attrs_a.is_empty() {
None
} else {
Some(attrs_a)
};
}
pub fn transform_attributes(

View File

@ -156,7 +156,13 @@ impl Delta {
return Err(OTError);
},
(Some(Operation::Retain(retain)), Some(Operation::Retain(o_retain))) => {
let composed_attrs = compose_attributes(&next_op1, &next_op2, true);
let composed_attrs = compose_attributes(&next_op1, &next_op2, false);
log::debug!(
"[retain:{} - retain:{}]: {:?}",
retain.num,
o_retain.num,
composed_attrs
);
match retain.cmp(&o_retain) {
Ordering::Less => {
new_delta.retain(retain.num, composed_attrs);
@ -201,6 +207,12 @@ impl Delta {
},
(Some(Operation::Insert(insert)), Some(Operation::Retain(o_retain))) => {
let composed_attrs = compose_attributes(&next_op1, &next_op2, false);
log::debug!(
"[insert:{} - retain:{}]: {:?}",
insert.s,
o_retain.num,
composed_attrs
);
match (insert.num_chars()).cmp(o_retain) {
Ordering::Less => {
new_delta.insert(&insert.s, composed_attrs.clone());
@ -465,6 +477,11 @@ fn merge_insert_or_new_op(
s: &str,
attributes: Option<Attributes>,
) -> Option<Operation> {
if attributes.is_none() {
insert.s += s;
return None;
}
if insert.attributes == attributes {
insert.s += s;
None
@ -478,11 +495,17 @@ fn merge_retain_or_new_op(
n: u64,
attributes: Option<Attributes>,
) -> Option<Operation> {
log::trace!(
"merge_retain_or_new_op: {:?}, {:?}",
retain.attributes,
attributes
);
if attributes.is_none() {
retain.num += n;
return None;
}
// log::debug!("merge retain: {:?}, {:?}", retain.attributes, attributes);
if retain.attributes == attributes {
retain.num += n;
None

View File

@ -76,7 +76,14 @@ impl OpBuilder {
pub fn insert(s: &str) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) }
pub fn attributes(mut self, attrs: Option<Attributes>) -> OpBuilder {
self.attrs = attrs;
match attrs {
None => self.attrs = attrs,
Some(attrs) => match attrs.is_empty() {
true => self.attrs = None,
false => self.attrs = Some(attrs),
},
}
self
}
@ -95,7 +102,7 @@ impl OpBuilder {
pub struct Retain {
#[serde(rename(serialize = "retain", deserialize = "retain"))]
pub num: u64,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(skip_serializing_if = "is_empty")]
pub attributes: Option<Attributes>,
}
@ -123,7 +130,7 @@ pub struct Insert {
#[serde(rename(serialize = "insert", deserialize = "insert"))]
pub s: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(skip_serializing_if = "is_empty")]
pub attributes: Option<Attributes>,
}
@ -152,3 +159,10 @@ impl std::convert::From<&str> for Insert {
}
}
}
fn is_empty(attributes: &Option<Attributes>) -> bool {
match attributes {
None => true,
Some(attributes) => attributes.is_empty(),
}
}

View File

@ -53,6 +53,30 @@ fn delta_add_bold_attr3() {
MergeTest::new().run_script(ops);
}
#[test]
fn delta_add_bold_italic() {
let ops = vec![
Insert(0, "1234"),
Bold(0, Interval::new(0, 4), true),
Italic(0, Interval::new(0, 4), true),
AssertOpsJson(
0,
r#"[{"insert":"1234","attributes":{"italic":"true","bold":"true"}}]"#,
),
Insert(0, "5678"),
AssertOpsJson(
0,
r#"[{"insert":"12345678","attributes":{"italic":"true","bold":"true"}}]"#,
),
Italic(0, Interval::new(4, 6), false),
AssertOpsJson(
0,
r#"[{"insert":"1234","attributes":{"italic":"true","bold":"true"}},{"insert":"56"},{"insert":"78","attributes":{"bold":"true","italic":"true"}}]"#,
),
];
MergeTest::new().run_script(ops);
}
#[test]
fn delta_add_bold_attr_and_invert() {
let ops = vec![

View File

@ -58,6 +58,7 @@ pub enum MergeTestOp {
InsertBold(usize, &'static str, Interval),
// delta_i, start, length, enable
Bold(usize, Interval, bool),
Italic(usize, Interval, bool),
Transform(usize, usize),
AssertStr(usize, &'static str),
AssertOpsJson(usize, &'static str),
@ -71,7 +72,7 @@ impl MergeTest {
pub fn new() -> Self {
static INIT: Once = Once::new();
INIT.call_once(|| {
std::env::set_var("RUST_LOG", "debug");
std::env::set_var("RUST_LOG", "info");
env_logger::init();
});
@ -90,40 +91,17 @@ impl MergeTest {
delta.insert(s, None);
},
MergeTestOp::InsertBold(delta_i, s, interval) => {
let attrs = AttrsBuilder::new().bold().build();
let attrs = AttrsBuilder::new().bold(true).build();
let delta = &mut self.deltas[*delta_i];
delta.insert(s, Some(attrs));
},
MergeTestOp::Bold(delta_i, interval, enable) => {
let attrs = if *enable {
AttrsBuilder::new().bold().build()
} else {
AttrsBuilder::new().un_bold().build()
};
let delta = &mut self.deltas[*delta_i];
let delta_interval = Interval::new(0, delta.target_len);
let mut new_delta = Delta::default();
let prefix = delta_interval.prefix(*interval);
if prefix.is_empty() == false && prefix != *interval {
let size = prefix.size();
// get attr in prefix interval
let attrs = attributes_in_interval(delta, &prefix);
new_delta.retain(size as u64, Some(attrs));
}
let size = interval.size();
new_delta.retain(size as u64, Some(attrs));
let suffix = delta_interval.suffix(*interval);
if suffix.is_empty() == false {
let size = suffix.size();
let attrs = attributes_in_interval(delta, &suffix);
new_delta.retain(size as u64, Some(attrs));
}
let a = delta.compose(&new_delta).unwrap();
self.deltas[*delta_i] = a;
let attrs = AttrsBuilder::new().bold(*enable).build();
self.replace_delta(*delta_i, attrs, interval);
},
MergeTestOp::Italic(delta_i, interval, enable) => {
let attrs = AttrsBuilder::new().italic(*enable).build();
self.replace_delta(*delta_i, attrs, interval);
},
MergeTestOp::Transform(delta_a_i, delta_b_i) => {
let delta_a = &self.deltas[*delta_a_i];
@ -142,12 +120,16 @@ impl MergeTest {
},
MergeTestOp::AssertOpsJson(delta_i, expected) => {
let s = serde_json::to_string(&self.deltas[*delta_i]).unwrap();
if &s != expected {
log::error!("{}", s);
}
let expected_delta: Delta = serde_json::from_str(expected).unwrap();
assert_eq!(&s, expected);
let delta_i_json = serde_json::to_string(&self.deltas[*delta_i]).unwrap();
let delta: Delta = serde_json::from_str(&delta_i_json).unwrap();
if expected_delta != delta {
log::error!("✅ {}", expected);
log::error!("❌ {}", delta_i_json);
}
assert_eq!(delta, expected_delta);
},
}
}
@ -157,13 +139,49 @@ impl MergeTest {
self.run_op(op);
}
}
pub fn replace_delta(
&mut self,
delta_index: usize,
attributes: Attributes,
interval: &Interval,
) {
let old_delta = &self.deltas[delta_index];
let new_delta = delta_with_attribute(old_delta, attributes, interval);
self.deltas[delta_index] = new_delta;
}
}
pub fn delta_with_attribute(delta: &Delta, attributes: Attributes, interval: &Interval) -> Delta {
let delta_interval = Interval::new(0, delta.target_len);
let mut new_delta = Delta::default();
let prefix = delta_interval.prefix(*interval);
if prefix.is_empty() == false && prefix != *interval {
let size = prefix.size();
let attrs = attributes_in_interval(delta, &prefix);
new_delta.retain(size as u64, attrs);
}
let size = interval.size();
log::debug!("Apply attribute {:?} to {}", attributes, interval);
new_delta.retain(size as u64, Some(attributes));
let suffix = delta_interval.suffix(*interval);
if suffix.is_empty() == false {
let size = suffix.size();
let attrs = attributes_in_interval(delta, &suffix);
new_delta.retain(size as u64, attrs);
}
delta.compose(&new_delta).unwrap()
}
pub fn debug_print_delta(delta: &Delta) {
log::debug!("😁 {}", serde_json::to_string(delta).unwrap());
}
pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Attributes {
pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Option<Attributes> {
let mut attributes = Attributes::new();
let mut offset = 0;
@ -178,12 +196,12 @@ pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Attributes
},
Operation::Insert(insert) => {
if insert.attributes.is_some() {
if interval.start >= offset || insert.num_chars() > interval.end as u64 {
if interval.start >= offset && insert.num_chars() > (interval.end as u64 - 1) {
attributes.extend(insert.attributes.as_ref().unwrap().clone());
}
offset += insert.num_chars() as usize;
}
},
});
attributes
Some(attributes)
}

View File

@ -182,10 +182,11 @@ fn transform() {
let (a_prime, b_prime) = a.transform(&b).unwrap();
let ab_prime = a.compose(&b_prime).unwrap();
let ba_prime = b.compose(&a_prime).unwrap();
let after_ab_prime = ab_prime.apply(&s).unwrap();
let after_ba_prime = ba_prime.apply(&s).unwrap();
assert_eq!(ab_prime, ba_prime);
assert_eq!(after_ab_prime, after_ba_prime);
// let after_ab_prime = ab_prime.apply(&s).unwrap();
// let after_ba_prime = ba_prime.apply(&s).unwrap();
// assert_eq!(after_ab_prime, after_ba_prime);
}
}
@ -207,13 +208,15 @@ fn transform2() {
fn delta_transform_test() {
let mut a = Delta::default();
let mut a_s = String::new();
a.insert("123", Some(AttrsBuilder::new().bold().build()));
a.insert("123", Some(AttrsBuilder::new().bold(true).build()));
a_s = a.apply(&a_s).unwrap();
assert_eq!(&a_s, "123");
let mut b = Delta::default();
let mut b_s = String::new();
b.insert("456", None);
b_s = a.apply(&b_s).unwrap();
b_s = b.apply(&b_s).unwrap();
assert_eq!(&b_s, "456");
let (a_prime, b_prime) = a.transform(&b).unwrap();
assert_eq!(

View File

@ -6,7 +6,7 @@ use flowy_ot::{
#[test]
fn operation_insert_serialize_test() {
let attributes = AttrsBuilder::new().bold().italic().build();
let attributes = AttrsBuilder::new().bold(true).italic(true).build();
let operation = OpBuilder::insert("123")
.attributes(Some(attributes))
.build();
@ -38,7 +38,7 @@ fn operation_delete_serialize_test() {
fn delta_serialize_test() {
let mut delta = Delta::default();
let attributes = AttrsBuilder::new().bold().italic().build();
let attributes = AttrsBuilder::new().bold(true).italic(true).build();
let retain = OpBuilder::insert("123")
.attributes(Some(attributes))
.build();