mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
attribute insert test
This commit is contained in:
parent
592244f6b9
commit
efaee88466
@ -1,3 +1,4 @@
|
||||
use crate::operation::Operation;
|
||||
use std::collections::{hash_map::RandomState, HashMap};
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
@ -64,11 +65,21 @@ impl AttributesBuilder {
|
||||
pub fn build(self) -> Attributes { self.inner }
|
||||
}
|
||||
|
||||
pub fn attributes_from(operation: &Option<Operation>) -> Option<Attributes> {
|
||||
match operation {
|
||||
None => None,
|
||||
Some(operation) => operation.attributes(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compose_attributes(
|
||||
a: Option<Attributes>,
|
||||
b: Option<Attributes>,
|
||||
op1: &Option<Operation>,
|
||||
op2: &Option<Operation>,
|
||||
keep_empty: bool,
|
||||
) -> Option<Attributes> {
|
||||
let a = attributes_from(op1);
|
||||
let b = attributes_from(op2);
|
||||
|
||||
if a.is_none() {
|
||||
return b;
|
||||
}
|
||||
@ -93,10 +104,13 @@ pub fn compose_attributes(
|
||||
}
|
||||
|
||||
pub fn transform_attributes(
|
||||
a: Option<Attributes>,
|
||||
b: Option<Attributes>,
|
||||
op1: &Option<Operation>,
|
||||
op2: &Option<Operation>,
|
||||
priority: bool,
|
||||
) -> Option<Attributes> {
|
||||
let a = attributes_from(op1);
|
||||
let b = attributes_from(op2);
|
||||
|
||||
if a.is_none() {
|
||||
return b;
|
||||
}
|
||||
|
@ -77,26 +77,29 @@ impl Delta {
|
||||
if s.is_empty() {
|
||||
return;
|
||||
}
|
||||
self.target_len += num_chars(s.as_bytes());
|
||||
|
||||
self.target_len += num_chars(s.as_bytes());
|
||||
let new_last = match self.ops.as_mut_slice() {
|
||||
[.., Operation::Insert(s_last)] => {
|
||||
s_last.s += &s;
|
||||
return;
|
||||
//
|
||||
merge_insert_or_new_op(s_last, s, attrs)
|
||||
},
|
||||
[.., Operation::Insert(s_pre_last), Operation::Delete(_)] => {
|
||||
s_pre_last.s += s;
|
||||
return;
|
||||
//
|
||||
merge_insert_or_new_op(s_pre_last, s, attrs)
|
||||
},
|
||||
[.., op_last @ Operation::Delete(_)] => {
|
||||
let new_last = op_last.clone();
|
||||
*op_last = Operation::Insert(s.into());
|
||||
new_last
|
||||
*op_last = OpBuilder::insert(s).attributes(attrs).build();
|
||||
Some(new_last)
|
||||
},
|
||||
_ => Operation::Insert(s.into()),
|
||||
_ => Some(OpBuilder::insert(s).attributes(attrs).build()),
|
||||
};
|
||||
self.ops
|
||||
.push(OpBuilder::new(new_last).attributes(attrs).build());
|
||||
|
||||
match new_last {
|
||||
None => {},
|
||||
Some(new_last) => self.ops.push(new_last),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn retain(&mut self, n: u64, attrs: Option<Attributes>) {
|
||||
@ -107,8 +110,10 @@ impl Delta {
|
||||
self.target_len += n as usize;
|
||||
|
||||
if let Some(Operation::Retain(i_last)) = self.ops.last_mut() {
|
||||
i_last.n += n;
|
||||
i_last.attributes = attrs;
|
||||
match merge_retain_or_new_op(i_last, n, attrs) {
|
||||
None => {},
|
||||
Some(new_op) => self.ops.push(new_op),
|
||||
}
|
||||
} else {
|
||||
self.ops
|
||||
.push(OpBuilder::retain(n).attributes(attrs).build());
|
||||
@ -144,15 +149,14 @@ impl Delta {
|
||||
next_op1 = ops1.next();
|
||||
},
|
||||
(_, Some(Operation::Insert(insert))) => {
|
||||
new_delta.insert(&insert.s, get_attrs(&next_op2));
|
||||
new_delta.insert(&insert.s, attributes_from(&next_op2));
|
||||
next_op2 = ops2.next();
|
||||
},
|
||||
(None, _) | (_, None) => {
|
||||
return Err(OTError);
|
||||
},
|
||||
(Some(Operation::Retain(i)), Some(Operation::Retain(j))) => {
|
||||
let new_attrs =
|
||||
compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), true);
|
||||
let new_attrs = compose_attributes(&next_op1, &next_op2, true);
|
||||
match i.cmp(&j) {
|
||||
Ordering::Less => {
|
||||
new_delta.retain(i.n, new_attrs);
|
||||
@ -185,16 +189,17 @@ impl Delta {
|
||||
},
|
||||
Ordering::Greater => {
|
||||
next_op1 = Some(
|
||||
OpBuilder::insert(insert.chars().skip(*j as usize).collect())
|
||||
.build(),
|
||||
OpBuilder::insert(
|
||||
&insert.chars().skip(*j as usize).collect::<String>(),
|
||||
)
|
||||
.build(),
|
||||
);
|
||||
next_op2 = ops2.next();
|
||||
},
|
||||
}
|
||||
},
|
||||
(Some(Operation::Insert(insert)), Some(Operation::Retain(j))) => {
|
||||
let new_attrs =
|
||||
compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), false);
|
||||
let new_attrs = compose_attributes(&next_op1, &next_op2, false);
|
||||
match (insert.num_chars()).cmp(j) {
|
||||
Ordering::Less => {
|
||||
new_delta.insert(&insert.s, new_attrs);
|
||||
@ -210,7 +215,7 @@ impl Delta {
|
||||
let chars = &mut insert.chars();
|
||||
new_delta
|
||||
.insert(&chars.take(j.n as usize).collect::<String>(), new_attrs);
|
||||
next_op1 = Some(OpBuilder::insert(chars.collect()).build());
|
||||
next_op1 = Some(OpBuilder::insert(&chars.collect::<String>()).build());
|
||||
next_op2 = ops2.next();
|
||||
},
|
||||
}
|
||||
@ -263,15 +268,13 @@ impl Delta {
|
||||
match (&next_op1, &next_op2) {
|
||||
(None, None) => break,
|
||||
(Some(Operation::Insert(insert)), _) => {
|
||||
let new_attrs =
|
||||
compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), true);
|
||||
let new_attrs = compose_attributes(&next_op1, &next_op2, true);
|
||||
a_prime.insert(&insert.s, new_attrs.clone());
|
||||
b_prime.retain(insert.num_chars(), new_attrs.clone());
|
||||
next_op1 = ops1.next();
|
||||
},
|
||||
(_, Some(Operation::Insert(insert))) => {
|
||||
let new_attrs =
|
||||
compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), true);
|
||||
let new_attrs = compose_attributes(&next_op1, &next_op2, true);
|
||||
a_prime.retain(insert.num_chars(), new_attrs.clone());
|
||||
b_prime.insert(&insert.s, new_attrs.clone());
|
||||
next_op2 = ops2.next();
|
||||
@ -283,8 +286,7 @@ impl Delta {
|
||||
return Err(OTError);
|
||||
},
|
||||
(Some(Operation::Retain(i)), Some(Operation::Retain(j))) => {
|
||||
let new_attrs =
|
||||
compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), true);
|
||||
let new_attrs = compose_attributes(&next_op1, &next_op2, true);
|
||||
match i.cmp(&j) {
|
||||
Ordering::Less => {
|
||||
a_prime.retain(i.n, new_attrs.clone());
|
||||
@ -449,9 +451,33 @@ impl Delta {
|
||||
pub fn is_empty(&self) -> bool { self.ops.is_empty() }
|
||||
}
|
||||
|
||||
pub fn get_attrs(operation: &Option<Operation>) -> Option<Attributes> {
|
||||
match operation {
|
||||
None => None,
|
||||
Some(operation) => operation.attributes(),
|
||||
fn merge_insert_or_new_op(
|
||||
insert: &mut Insert,
|
||||
s: &str,
|
||||
attributes: Option<Attributes>,
|
||||
) -> Option<Operation> {
|
||||
if insert.attributes == attributes {
|
||||
insert.s += s;
|
||||
None
|
||||
} else {
|
||||
Some(OpBuilder::insert(s).attributes(attributes).build())
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_retain_or_new_op(
|
||||
retain: &mut Retain,
|
||||
n: u64,
|
||||
attributes: Option<Attributes>,
|
||||
) -> Option<Operation> {
|
||||
if attributes.is_none() {
|
||||
retain.n += n;
|
||||
return None;
|
||||
}
|
||||
|
||||
if retain.attributes == attributes {
|
||||
retain.n += n;
|
||||
None
|
||||
} else {
|
||||
Some(OpBuilder::retain(n).attributes(attributes).build())
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ impl Operation {
|
||||
Operation::Insert(i) => i.num_chars(),
|
||||
}
|
||||
}
|
||||
pub fn is_empty(&self) -> bool { self.length() == 0 }
|
||||
}
|
||||
|
||||
pub struct OpBuilder {
|
||||
@ -72,7 +73,7 @@ impl OpBuilder {
|
||||
|
||||
pub fn delete(n: u64) -> OpBuilder { OpBuilder::new(Operation::Delete(n)) }
|
||||
|
||||
pub fn insert(s: String) -> 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 {
|
||||
self.attrs = attrs;
|
||||
|
16
rust-lib/flowy-ot/tests/attribute_test.rs
Normal file
16
rust-lib/flowy-ot/tests/attribute_test.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use flowy_ot::{
|
||||
attributes::{Attributes, AttributesBuilder},
|
||||
delta::Delta,
|
||||
operation::{OpBuilder, Operation, Retain},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn attribute_insert_merge_test() {
|
||||
let mut delta = Delta::default();
|
||||
delta.insert("123", Some(AttributesBuilder::new().bold().build()));
|
||||
delta.insert("456", Some(AttributesBuilder::new().bold().build()));
|
||||
assert_eq!(
|
||||
r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#,
|
||||
serde_json::to_string(&delta).unwrap()
|
||||
)
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
mod helper;
|
||||
mod serde_test;
|
@ -1,10 +1,12 @@
|
||||
use crate::helper::Rng;
|
||||
pub mod helper;
|
||||
|
||||
use bytecount::num_chars;
|
||||
use flowy_ot::{
|
||||
attributes::*,
|
||||
delta::Delta,
|
||||
operation::{OpBuilder, Operation},
|
||||
};
|
||||
use helper::*;
|
||||
|
||||
#[test]
|
||||
fn lengths() {
|
||||
@ -126,16 +128,10 @@ fn ops_merging() {
|
||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(5).build()));
|
||||
delta.insert("abc", None);
|
||||
assert_eq!(delta.ops.len(), 2);
|
||||
assert_eq!(
|
||||
delta.ops.last(),
|
||||
Some(&OpBuilder::insert("abc".to_owned()).build())
|
||||
);
|
||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abc").build()));
|
||||
delta.insert("xyz", None);
|
||||
assert_eq!(delta.ops.len(), 2);
|
||||
assert_eq!(
|
||||
delta.ops.last(),
|
||||
Some(&OpBuilder::insert("abcxyz".to_owned()).build())
|
||||
);
|
||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abcxyz").build()));
|
||||
delta.delete(1);
|
||||
assert_eq!(delta.ops.len(), 3);
|
||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(1).build()));
|
||||
|
@ -7,7 +7,7 @@ use flowy_ot::{
|
||||
#[test]
|
||||
fn operation_insert_serialize_test() {
|
||||
let attributes = AttributesBuilder::new().bold().italic().build();
|
||||
let operation = OpBuilder::insert("123".to_owned())
|
||||
let operation = OpBuilder::insert("123")
|
||||
.attributes(Some(attributes))
|
||||
.build();
|
||||
let json = serde_json::to_string(&operation).unwrap();
|
||||
@ -39,7 +39,7 @@ fn delta_serialize_test() {
|
||||
let mut delta = Delta::default();
|
||||
|
||||
let attributes = AttributesBuilder::new().bold().italic().build();
|
||||
let retain = OpBuilder::insert("123".to_owned())
|
||||
let retain = OpBuilder::insert("123")
|
||||
.attributes(Some(attributes))
|
||||
.build();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user