fix invert bugs

This commit is contained in:
appflowy 2021-08-05 11:30:29 +08:00
parent c960d063fb
commit 98f8442194
5 changed files with 110 additions and 104 deletions

View File

@ -1,8 +1,8 @@
use crate::core::Operation;
use std::{collections::HashMap, fmt};
const PLAIN: &'static str = "";
fn is_plain(s: &str) -> bool { s == PLAIN }
const REMOVE_FLAG: &'static str = "";
fn should_remove(s: &str) -> bool { s == REMOVE_FLAG }
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
@ -15,45 +15,21 @@ pub enum Attributes {
}
impl Attributes {
pub fn extend(&self, other: Option<Attributes>) -> Attributes {
log::debug!("Attribute extend: {:?} with {:?}", self, other);
let other = other.unwrap_or(Attributes::Empty);
let result = match (self, &other) {
(Attributes::Custom(data), Attributes::Custom(o_data)) => {
if !data.is_plain() {
let mut data = data.clone();
data.extend(o_data.clone());
Attributes::Custom(data)
} else {
Attributes::Custom(data.clone())
}
},
(Attributes::Custom(data), _) => Attributes::Custom(data.clone()),
// (Attributes::Empty, _) => Attributes::Empty,
_ => other,
};
log::debug!("result {:?}", result);
result
}
// remove attribute if the value is PLAIN
// { "k": PLAIN } -> {}
pub fn remove_plain(&mut self) {
match self {
Attributes::Follow => {},
Attributes::Custom(data) => {
data.remove_plain();
},
Attributes::Empty => {},
}
}
pub fn get_attributes_data(&self) -> Option<AttributesData> {
pub fn data(&self) -> Option<AttributesData> {
match self {
Attributes::Follow => None,
Attributes::Custom(data) => Some(data.clone()),
Attributes::Empty => None,
}
}
pub fn is_empty(&self) -> bool {
match self {
Attributes::Follow => true,
Attributes::Custom(data) => data.is_empty(),
Attributes::Empty => true,
}
}
}
impl std::default::Default for Attributes {
@ -90,12 +66,62 @@ impl AttributesData {
inner: HashMap::new(),
}
}
pub fn is_empty(&self) -> bool {
self.inner.values().filter(|v| !should_remove(v)).count() == 0
}
pub fn remove_plain(&mut self) { self.inner.retain(|_, v| !is_plain(v)); }
fn remove_empty(&mut self) { self.inner.retain(|_, v| !should_remove(v)); }
pub fn extend(&mut self, other: AttributesData) { self.inner.extend(other.inner); }
pub fn is_plain(&self) -> bool { self.inner.values().filter(|v| !is_plain(v)).count() == 0 }
pub fn merge(&mut self, other: Option<AttributesData>) {
if other.is_none() {
return;
}
let mut new_attributes = other.unwrap().inner;
self.inner.iter().for_each(|(k, v)| {
if should_remove(v) {
new_attributes.remove(k);
} else {
new_attributes.insert(k.clone(), v.clone());
}
});
self.inner = new_attributes;
}
}
pub trait AttributesDataRule {
fn apply_rule(&mut self);
fn into_attributes(self) -> Attributes;
}
impl AttributesDataRule for AttributesData {
fn apply_rule(&mut self) { self.remove_empty(); }
fn into_attributes(mut self) -> Attributes {
self.apply_rule();
if self.is_empty() {
Attributes::Empty
} else {
Attributes::Custom(self)
}
}
}
pub trait AttributesRule {
fn apply_rule(self) -> Attributes;
}
impl AttributesRule for Attributes {
fn apply_rule(self) -> Attributes {
match self {
Attributes::Follow => self,
Attributes::Custom(data) => data.into_attributes(),
Attributes::Empty => self,
}
}
}
impl std::convert::From<HashMap<String, String>> for AttributesData {
@ -126,7 +152,7 @@ impl AttrsBuilder {
pub fn bold(mut self, bold: bool) -> Self {
let val = match bold {
true => "true",
false => PLAIN,
false => REMOVE_FLAG,
};
self.inner.insert("bold".to_owned(), val.to_owned());
self
@ -135,7 +161,7 @@ impl AttrsBuilder {
pub fn italic(mut self, italic: bool) -> Self {
let val = match italic {
true => "true",
false => PLAIN,
false => REMOVE_FLAG,
};
self.inner.insert("italic".to_owned(), val.to_owned());
self
@ -167,54 +193,52 @@ pub fn compose_attributes(left: &Option<Operation>, right: &Option<Operation>) -
let mut attr = match (&attr_l, &attr_r) {
(_, Some(Attributes::Custom(_))) => match attr_l {
None => attr_r.unwrap(),
Some(_) => attr_l.unwrap().extend(attr_r.clone()),
// Some(attr_l) => merge_attributes(attr_l, attr_r),
Some(attr_l) => merge_attributes(attr_l, attr_r),
},
(Some(Attributes::Custom(_)), Some(Attributes::Follow))
| (Some(Attributes::Custom(_)), Some(Attributes::Custom(_))) => {
merge_attributes(attr_l.unwrap(), attr_r)
},
(Some(Attributes::Custom(_)), _) => attr_l.unwrap().extend(attr_r),
// (Some(Attributes::Custom(_)), _) => merge_attributes(attr_l.unwrap(), attr_r),
(Some(Attributes::Follow), Some(Attributes::Follow)) => Attributes::Follow,
_ => Attributes::Empty,
};
log::trace!("composed_attributes: a: {:?}", attr);
match &mut attr {
Attributes::Custom(data) => {
data.remove_plain();
match data.is_plain() {
true => Attributes::Empty,
false => attr,
}
},
_ => attr,
}
attr.apply_rule()
}
pub fn transform_attributes(
pub fn transform_op_attributes(
left: &Option<Operation>,
right: &Option<Operation>,
priority: bool,
) -> Attributes {
let attr_l = attributes_from(left);
let attr_r = attributes_from(right);
transform_attributes(attr_l, attr_r, priority)
}
if attr_l.is_none() {
if attr_r.is_none() {
pub fn transform_attributes(
left: Option<Attributes>,
right: Option<Attributes>,
priority: bool,
) -> Attributes {
if left.is_none() {
if right.is_none() {
return Attributes::Empty;
}
return match attr_r.as_ref().unwrap() {
return match right.as_ref().unwrap() {
Attributes::Follow => Attributes::Follow,
Attributes::Custom(_) => attr_r.unwrap(),
Attributes::Custom(_) => right.unwrap(),
Attributes::Empty => Attributes::Empty,
};
}
if !priority {
return attr_r.unwrap();
return right.unwrap();
}
match (attr_l.unwrap(), attr_r.unwrap()) {
match (left.unwrap(), right.unwrap()) {
(Attributes::Custom(attr_data_l), Attributes::Custom(attr_data_r)) => {
let result = transform_attribute_data(attr_data_l, attr_data_r);
Attributes::Custom(result)
@ -224,8 +248,8 @@ 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();
let attr = attr.data();
let base = base.data();
if attr.is_none() && base.is_none() {
return Attributes::Empty;

View File

@ -249,6 +249,7 @@ impl Delta {
);
next_op1 = Some(
OpBuilder::insert(&chars.collect::<String>())
//maybe o_retain attributes
.attributes(Attributes::Empty)
.build(),
);
@ -312,7 +313,7 @@ impl Delta {
next_op1 = ops1.next();
},
(_, Some(Operation::Insert(o_insert))) => {
let composed_attrs = transform_attributes(&next_op1, &next_op2, true);
let composed_attrs = transform_op_attributes(&next_op1, &next_op2, true);
a_prime.retain(o_insert.num_chars(), composed_attrs.clone());
b_prime.insert(&o_insert.s, composed_attrs);
next_op2 = ops2.next();
@ -324,7 +325,7 @@ impl Delta {
return Err(OTError);
},
(Some(Operation::Retain(retain)), Some(Operation::Retain(o_retain))) => {
let composed_attrs = transform_attributes(&next_op1, &next_op2, true);
let composed_attrs = transform_op_attributes(&next_op1, &next_op2, true);
match retain.cmp(&o_retain) {
Ordering::Less => {
a_prime.retain(retain.n, composed_attrs.clone());
@ -503,7 +504,7 @@ impl Delta {
index += len;
},
Operation::Retain(_) => {
match op.is_plain() {
match op.has_attribute() {
true => inverted.retain(len as u64, op.get_attributes()),
false => inverted_from_other(&mut inverted, op, index, len as usize),
}
@ -574,7 +575,7 @@ impl Delta {
match &insert.attributes {
Attributes::Follow => {},
Attributes::Custom(data) => {
log::debug!("get attributes from op: {:?} at {:?}", op, interval);
log::debug!("extend attributes from op: {:?} at {:?}", op, interval);
if interval.contains_range(offset, offset + end) {
attributes_data.extend(data.clone());
}
@ -585,10 +586,6 @@ impl Delta {
},
});
if attributes_data.is_plain() {
Attributes::Empty
} else {
Attributes::Custom(attributes_data)
}
attributes_data.into_attributes()
}
}

View File

@ -1,4 +1,4 @@
use crate::core::Attributes;
use crate::core::{transform_attributes, Attributes};
use bytecount::num_chars;
use std::{
fmt,
@ -36,20 +36,6 @@ impl Operation {
}
}
pub fn extend_attributes(&mut self, attributes: Attributes) {
match self {
Operation::Delete(_) => {},
Operation::Retain(retain) => {
let a = retain.attributes.extend(Some(attributes));
retain.attributes = a;
},
Operation::Insert(insert) => {
let a = insert.attributes.extend(Some(attributes));
insert.attributes = a;
},
}
}
pub fn set_attributes(&mut self, attributes: Attributes) {
match self {
Operation::Delete(_) => {
@ -64,7 +50,7 @@ impl Operation {
}
}
pub fn is_plain(&self) -> bool {
pub fn has_attribute(&self) -> bool {
match self.get_attributes() {
Attributes::Follow => true,
Attributes::Custom(_) => false,
@ -240,13 +226,4 @@ impl std::convert::From<&str> for Insert {
fn from(s: &str) -> Self { Insert::from(s.to_owned()) }
}
fn is_empty(attributes: &Attributes) -> bool {
match attributes {
Attributes::Follow => true,
Attributes::Custom(data) => {
let is_empty = data.is_plain();
is_empty
},
Attributes::Empty => true,
}
}
fn is_empty(attributes: &Attributes) -> bool { attributes.is_empty() }

View File

@ -154,7 +154,7 @@ fn delta_add_bold_italic() {
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"}}]"#,
r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true"}},{"insert":"78","attributes":{"bold":"true","italic":"true"}}]"#,
),
];
OpTester::new().run_script(ops);

View File

@ -133,16 +133,24 @@ impl OpTester {
pub fn update_delta_with_attribute(
&mut self,
delta_index: usize,
attributes: Attributes,
mut attributes: Attributes,
interval: &Interval,
) {
let old_delta = &self.deltas[delta_index];
let mut retain = OpBuilder::retain(interval.size() as u64)
.attributes(attributes)
let old_attributes = old_delta.attributes_in_interval(*interval);
let new_attributes = match &mut attributes {
Attributes::Follow => old_attributes,
Attributes::Custom(attr_data) => {
attr_data.merge(old_attributes.data());
attr_data.clone().into_attributes()
},
Attributes::Empty => Attributes::Empty,
};
let retain = OpBuilder::retain(interval.size() as u64)
.attributes(new_attributes)
.build();
let attributes_in_interval = old_delta.attributes_in_interval(*interval);
retain.extend_attributes(attributes_in_interval);
log::debug!(
"Update delta with attributes: {:?} at: {:?}",
retain,