mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
attribute: follow, empty, custom
This commit is contained in:
parent
d78c33e194
commit
3770c648c3
@ -1,58 +1,97 @@
|
|||||||
use crate::operation::Operation;
|
use crate::operation::Operation;
|
||||||
use std::collections::{hash_map::RandomState, HashMap};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
const PLAIN: &'static str = "";
|
||||||
|
fn is_plain(s: &str) -> bool { s == PLAIN }
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Attributes {
|
||||||
|
#[serde(skip)]
|
||||||
|
Follow,
|
||||||
|
Custom(AttributesData),
|
||||||
|
#[serde(skip)]
|
||||||
|
Empty,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attributes {
|
||||||
|
pub fn merge(&self, other: Option<Attributes>) -> Attributes {
|
||||||
|
let other = other.unwrap_or(Attributes::Empty);
|
||||||
|
match (self, &other) {
|
||||||
|
(Attributes::Custom(data), Attributes::Custom(o_data)) => {
|
||||||
|
let mut data = data.clone();
|
||||||
|
data.extend(o_data.clone());
|
||||||
|
Attributes::Custom(data)
|
||||||
|
},
|
||||||
|
_ => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// remove attribute if the value is PLAIN
|
||||||
|
pub fn remove_plain(&mut self) {
|
||||||
|
match self {
|
||||||
|
Attributes::Follow => {},
|
||||||
|
Attributes::Custom(data) => {
|
||||||
|
data.remove_plain();
|
||||||
|
},
|
||||||
|
Attributes::Empty => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for Attributes {
|
||||||
|
fn default() -> Self { Attributes::Empty }
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Attributes {
|
pub struct AttributesData {
|
||||||
#[serde(skip_serializing_if = "HashMap::is_empty")]
|
#[serde(skip_serializing_if = "HashMap::is_empty")]
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
inner: HashMap<String, String>,
|
inner: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attributes {
|
impl AttributesData {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Attributes {
|
AttributesData {
|
||||||
inner: HashMap::new(),
|
inner: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_empty(&mut self) { self.inner.retain(|_, v| v.is_empty() == false); }
|
pub fn remove_plain(&mut self) { self.inner.retain(|_, v| !is_plain(v)); }
|
||||||
|
|
||||||
pub fn extend(&mut self, other: Attributes) { self.inner.extend(other.inner); }
|
pub fn extend(&mut self, other: AttributesData) { self.inner.extend(other.inner); }
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool { self.inner.is_empty() }
|
pub fn is_plain(&self) -> bool { self.inner.values().filter(|v| !is_plain(v)).count() == 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<HashMap<String, String>> for Attributes {
|
impl std::convert::From<HashMap<String, String>> for AttributesData {
|
||||||
fn from(attributes: HashMap<String, String, RandomState>) -> Self {
|
fn from(attributes: HashMap<String, String>) -> Self { AttributesData { inner: attributes } }
|
||||||
Attributes { inner: attributes }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for Attributes {
|
impl std::ops::Deref for AttributesData {
|
||||||
type Target = HashMap<String, String>;
|
type Target = HashMap<String, String>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { &self.inner }
|
fn deref(&self) -> &Self::Target { &self.inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DerefMut for Attributes {
|
impl std::ops::DerefMut for AttributesData {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
|
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AttrsBuilder {
|
pub struct AttrsBuilder {
|
||||||
inner: Attributes,
|
inner: AttributesData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttrsBuilder {
|
impl AttrsBuilder {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Attributes::default(),
|
inner: AttributesData::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bold(mut self, bold: bool) -> Self {
|
pub fn bold(mut self, bold: bool) -> Self {
|
||||||
let val = match bold {
|
let val = match bold {
|
||||||
true => "true",
|
true => "true",
|
||||||
false => "",
|
false => PLAIN,
|
||||||
};
|
};
|
||||||
self.inner.insert("bold".to_owned(), val.to_owned());
|
self.inner.insert("bold".to_owned(), val.to_owned());
|
||||||
self
|
self
|
||||||
@ -61,7 +100,7 @@ impl AttrsBuilder {
|
|||||||
pub fn italic(mut self, italic: bool) -> Self {
|
pub fn italic(mut self, italic: bool) -> Self {
|
||||||
let val = match italic {
|
let val = match italic {
|
||||||
true => "true",
|
true => "true",
|
||||||
false => "",
|
false => PLAIN,
|
||||||
};
|
};
|
||||||
self.inner.insert("italic".to_owned(), val.to_owned());
|
self.inner.insert("italic".to_owned(), val.to_owned());
|
||||||
self
|
self
|
||||||
@ -72,98 +111,108 @@ impl AttrsBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Attributes { self.inner }
|
pub fn build(self) -> Attributes { Attributes::Custom(self.inner) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attributes_from(operation: &Option<Operation>) -> Option<Attributes> {
|
pub fn attributes_from(operation: &Option<Operation>) -> Option<Attributes> {
|
||||||
match operation {
|
match operation {
|
||||||
None => None,
|
None => None,
|
||||||
Some(operation) => operation.get_attributes(),
|
Some(operation) => Some(operation.get_attributes()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compose_attributes(
|
pub fn compose_attributes(left: &Option<Operation>, right: &Option<Operation>) -> Attributes {
|
||||||
op1: &Option<Operation>,
|
if left.is_none() && right.is_none() {
|
||||||
op2: &Option<Operation>,
|
return Attributes::Empty;
|
||||||
keep_empty: bool,
|
|
||||||
) -> Option<Attributes> {
|
|
||||||
let a = attributes_from(op1);
|
|
||||||
let b = attributes_from(op2);
|
|
||||||
|
|
||||||
if a.is_none() && b.is_none() {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
let attr_l = attributes_from(left);
|
||||||
|
let attr_r = attributes_from(right);
|
||||||
|
log::trace!("compose_attributes: a: {:?}, b: {:?}", attr_l, attr_r);
|
||||||
|
|
||||||
let mut attrs_a = a.unwrap_or(Attributes::default());
|
let mut attr = match (&attr_l, &attr_r) {
|
||||||
let attrs_b = b.unwrap_or(Attributes::default());
|
(_, Some(Attributes::Custom(_))) => match &attr_l {
|
||||||
|
None => attr_r.unwrap(),
|
||||||
|
Some(_) => attr_l.unwrap().merge(attr_r.clone()),
|
||||||
|
},
|
||||||
|
(Some(Attributes::Custom(_)), _) => attr_l.unwrap().merge(attr_r),
|
||||||
|
_ => Attributes::Empty,
|
||||||
|
};
|
||||||
|
|
||||||
log::trace!(
|
log::trace!("composed_attributes: a: {:?}", attr);
|
||||||
"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()
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(attrs_a)
|
// remove the attribute if the value is PLAIN
|
||||||
|
attr.remove_plain();
|
||||||
|
|
||||||
|
attr
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transform_attributes(
|
pub fn transform_attributes(
|
||||||
op1: &Option<Operation>,
|
left: &Option<Operation>,
|
||||||
op2: &Option<Operation>,
|
right: &Option<Operation>,
|
||||||
priority: bool,
|
priority: bool,
|
||||||
) -> Option<Attributes> {
|
) -> Attributes {
|
||||||
let a = attributes_from(op1);
|
let attr_l = attributes_from(left);
|
||||||
let b = attributes_from(op2);
|
let attr_r = attributes_from(right);
|
||||||
|
|
||||||
if a.is_none() {
|
if attr_l.is_none() {
|
||||||
return b;
|
if attr_r.is_none() {
|
||||||
}
|
return Attributes::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
if b.is_none() {
|
return match attr_r.as_ref().unwrap() {
|
||||||
return None;
|
Attributes::Follow => Attributes::Empty,
|
||||||
|
Attributes::Custom(_) => attr_r.unwrap(),
|
||||||
|
Attributes::Empty => Attributes::Empty,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if !priority {
|
if !priority {
|
||||||
return b;
|
return attr_r.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let attrs_a = a.unwrap_or(Attributes::default());
|
match (attr_l.unwrap(), attr_r.unwrap()) {
|
||||||
let attrs_b = b.unwrap_or(Attributes::default());
|
(Attributes::Custom(attr_data_l), Attributes::Custom(attr_data_r)) => {
|
||||||
|
let result = transform_attribute_data(attr_data_l, attr_data_r);
|
||||||
let result = attrs_b
|
Attributes::Custom(result)
|
||||||
.iter()
|
},
|
||||||
.fold(Attributes::new(), |mut attributes, (k, v)| {
|
_ => Attributes::Empty,
|
||||||
if attrs_a.contains_key(k) == false {
|
}
|
||||||
attributes.insert(k.clone(), v.clone());
|
|
||||||
}
|
|
||||||
attributes
|
|
||||||
});
|
|
||||||
Some(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invert_attributes(attr: Option<Attributes>, base: Option<Attributes>) -> Attributes {
|
fn transform_attribute_data(left: AttributesData, right: AttributesData) -> AttributesData {
|
||||||
let attr = attr.unwrap_or(Attributes::new());
|
let result = right
|
||||||
let base = base.unwrap_or(Attributes::new());
|
|
||||||
|
|
||||||
let base_inverted = base
|
|
||||||
.iter()
|
.iter()
|
||||||
.fold(Attributes::new(), |mut attributes, (k, v)| {
|
.fold(AttributesData::new(), |mut new_attr_data, (k, v)| {
|
||||||
if base.get(k) != attr.get(k) && attr.contains_key(k) {
|
if !left.contains_key(k) {
|
||||||
attributes.insert(k.clone(), v.clone());
|
new_attr_data.insert(k.clone(), v.clone());
|
||||||
}
|
}
|
||||||
attributes
|
new_attr_data
|
||||||
});
|
});
|
||||||
|
result
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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,6 +1,6 @@
|
|||||||
use crate::{attributes::*, errors::OTError, operation::*};
|
use crate::{attributes::*, errors::OTError, operation::*};
|
||||||
use bytecount::num_chars;
|
use bytecount::num_chars;
|
||||||
use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator, str::FromStr};
|
use std::{cmp::Ordering, iter::FromIterator, str::FromStr};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Delta {
|
pub struct Delta {
|
||||||
@ -73,7 +73,7 @@ impl Delta {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, s: &str, attrs: Option<Attributes>) {
|
pub fn insert(&mut self, s: &str, attrs: Attributes) {
|
||||||
if s.is_empty() {
|
if s.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@ impl Delta {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn retain(&mut self, n: u64, attrs: Option<Attributes>) {
|
pub fn retain(&mut self, n: u64, attrs: Attributes) {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -149,14 +149,17 @@ impl Delta {
|
|||||||
next_op1 = ops1.next();
|
next_op1 = ops1.next();
|
||||||
},
|
},
|
||||||
(_, Some(Operation::Insert(o_insert))) => {
|
(_, Some(Operation::Insert(o_insert))) => {
|
||||||
new_delta.insert(&o_insert.s, attributes_from(&next_op2));
|
new_delta.insert(
|
||||||
|
&o_insert.s,
|
||||||
|
attributes_from(&next_op2).unwrap_or(Attributes::Empty),
|
||||||
|
);
|
||||||
next_op2 = ops2.next();
|
next_op2 = ops2.next();
|
||||||
},
|
},
|
||||||
(None, _) | (_, None) => {
|
(None, _) | (_, None) => {
|
||||||
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, false);
|
let composed_attrs = compose_attributes(&next_op1, &next_op2);
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"[retain:{} - retain:{}]: {:?}",
|
"[retain:{} - retain:{}]: {:?}",
|
||||||
retain.num,
|
retain.num,
|
||||||
@ -186,6 +189,7 @@ impl Delta {
|
|||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
next_op2 = Some(
|
next_op2 = Some(
|
||||||
OpBuilder::delete(*o_num - num_chars(insert.as_bytes()) as u64)
|
OpBuilder::delete(*o_num - num_chars(insert.as_bytes()) as u64)
|
||||||
|
.attributes(insert.attributes.clone())
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
next_op1 = ops1.next();
|
next_op1 = ops1.next();
|
||||||
@ -206,7 +210,7 @@ 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);
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"[insert:{} - retain:{}]: {:?}",
|
"[insert:{} - retain:{}]: {:?}",
|
||||||
insert.s,
|
insert.s,
|
||||||
@ -234,7 +238,11 @@ impl Delta {
|
|||||||
&chars.take(o_retain.num as usize).collect::<String>(),
|
&chars.take(o_retain.num as usize).collect::<String>(),
|
||||||
composed_attrs,
|
composed_attrs,
|
||||||
);
|
);
|
||||||
next_op1 = Some(OpBuilder::insert(&chars.collect::<String>()).build());
|
next_op1 = Some(
|
||||||
|
OpBuilder::insert(&chars.collect::<String>())
|
||||||
|
.attributes(Attributes::Empty)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
next_op2 = ops2.next();
|
next_op2 = ops2.next();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -297,7 +305,7 @@ impl Delta {
|
|||||||
(_, Some(Operation::Insert(o_insert))) => {
|
(_, Some(Operation::Insert(o_insert))) => {
|
||||||
let composed_attrs = transform_attributes(&next_op1, &next_op2, true);
|
let composed_attrs = transform_attributes(&next_op1, &next_op2, true);
|
||||||
a_prime.retain(o_insert.num_chars(), composed_attrs.clone());
|
a_prime.retain(o_insert.num_chars(), composed_attrs.clone());
|
||||||
b_prime.insert(&o_insert.s, composed_attrs.clone());
|
b_prime.insert(&o_insert.s, composed_attrs);
|
||||||
next_op2 = ops2.next();
|
next_op2 = ops2.next();
|
||||||
},
|
},
|
||||||
(None, _) => {
|
(None, _) => {
|
||||||
@ -427,7 +435,7 @@ 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, None);
|
inverted.retain(retain.num, Attributes::Follow);
|
||||||
for _ in 0..retain.num {
|
for _ in 0..retain.num {
|
||||||
chars.next();
|
chars.next();
|
||||||
}
|
}
|
||||||
@ -475,41 +483,47 @@ impl Delta {
|
|||||||
fn merge_insert_or_new_op(
|
fn merge_insert_or_new_op(
|
||||||
insert: &mut Insert,
|
insert: &mut Insert,
|
||||||
s: &str,
|
s: &str,
|
||||||
attributes: Option<Attributes>,
|
attributes: Attributes,
|
||||||
) -> Option<Operation> {
|
) -> Option<Operation> {
|
||||||
if attributes.is_none() {
|
match &attributes {
|
||||||
insert.s += s;
|
Attributes::Follow => {
|
||||||
return None;
|
insert.s += s;
|
||||||
}
|
return None;
|
||||||
|
},
|
||||||
if insert.attributes == attributes {
|
Attributes::Custom(_) | Attributes::Empty => {
|
||||||
insert.s += s;
|
if insert.attributes == attributes {
|
||||||
None
|
insert.s += s;
|
||||||
} else {
|
None
|
||||||
Some(OpBuilder::insert(s).attributes(attributes).build())
|
} else {
|
||||||
|
Some(OpBuilder::insert(s).attributes(attributes).build())
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_retain_or_new_op(
|
fn merge_retain_or_new_op(
|
||||||
retain: &mut Retain,
|
retain: &mut Retain,
|
||||||
n: u64,
|
n: u64,
|
||||||
attributes: Option<Attributes>,
|
attributes: Attributes,
|
||||||
) -> Option<Operation> {
|
) -> Option<Operation> {
|
||||||
log::trace!(
|
log::debug!(
|
||||||
"merge_retain_or_new_op: {:?}, {:?}",
|
"merge_retain_or_new_op: {:?}, {:?}",
|
||||||
retain.attributes,
|
retain.attributes,
|
||||||
attributes
|
attributes
|
||||||
);
|
);
|
||||||
|
|
||||||
if attributes.is_none() {
|
match &attributes {
|
||||||
retain.num += n;
|
Attributes::Follow => {
|
||||||
return None;
|
retain.num += n;
|
||||||
}
|
None
|
||||||
|
},
|
||||||
if retain.attributes == attributes {
|
Attributes::Custom(_) | Attributes::Empty => {
|
||||||
retain.num += n;
|
if retain.attributes == attributes {
|
||||||
None
|
retain.num += n;
|
||||||
} else {
|
None
|
||||||
Some(OpBuilder::retain(n).attributes(attributes).build())
|
} else {
|
||||||
|
Some(OpBuilder::retain(n).attributes(attributes).build())
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cmp::{max, min},
|
cmp::{max, min},
|
||||||
fmt,
|
fmt,
|
||||||
ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
|
ops::{Range, RangeInclusive, RangeTo, RangeToInclusive},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Representing a closed-open range;
|
/// Representing a closed-open range;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use crate::attributes::Attributes;
|
use crate::attributes::Attributes;
|
||||||
use bytecount::num_chars;
|
use bytecount::num_chars;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
|
||||||
collections::{hash_map::RandomState, HashMap},
|
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
str::Chars,
|
str::Chars,
|
||||||
};
|
};
|
||||||
@ -29,17 +27,19 @@ impl Operation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_attributes(&self) -> Option<Attributes> {
|
pub fn get_attributes(&self) -> Attributes {
|
||||||
match self {
|
match self {
|
||||||
Operation::Delete(_) => None,
|
Operation::Delete(_) => Attributes::Empty,
|
||||||
Operation::Retain(retain) => retain.attributes.clone(),
|
Operation::Retain(retain) => retain.attributes.clone(),
|
||||||
Operation::Insert(insert) => insert.attributes.clone(),
|
Operation::Insert(insert) => insert.attributes.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_attributes(&mut self, attributes: Option<Attributes>) {
|
pub fn set_attributes(&mut self, attributes: Attributes) {
|
||||||
match self {
|
match self {
|
||||||
Operation::Delete(_) => {},
|
Operation::Delete(_) => {
|
||||||
|
log::error!("Delete should not contains attributes");
|
||||||
|
},
|
||||||
Operation::Retain(retain) => {
|
Operation::Retain(retain) => {
|
||||||
retain.attributes = attributes;
|
retain.attributes = attributes;
|
||||||
},
|
},
|
||||||
@ -49,7 +49,13 @@ impl Operation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_plain(&self) -> bool { self.get_attributes().is_none() }
|
pub fn is_plain(&self) -> bool {
|
||||||
|
match self.get_attributes() {
|
||||||
|
Attributes::Follow => true,
|
||||||
|
Attributes::Custom(_) => false,
|
||||||
|
Attributes::Empty => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn length(&self) -> u64 {
|
pub fn length(&self) -> u64 {
|
||||||
match self {
|
match self {
|
||||||
@ -63,11 +69,16 @@ impl Operation {
|
|||||||
|
|
||||||
pub struct OpBuilder {
|
pub struct OpBuilder {
|
||||||
ty: Operation,
|
ty: Operation,
|
||||||
attrs: Option<Attributes>,
|
attrs: Attributes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpBuilder {
|
impl OpBuilder {
|
||||||
pub fn new(ty: Operation) -> OpBuilder { OpBuilder { ty, attrs: None } }
|
pub fn new(ty: Operation) -> OpBuilder {
|
||||||
|
OpBuilder {
|
||||||
|
ty,
|
||||||
|
attrs: Attributes::Empty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn retain(n: u64) -> OpBuilder { OpBuilder::new(Operation::Retain(n.into())) }
|
pub fn retain(n: u64) -> OpBuilder { OpBuilder::new(Operation::Retain(n.into())) }
|
||||||
|
|
||||||
@ -75,15 +86,8 @@ 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: Attributes) -> OpBuilder {
|
||||||
match attrs {
|
self.attrs = attrs;
|
||||||
None => self.attrs = attrs,
|
|
||||||
Some(attrs) => match attrs.is_empty() {
|
|
||||||
true => self.attrs = None,
|
|
||||||
false => self.attrs = Some(attrs),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,14 +107,14 @@ 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 = "is_empty")]
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
pub attributes: Option<Attributes>,
|
pub attributes: Attributes,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
num: n,
|
||||||
attributes: None,
|
attributes: Attributes::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,7 +135,7 @@ pub struct Insert {
|
|||||||
pub s: String,
|
pub s: String,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "is_empty")]
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
pub attributes: Option<Attributes>,
|
pub attributes: Attributes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Insert {
|
impl Insert {
|
||||||
@ -146,23 +150,22 @@ impl std::convert::From<String> for Insert {
|
|||||||
fn from(s: String) -> Self {
|
fn from(s: String) -> Self {
|
||||||
Insert {
|
Insert {
|
||||||
s,
|
s,
|
||||||
attributes: None,
|
attributes: Attributes::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<&str> for Insert {
|
impl std::convert::From<&str> for Insert {
|
||||||
fn from(s: &str) -> Self {
|
fn from(s: &str) -> Self { Insert::from(s.to_owned()) }
|
||||||
Insert {
|
|
||||||
s: s.to_owned(),
|
|
||||||
attributes: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_empty(attributes: &Option<Attributes>) -> bool {
|
fn is_empty(attributes: &Attributes) -> bool {
|
||||||
match attributes {
|
match attributes {
|
||||||
None => true,
|
Attributes::Follow => true,
|
||||||
Some(attributes) => attributes.is_empty(),
|
Attributes::Custom(data) => {
|
||||||
|
let is_empty = data.is_plain();
|
||||||
|
is_empty
|
||||||
|
},
|
||||||
|
Attributes::Empty => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use serde::{
|
|||||||
Serialize,
|
Serialize,
|
||||||
Serializer,
|
Serializer,
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, fmt};
|
use std::fmt;
|
||||||
|
|
||||||
impl Serialize for Operation {
|
impl Serialize for Operation {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
@ -82,7 +82,7 @@ impl<'de> Deserialize<'de> for Operation {
|
|||||||
match operation {
|
match operation {
|
||||||
None => Err(de::Error::missing_field("operation")),
|
None => Err(de::Error::missing_field("operation")),
|
||||||
Some(mut operation) => {
|
Some(mut operation) => {
|
||||||
operation.set_attributes(attributes);
|
operation.set_attributes(attributes.unwrap_or(Attributes::Empty));
|
||||||
Ok(operation)
|
Ok(operation)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,13 @@
|
|||||||
pub mod helper;
|
pub mod helper;
|
||||||
|
|
||||||
use crate::{
|
use crate::helper::{MergeTestOp::*, *};
|
||||||
helper::{MergeTestOp::*, *},
|
|
||||||
MergeTestOp::*,
|
|
||||||
};
|
|
||||||
use flowy_ot::{
|
use flowy_ot::{
|
||||||
attributes::{Attributes, AttrsBuilder},
|
|
||||||
delta::Delta,
|
|
||||||
interval::Interval,
|
interval::Interval,
|
||||||
operation::{OpBuilder, Operation, Retain},
|
operation::{OpBuilder, Operation, Retain},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_add_bold_attr1() {
|
fn delta_add_bold_and_invert_all() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
Insert(0, "123"),
|
Insert(0, "123"),
|
||||||
Bold(0, Interval::new(0, 3), true),
|
Bold(0, Interval::new(0, 3), true),
|
||||||
@ -24,7 +19,7 @@ fn delta_add_bold_attr1() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_add_bold_attr2() {
|
fn delta_add_bold_and_invert_partial_suffix() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
Insert(0, "1234"),
|
Insert(0, "1234"),
|
||||||
Bold(0, Interval::new(0, 4), true),
|
Bold(0, Interval::new(0, 4), true),
|
||||||
@ -39,7 +34,24 @@ fn delta_add_bold_attr2() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_add_bold_attr3() {
|
fn delta_add_bold_and_invert_partial_suffix2() {
|
||||||
|
let ops = vec![
|
||||||
|
Insert(0, "1234"),
|
||||||
|
Bold(0, Interval::new(0, 4), true),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
|
||||||
|
Bold(0, Interval::new(2, 4), false),
|
||||||
|
AssertOpsJson(
|
||||||
|
0,
|
||||||
|
r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#,
|
||||||
|
),
|
||||||
|
Bold(0, Interval::new(2, 4), true),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
|
||||||
|
];
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_add_bold_and_invert_partial_prefix() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
Insert(0, "1234"),
|
Insert(0, "1234"),
|
||||||
Bold(0, Interval::new(0, 4), true),
|
Bold(0, Interval::new(0, 4), true),
|
||||||
@ -53,6 +65,31 @@ fn delta_add_bold_attr3() {
|
|||||||
MergeTest::new().run_script(ops);
|
MergeTest::new().run_script(ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_add_bold_consecutive() {
|
||||||
|
let ops = vec![
|
||||||
|
Insert(0, "1234"),
|
||||||
|
Bold(0, Interval::new(0, 1), true),
|
||||||
|
AssertOpsJson(
|
||||||
|
0,
|
||||||
|
r#"[{"insert":"1","attributes":{"bold":"true"}},{"insert":"234"}]"#,
|
||||||
|
),
|
||||||
|
Bold(0, Interval::new(1, 2), true),
|
||||||
|
AssertOpsJson(
|
||||||
|
0,
|
||||||
|
r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
MergeTest::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);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_add_bold_italic() {
|
fn delta_add_bold_italic() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
@ -77,23 +114,6 @@ fn delta_add_bold_italic() {
|
|||||||
MergeTest::new().run_script(ops);
|
MergeTest::new().run_script(ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn delta_add_bold_attr_and_invert() {
|
|
||||||
let ops = vec![
|
|
||||||
Insert(0, "1234"),
|
|
||||||
Bold(0, Interval::new(0, 4), true),
|
|
||||||
AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
|
|
||||||
Bold(0, Interval::new(2, 4), false),
|
|
||||||
AssertOpsJson(
|
|
||||||
0,
|
|
||||||
r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#,
|
|
||||||
),
|
|
||||||
Bold(0, Interval::new(2, 4), true),
|
|
||||||
AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
|
|
||||||
];
|
|
||||||
MergeTest::new().run_script(ops);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_merge_inserted_text_with_same_attribute() {
|
fn delta_merge_inserted_text_with_same_attribute() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
@ -134,3 +154,53 @@ fn delta_compose_attr_delta_with_attr_delta_test() {
|
|||||||
|
|
||||||
MergeTest::new().run_script(ops);
|
MergeTest::new().run_script(ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_delete_heading() {
|
||||||
|
let ops = vec![
|
||||||
|
InsertBold(0, "123456", Interval::new(0, 6)),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
|
||||||
|
Delete(0, Interval::new(0, 2)),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"3456","attributes":{"bold":"true"}}]"#),
|
||||||
|
];
|
||||||
|
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_delete_trailing() {
|
||||||
|
let ops = vec![
|
||||||
|
InsertBold(0, "123456", Interval::new(0, 6)),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
|
||||||
|
Delete(0, Interval::new(5, 6)),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"12345","attributes":{"bold":"true"}}]"#),
|
||||||
|
];
|
||||||
|
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_delete_middle() {
|
||||||
|
let ops = vec![
|
||||||
|
InsertBold(0, "123456", Interval::new(0, 6)),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
|
||||||
|
Delete(0, Interval::new(0, 2)),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"3456","attributes":{"bold":"true"}}]"#),
|
||||||
|
Delete(0, Interval::new(2, 4)),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"34","attributes":{"bold":"true"}}]"#),
|
||||||
|
];
|
||||||
|
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_delete_all() {
|
||||||
|
let ops = vec![
|
||||||
|
InsertBold(0, "123456", Interval::new(0, 6)),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
|
||||||
|
Delete(0, Interval::new(0, 6)),
|
||||||
|
AssertOpsJson(0, r#"[]"#),
|
||||||
|
];
|
||||||
|
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use flowy_ot::{
|
use flowy_ot::{
|
||||||
attributes::{Attributes, AttrsBuilder},
|
attributes::{Attributes, AttributesData, AttrsBuilder},
|
||||||
delta::Delta,
|
delta::Delta,
|
||||||
interval::Interval,
|
interval::Interval,
|
||||||
operation::{OpBuilder, Operation},
|
operation::{OpBuilder, Operation},
|
||||||
@ -7,50 +7,6 @@ use flowy_ot::{
|
|||||||
use rand::{prelude::*, Rng as WrappedRng};
|
use rand::{prelude::*, Rng as WrappedRng};
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
|
|
||||||
pub struct Rng(StdRng);
|
|
||||||
|
|
||||||
impl Default for Rng {
|
|
||||||
fn default() -> Self { Rng(StdRng::from_rng(thread_rng()).unwrap()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rng {
|
|
||||||
pub fn from_seed(seed: [u8; 32]) -> Self { Rng(StdRng::from_seed(seed)) }
|
|
||||||
|
|
||||||
pub fn gen_string(&mut self, len: usize) -> String {
|
|
||||||
(0..len).map(|_| self.0.gen::<char>()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gen_delta(&mut self, s: &str) -> Delta {
|
|
||||||
let mut delta = Delta::default();
|
|
||||||
loop {
|
|
||||||
let left = s.chars().count() - delta.base_len();
|
|
||||||
if left == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let i = if left == 1 {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
1 + self.0.gen_range(0, std::cmp::min(left - 1, 20))
|
|
||||||
};
|
|
||||||
match self.0.gen_range(0.0, 1.0) {
|
|
||||||
f if f < 0.2 => {
|
|
||||||
delta.insert(&self.gen_string(i), None);
|
|
||||||
},
|
|
||||||
f if f < 0.4 => {
|
|
||||||
delta.delete(i as u64);
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
delta.retain(i as u64, None);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.0.gen_range(0.0, 1.0) < 0.3 {
|
|
||||||
delta.insert(&("1".to_owned() + &self.gen_string(10)), None);
|
|
||||||
}
|
|
||||||
delta
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum MergeTestOp {
|
pub enum MergeTestOp {
|
||||||
Insert(usize, &'static str),
|
Insert(usize, &'static str),
|
||||||
@ -58,6 +14,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),
|
||||||
|
Delete(usize, Interval),
|
||||||
Italic(usize, Interval, bool),
|
Italic(usize, Interval, bool),
|
||||||
Transform(usize, usize),
|
Transform(usize, usize),
|
||||||
AssertStr(usize, &'static str),
|
AssertStr(usize, &'static str),
|
||||||
@ -88,26 +45,31 @@ impl MergeTest {
|
|||||||
match op {
|
match op {
|
||||||
MergeTestOp::Insert(delta_i, s) => {
|
MergeTestOp::Insert(delta_i, s) => {
|
||||||
let delta = &mut self.deltas[*delta_i];
|
let delta = &mut self.deltas[*delta_i];
|
||||||
delta.insert(s, None);
|
delta.insert(s, Attributes::Follow);
|
||||||
},
|
},
|
||||||
MergeTestOp::InsertBold(delta_i, s, interval) => {
|
MergeTestOp::Delete(delta_i, interval) => {
|
||||||
|
//
|
||||||
|
self.update_delta_with_delete(*delta_i, interval);
|
||||||
|
},
|
||||||
|
MergeTestOp::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, Some(attrs));
|
delta.insert(s, attrs);
|
||||||
},
|
},
|
||||||
MergeTestOp::Bold(delta_i, interval, enable) => {
|
MergeTestOp::Bold(delta_i, interval, enable) => {
|
||||||
let attrs = AttrsBuilder::new().bold(*enable).build();
|
let attrs = AttrsBuilder::new().bold(*enable).build();
|
||||||
self.replace_delta(*delta_i, attrs, interval);
|
self.update_delta_with_attribute(*delta_i, attrs, interval);
|
||||||
},
|
},
|
||||||
MergeTestOp::Italic(delta_i, interval, enable) => {
|
MergeTestOp::Italic(delta_i, interval, enable) => {
|
||||||
let attrs = AttrsBuilder::new().italic(*enable).build();
|
let attrs = AttrsBuilder::new().italic(*enable).build();
|
||||||
self.replace_delta(*delta_i, attrs, interval);
|
self.update_delta_with_attribute(*delta_i, attrs, interval);
|
||||||
},
|
},
|
||||||
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];
|
||||||
let delta_b = &self.deltas[*delta_b_i];
|
let delta_b = &self.deltas[*delta_b_i];
|
||||||
|
|
||||||
let (a_prime, b_prime) = delta_a.transform(delta_b).unwrap();
|
let (a_prime, b_prime) = delta_a.transform(delta_b).unwrap();
|
||||||
|
log::trace!("a:{:?},b:{:?}", a_prime, b_prime);
|
||||||
let new_delta_a = delta_a.compose(&b_prime).unwrap();
|
let new_delta_a = delta_a.compose(&b_prime).unwrap();
|
||||||
let new_delta_b = delta_b.compose(&a_prime).unwrap();
|
let new_delta_b = delta_b.compose(&a_prime).unwrap();
|
||||||
|
|
||||||
@ -120,54 +82,62 @@ impl MergeTest {
|
|||||||
},
|
},
|
||||||
|
|
||||||
MergeTestOp::AssertOpsJson(delta_i, expected) => {
|
MergeTestOp::AssertOpsJson(delta_i, expected) => {
|
||||||
let expected_delta: Delta = serde_json::from_str(expected).unwrap();
|
|
||||||
|
|
||||||
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 delta: Delta = serde_json::from_str(&delta_i_json).unwrap();
|
|
||||||
|
|
||||||
if expected_delta != delta {
|
let expected_delta: Delta = serde_json::from_str(expected).unwrap();
|
||||||
|
let target_delta: Delta = serde_json::from_str(&delta_i_json).unwrap();
|
||||||
|
|
||||||
|
if expected_delta != target_delta {
|
||||||
log::error!("✅ {}", expected);
|
log::error!("✅ {}", expected);
|
||||||
log::error!("❌ {}", delta_i_json);
|
log::error!("❌ {}", delta_i_json);
|
||||||
}
|
}
|
||||||
assert_eq!(delta, expected_delta);
|
assert_eq!(target_delta, expected_delta);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_script(&mut self, script: Vec<MergeTestOp>) {
|
pub fn run_script(&mut self, script: Vec<MergeTestOp>) {
|
||||||
for (i, op) in script.iter().enumerate() {
|
for (_i, op) in script.iter().enumerate() {
|
||||||
self.run_op(op);
|
self.run_op(op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_delta(
|
pub fn update_delta_with_attribute(
|
||||||
&mut self,
|
&mut self,
|
||||||
delta_index: usize,
|
delta_index: usize,
|
||||||
attributes: Attributes,
|
attributes: Attributes,
|
||||||
interval: &Interval,
|
interval: &Interval,
|
||||||
) {
|
) {
|
||||||
let old_delta = &self.deltas[delta_index];
|
let old_delta = &self.deltas[delta_index];
|
||||||
let new_delta = delta_with_attribute(old_delta, attributes, interval);
|
let retain = OpBuilder::retain(interval.size() as u64)
|
||||||
|
.attributes(attributes)
|
||||||
|
.build();
|
||||||
|
let new_delta = make_delta_with_op(old_delta, retain, interval);
|
||||||
|
self.deltas[delta_index] = new_delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_delta_with_delete(&mut self, delta_index: usize, interval: &Interval) {
|
||||||
|
let old_delta = &self.deltas[delta_index];
|
||||||
|
let delete = OpBuilder::delete(interval.size() as u64).build();
|
||||||
|
let new_delta = make_delta_with_op(old_delta, delete, interval);
|
||||||
self.deltas[delta_index] = new_delta;
|
self.deltas[delta_index] = new_delta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delta_with_attribute(delta: &Delta, attributes: Attributes, interval: &Interval) -> Delta {
|
pub fn make_delta_with_op(delta: &Delta, op: Operation, interval: &Interval) -> Delta {
|
||||||
let delta_interval = Interval::new(0, delta.target_len);
|
|
||||||
|
|
||||||
let mut new_delta = Delta::default();
|
let mut new_delta = Delta::default();
|
||||||
let prefix = delta_interval.prefix(*interval);
|
let (prefix, suffix) = length_split_with_interval(delta.target_len, interval);
|
||||||
|
|
||||||
|
// prefix
|
||||||
if prefix.is_empty() == false && prefix != *interval {
|
if prefix.is_empty() == false && prefix != *interval {
|
||||||
let size = prefix.size();
|
let size = prefix.size();
|
||||||
let attrs = attributes_in_interval(delta, &prefix);
|
let attrs = attributes_in_interval(delta, &prefix);
|
||||||
new_delta.retain(size as u64, attrs);
|
new_delta.retain(size as u64, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = interval.size();
|
new_delta.add(op);
|
||||||
log::debug!("Apply attribute {:?} to {}", attributes, interval);
|
|
||||||
new_delta.retain(size as u64, Some(attributes));
|
|
||||||
|
|
||||||
let suffix = delta_interval.suffix(*interval);
|
// suffix
|
||||||
if suffix.is_empty() == false {
|
if suffix.is_empty() == false {
|
||||||
let size = suffix.size();
|
let size = suffix.size();
|
||||||
let attrs = attributes_in_interval(delta, &suffix);
|
let attrs = attributes_in_interval(delta, &suffix);
|
||||||
@ -177,31 +147,93 @@ pub fn delta_with_attribute(delta: &Delta, attributes: Attributes, interval: &In
|
|||||||
delta.compose(&new_delta).unwrap()
|
delta.compose(&new_delta).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn length_split_with_interval(length: usize, interval: &Interval) -> (Interval, Interval) {
|
||||||
|
let original_interval = Interval::new(0, length);
|
||||||
|
let prefix = original_interval.prefix(*interval);
|
||||||
|
let suffix = original_interval.suffix(*interval);
|
||||||
|
(prefix, suffix)
|
||||||
|
}
|
||||||
|
|
||||||
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) -> Option<Attributes> {
|
pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Attributes {
|
||||||
let mut attributes = Attributes::new();
|
let mut attributes_data = AttributesData::new();
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
delta.ops.iter().for_each(|op| match op {
|
delta.ops.iter().for_each(|op| match op {
|
||||||
Operation::Delete(n) => {},
|
Operation::Delete(_n) => {},
|
||||||
Operation::Retain(retain) => {
|
Operation::Retain(retain) => {
|
||||||
if retain.attributes.is_some() {
|
if interval.contains(retain.num as usize) {
|
||||||
if interval.contains(retain.num as usize) {
|
match &retain.attributes {
|
||||||
attributes.extend(retain.attributes.as_ref().unwrap().clone());
|
Attributes::Follow => {},
|
||||||
|
Attributes::Custom(data) => {
|
||||||
|
attributes_data.extend(data.clone());
|
||||||
|
},
|
||||||
|
Attributes::Empty => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Operation::Insert(insert) => {
|
Operation::Insert(insert) => match &insert.attributes {
|
||||||
if insert.attributes.is_some() {
|
Attributes::Follow => {},
|
||||||
|
Attributes::Custom(data) => {
|
||||||
if interval.start >= offset && insert.num_chars() > (interval.end as u64 - 1) {
|
if interval.start >= offset && insert.num_chars() > (interval.end as u64 - 1) {
|
||||||
attributes.extend(insert.attributes.as_ref().unwrap().clone());
|
attributes_data.extend(data.clone());
|
||||||
}
|
}
|
||||||
offset += insert.num_chars() as usize;
|
offset += insert.num_chars() as usize;
|
||||||
}
|
},
|
||||||
|
Attributes::Empty => {},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
Some(attributes)
|
|
||||||
|
if attributes_data.is_plain() {
|
||||||
|
Attributes::Empty
|
||||||
|
} else {
|
||||||
|
Attributes::Custom(attributes_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Rng(StdRng);
|
||||||
|
|
||||||
|
impl Default for Rng {
|
||||||
|
fn default() -> Self { Rng(StdRng::from_rng(thread_rng()).unwrap()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rng {
|
||||||
|
pub fn from_seed(seed: [u8; 32]) -> Self { Rng(StdRng::from_seed(seed)) }
|
||||||
|
|
||||||
|
pub fn gen_string(&mut self, len: usize) -> String {
|
||||||
|
(0..len).map(|_| self.0.gen::<char>()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_delta(&mut self, s: &str) -> Delta {
|
||||||
|
let mut delta = Delta::default();
|
||||||
|
loop {
|
||||||
|
let left = s.chars().count() - delta.base_len();
|
||||||
|
if left == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let i = if left == 1 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
1 + self.0.gen_range(0, std::cmp::min(left - 1, 20))
|
||||||
|
};
|
||||||
|
match self.0.gen_range(0.0, 1.0) {
|
||||||
|
f if f < 0.2 => {
|
||||||
|
delta.insert(&self.gen_string(i), Attributes::Empty);
|
||||||
|
},
|
||||||
|
f if f < 0.4 => {
|
||||||
|
delta.delete(i as u64);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
delta.retain(i as u64, Attributes::Empty);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.0.gen_range(0.0, 1.0) < 0.3 {
|
||||||
|
delta.insert(&("1".to_owned() + &self.gen_string(10)), Attributes::Empty);
|
||||||
|
}
|
||||||
|
delta
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,26 +2,21 @@ pub mod helper;
|
|||||||
|
|
||||||
use crate::helper::MergeTestOp::*;
|
use crate::helper::MergeTestOp::*;
|
||||||
use bytecount::num_chars;
|
use bytecount::num_chars;
|
||||||
use flowy_ot::{
|
use flowy_ot::{attributes::*, delta::Delta, operation::OpBuilder};
|
||||||
attributes::*,
|
|
||||||
delta::Delta,
|
|
||||||
operation::{OpBuilder, Operation},
|
|
||||||
};
|
|
||||||
use helper::*;
|
use helper::*;
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lengths() {
|
fn lengths() {
|
||||||
let mut delta = Delta::default();
|
let mut delta = Delta::default();
|
||||||
assert_eq!(delta.base_len, 0);
|
assert_eq!(delta.base_len, 0);
|
||||||
assert_eq!(delta.target_len, 0);
|
assert_eq!(delta.target_len, 0);
|
||||||
delta.retain(5, None);
|
delta.retain(5, Attributes::Empty);
|
||||||
assert_eq!(delta.base_len, 5);
|
assert_eq!(delta.base_len, 5);
|
||||||
assert_eq!(delta.target_len, 5);
|
assert_eq!(delta.target_len, 5);
|
||||||
delta.insert("abc", None);
|
delta.insert("abc", Attributes::Empty);
|
||||||
assert_eq!(delta.base_len, 5);
|
assert_eq!(delta.base_len, 5);
|
||||||
assert_eq!(delta.target_len, 8);
|
assert_eq!(delta.target_len, 8);
|
||||||
delta.retain(2, None);
|
delta.retain(2, Attributes::Empty);
|
||||||
assert_eq!(delta.base_len, 7);
|
assert_eq!(delta.base_len, 7);
|
||||||
assert_eq!(delta.target_len, 10);
|
assert_eq!(delta.target_len, 10);
|
||||||
delta.delete(2);
|
delta.delete(2);
|
||||||
@ -31,10 +26,10 @@ fn lengths() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn sequence() {
|
fn sequence() {
|
||||||
let mut delta = Delta::default();
|
let mut delta = Delta::default();
|
||||||
delta.retain(5, None);
|
delta.retain(5, Attributes::Empty);
|
||||||
delta.retain(0, None);
|
delta.retain(0, Attributes::Empty);
|
||||||
delta.insert("appflowy", None);
|
delta.insert("appflowy", Attributes::Empty);
|
||||||
delta.insert("", None);
|
delta.insert("", Attributes::Empty);
|
||||||
delta.delete(3);
|
delta.delete(3);
|
||||||
delta.delete(0);
|
delta.delete(0);
|
||||||
assert_eq!(delta.ops.len(), 3);
|
assert_eq!(delta.ops.len(), 3);
|
||||||
@ -55,11 +50,11 @@ fn apply_1000() {
|
|||||||
fn apply() {
|
fn apply() {
|
||||||
let s = "hello world,".to_owned();
|
let s = "hello world,".to_owned();
|
||||||
let mut delta_a = Delta::default();
|
let mut delta_a = Delta::default();
|
||||||
delta_a.insert(&s, None);
|
delta_a.insert(&s, Attributes::Empty);
|
||||||
|
|
||||||
let mut delta_b = Delta::default();
|
let mut delta_b = Delta::default();
|
||||||
delta_b.retain(s.len() as u64, None);
|
delta_b.retain(s.len() as u64, Attributes::Empty);
|
||||||
delta_b.insert("appflowy", None);
|
delta_b.insert("appflowy", Attributes::Empty);
|
||||||
|
|
||||||
let after_a = delta_a.apply("").unwrap();
|
let after_a = delta_a.apply("").unwrap();
|
||||||
let after_b = delta_b.apply(&after_a).unwrap();
|
let after_b = delta_b.apply(&after_a).unwrap();
|
||||||
@ -69,15 +64,15 @@ fn apply() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn base_len_test() {
|
fn base_len_test() {
|
||||||
let mut delta_a = Delta::default();
|
let mut delta_a = Delta::default();
|
||||||
delta_a.insert("a", None);
|
delta_a.insert("a", Attributes::Empty);
|
||||||
delta_a.insert("b", None);
|
delta_a.insert("b", Attributes::Empty);
|
||||||
delta_a.insert("c", None);
|
delta_a.insert("c", Attributes::Empty);
|
||||||
|
|
||||||
let s = "hello world,".to_owned();
|
let s = "hello world,".to_owned();
|
||||||
delta_a.delete(s.len() as u64);
|
delta_a.delete(s.len() as u64);
|
||||||
let after_a = delta_a.apply(&s).unwrap();
|
let after_a = delta_a.apply(&s).unwrap();
|
||||||
|
|
||||||
delta_a.insert("d", None);
|
delta_a.insert("d", Attributes::Empty);
|
||||||
assert_eq!("abc", &after_a);
|
assert_eq!("abc", &after_a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,8 +92,8 @@ fn invert() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn empty_ops() {
|
fn empty_ops() {
|
||||||
let mut delta = Delta::default();
|
let mut delta = Delta::default();
|
||||||
delta.retain(0, None);
|
delta.retain(0, Attributes::Empty);
|
||||||
delta.insert("", None);
|
delta.insert("", Attributes::Empty);
|
||||||
delta.delete(0);
|
delta.delete(0);
|
||||||
assert_eq!(delta.ops.len(), 0);
|
assert_eq!(delta.ops.len(), 0);
|
||||||
}
|
}
|
||||||
@ -106,33 +101,33 @@ fn empty_ops() {
|
|||||||
fn eq() {
|
fn eq() {
|
||||||
let mut delta_a = Delta::default();
|
let mut delta_a = Delta::default();
|
||||||
delta_a.delete(1);
|
delta_a.delete(1);
|
||||||
delta_a.insert("lo", None);
|
delta_a.insert("lo", Attributes::Empty);
|
||||||
delta_a.retain(2, None);
|
delta_a.retain(2, Attributes::Empty);
|
||||||
delta_a.retain(3, None);
|
delta_a.retain(3, Attributes::Empty);
|
||||||
let mut delta_b = Delta::default();
|
let mut delta_b = Delta::default();
|
||||||
delta_b.delete(1);
|
delta_b.delete(1);
|
||||||
delta_b.insert("l", None);
|
delta_b.insert("l", Attributes::Empty);
|
||||||
delta_b.insert("o", None);
|
delta_b.insert("o", Attributes::Empty);
|
||||||
delta_b.retain(5, None);
|
delta_b.retain(5, Attributes::Empty);
|
||||||
assert_eq!(delta_a, delta_b);
|
assert_eq!(delta_a, delta_b);
|
||||||
delta_a.delete(1);
|
delta_a.delete(1);
|
||||||
delta_b.retain(1, None);
|
delta_b.retain(1, Attributes::Empty);
|
||||||
assert_ne!(delta_a, delta_b);
|
assert_ne!(delta_a, delta_b);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn ops_merging() {
|
fn ops_merging() {
|
||||||
let mut delta = Delta::default();
|
let mut delta = Delta::default();
|
||||||
assert_eq!(delta.ops.len(), 0);
|
assert_eq!(delta.ops.len(), 0);
|
||||||
delta.retain(2, None);
|
delta.retain(2, Attributes::Empty);
|
||||||
assert_eq!(delta.ops.len(), 1);
|
assert_eq!(delta.ops.len(), 1);
|
||||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(2).build()));
|
assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(2).build()));
|
||||||
delta.retain(3, None);
|
delta.retain(3, Attributes::Empty);
|
||||||
assert_eq!(delta.ops.len(), 1);
|
assert_eq!(delta.ops.len(), 1);
|
||||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(5).build()));
|
assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(5).build()));
|
||||||
delta.insert("abc", None);
|
delta.insert("abc", Attributes::Empty);
|
||||||
assert_eq!(delta.ops.len(), 2);
|
assert_eq!(delta.ops.len(), 2);
|
||||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abc").build()));
|
assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abc").build()));
|
||||||
delta.insert("xyz", None);
|
delta.insert("xyz", Attributes::Empty);
|
||||||
assert_eq!(delta.ops.len(), 2);
|
assert_eq!(delta.ops.len(), 2);
|
||||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abcxyz").build()));
|
assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abcxyz").build()));
|
||||||
delta.delete(1);
|
delta.delete(1);
|
||||||
@ -146,11 +141,11 @@ fn ops_merging() {
|
|||||||
fn is_noop() {
|
fn is_noop() {
|
||||||
let mut delta = Delta::default();
|
let mut delta = Delta::default();
|
||||||
assert!(delta.is_noop());
|
assert!(delta.is_noop());
|
||||||
delta.retain(5, None);
|
delta.retain(5, Attributes::Empty);
|
||||||
assert!(delta.is_noop());
|
assert!(delta.is_noop());
|
||||||
delta.retain(3, None);
|
delta.retain(3, Attributes::Empty);
|
||||||
assert!(delta.is_noop());
|
assert!(delta.is_noop());
|
||||||
delta.insert("lorem", None);
|
delta.insert("lorem", Attributes::Empty);
|
||||||
assert!(!delta.is_noop());
|
assert!(!delta.is_noop());
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@ -184,9 +179,9 @@ fn transform() {
|
|||||||
let ba_prime = b.compose(&a_prime).unwrap();
|
let ba_prime = b.compose(&a_prime).unwrap();
|
||||||
assert_eq!(ab_prime, ba_prime);
|
assert_eq!(ab_prime, ba_prime);
|
||||||
|
|
||||||
// let after_ab_prime = ab_prime.apply(&s).unwrap();
|
let after_ab_prime = ab_prime.apply(&s).unwrap();
|
||||||
// let after_ba_prime = ba_prime.apply(&s).unwrap();
|
let after_ba_prime = ba_prime.apply(&s).unwrap();
|
||||||
// assert_eq!(after_ab_prime, after_ba_prime);
|
assert_eq!(after_ab_prime, after_ba_prime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,13 +203,13 @@ 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(true).build()));
|
a.insert("123", AttrsBuilder::new().bold(true).build());
|
||||||
a_s = a.apply(&a_s).unwrap();
|
a_s = a.apply(&a_s).unwrap();
|
||||||
assert_eq!(&a_s, "123");
|
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", Attributes::Empty);
|
||||||
b_s = b.apply(&b_s).unwrap();
|
b_s = b.apply(&b_s).unwrap();
|
||||||
assert_eq!(&b_s, "456");
|
assert_eq!(&b_s, "456");
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use flowy_ot::{
|
use flowy_ot::{
|
||||||
attributes::{Attributes, AttrsBuilder},
|
attributes::AttrsBuilder,
|
||||||
delta::Delta,
|
delta::Delta,
|
||||||
operation::{OpBuilder, Operation, Retain},
|
operation::{OpBuilder, Operation, Retain},
|
||||||
};
|
};
|
||||||
@ -7,9 +7,7 @@ use flowy_ot::{
|
|||||||
#[test]
|
#[test]
|
||||||
fn operation_insert_serialize_test() {
|
fn operation_insert_serialize_test() {
|
||||||
let attributes = AttrsBuilder::new().bold(true).italic(true).build();
|
let attributes = AttrsBuilder::new().bold(true).italic(true).build();
|
||||||
let operation = OpBuilder::insert("123")
|
let operation = OpBuilder::insert("123").attributes(attributes).build();
|
||||||
.attributes(Some(attributes))
|
|
||||||
.build();
|
|
||||||
let json = serde_json::to_string(&operation).unwrap();
|
let json = serde_json::to_string(&operation).unwrap();
|
||||||
eprintln!("{}", json);
|
eprintln!("{}", json);
|
||||||
|
|
||||||
@ -39,9 +37,7 @@ fn delta_serialize_test() {
|
|||||||
let mut delta = Delta::default();
|
let mut delta = Delta::default();
|
||||||
|
|
||||||
let attributes = AttrsBuilder::new().bold(true).italic(true).build();
|
let attributes = AttrsBuilder::new().bold(true).italic(true).build();
|
||||||
let retain = OpBuilder::insert("123")
|
let retain = OpBuilder::insert("123").attributes(attributes).build();
|
||||||
.attributes(Some(attributes))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
delta.add(retain);
|
delta.add(retain);
|
||||||
delta.add(Operation::Retain(5.into()));
|
delta.add(Operation::Retain(5.into()));
|
||||||
|
Loading…
Reference in New Issue
Block a user