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

View File

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

View File

@ -76,7 +76,14 @@ impl OpBuilder {
pub fn insert(s: &str) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) } pub fn insert(s: &str) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) }
pub fn attributes(mut self, attrs: Option<Attributes>) -> OpBuilder { 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 self
} }
@ -95,7 +102,7 @@ impl OpBuilder {
pub struct Retain { pub struct Retain {
#[serde(rename(serialize = "retain", deserialize = "retain"))] #[serde(rename(serialize = "retain", deserialize = "retain"))]
pub num: u64, pub num: u64,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "is_empty")]
pub attributes: Option<Attributes>, pub attributes: Option<Attributes>,
} }
@ -123,7 +130,7 @@ pub struct Insert {
#[serde(rename(serialize = "insert", deserialize = "insert"))] #[serde(rename(serialize = "insert", deserialize = "insert"))]
pub s: String, pub s: String,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "is_empty")]
pub attributes: Option<Attributes>, 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); 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] #[test]
fn delta_add_bold_attr_and_invert() { fn delta_add_bold_attr_and_invert() {
let ops = vec![ let ops = vec![

View File

@ -58,6 +58,7 @@ pub enum MergeTestOp {
InsertBold(usize, &'static str, Interval), InsertBold(usize, &'static str, Interval),
// delta_i, start, length, enable // delta_i, start, length, enable
Bold(usize, Interval, bool), Bold(usize, Interval, bool),
Italic(usize, Interval, bool),
Transform(usize, usize), Transform(usize, usize),
AssertStr(usize, &'static str), AssertStr(usize, &'static str),
AssertOpsJson(usize, &'static str), AssertOpsJson(usize, &'static str),
@ -71,7 +72,7 @@ impl MergeTest {
pub fn new() -> Self { pub fn new() -> Self {
static INIT: Once = Once::new(); static INIT: Once = Once::new();
INIT.call_once(|| { INIT.call_once(|| {
std::env::set_var("RUST_LOG", "debug"); std::env::set_var("RUST_LOG", "info");
env_logger::init(); env_logger::init();
}); });
@ -90,40 +91,17 @@ impl MergeTest {
delta.insert(s, None); delta.insert(s, None);
}, },
MergeTestOp::InsertBold(delta_i, s, interval) => { 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]; let delta = &mut self.deltas[*delta_i];
delta.insert(s, Some(attrs)); delta.insert(s, Some(attrs));
}, },
MergeTestOp::Bold(delta_i, interval, enable) => { MergeTestOp::Bold(delta_i, interval, enable) => {
let attrs = if *enable { let attrs = AttrsBuilder::new().bold(*enable).build();
AttrsBuilder::new().bold().build() self.replace_delta(*delta_i, attrs, interval);
} else { },
AttrsBuilder::new().un_bold().build() MergeTestOp::Italic(delta_i, interval, enable) => {
}; let attrs = AttrsBuilder::new().italic(*enable).build();
let delta = &mut self.deltas[*delta_i]; self.replace_delta(*delta_i, attrs, interval);
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;
}, },
MergeTestOp::Transform(delta_a_i, delta_b_i) => { MergeTestOp::Transform(delta_a_i, delta_b_i) => {
let delta_a = &self.deltas[*delta_a_i]; let delta_a = &self.deltas[*delta_a_i];
@ -142,12 +120,16 @@ impl MergeTest {
}, },
MergeTestOp::AssertOpsJson(delta_i, expected) => { MergeTestOp::AssertOpsJson(delta_i, expected) => {
let s = serde_json::to_string(&self.deltas[*delta_i]).unwrap(); let expected_delta: Delta = serde_json::from_str(expected).unwrap();
if &s != expected {
log::error!("{}", s);
}
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); 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) { pub fn debug_print_delta(delta: &Delta) {
log::debug!("😁 {}", serde_json::to_string(delta).unwrap()); 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 attributes = Attributes::new();
let mut offset = 0; let mut offset = 0;
@ -178,12 +196,12 @@ pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Attributes
}, },
Operation::Insert(insert) => { Operation::Insert(insert) => {
if insert.attributes.is_some() { 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()); attributes.extend(insert.attributes.as_ref().unwrap().clone());
} }
offset += insert.num_chars() as usize; 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 (a_prime, b_prime) = a.transform(&b).unwrap();
let ab_prime = a.compose(&b_prime).unwrap(); let ab_prime = a.compose(&b_prime).unwrap();
let ba_prime = b.compose(&a_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!(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() { fn delta_transform_test() {
let mut a = Delta::default(); let mut a = Delta::default();
let mut a_s = String::new(); 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(); a_s = a.apply(&a_s).unwrap();
assert_eq!(&a_s, "123");
let mut b = Delta::default(); let mut b = Delta::default();
let mut b_s = String::new(); let mut b_s = String::new();
b.insert("456", None); 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(); let (a_prime, b_prime) = a.transform(&b).unwrap();
assert_eq!( assert_eq!(

View File

@ -6,7 +6,7 @@ use flowy_ot::{
#[test] #[test]
fn operation_insert_serialize_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") let operation = OpBuilder::insert("123")
.attributes(Some(attributes)) .attributes(Some(attributes))
.build(); .build();
@ -38,7 +38,7 @@ fn operation_delete_serialize_test() {
fn delta_serialize_test() { fn delta_serialize_test() {
let mut delta = Delta::default(); 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") let retain = OpBuilder::insert("123")
.attributes(Some(attributes)) .attributes(Some(attributes))
.build(); .build();