mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
add attributes
This commit is contained in:
parent
b449707021
commit
a3ed1b2874
@ -1,6 +1,6 @@
|
||||
use flowy_test::builder::SingleUserTestBuilder;
|
||||
|
||||
use flowy_editor::{entities::doc::*, event::EditorEvent::*};
|
||||
use flowy_document::{entities::doc::*, event::EditorEvent::*};
|
||||
use flowy_infra::uuid;
|
||||
|
||||
pub fn create_doc(name: &str, desc: &str, text: &str) -> DocInfo {
|
||||
|
@ -33,39 +33,87 @@ impl std::ops::DerefMut for Attributes {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
|
||||
}
|
||||
|
||||
pub fn compose_attributes(
|
||||
mut a: Attributes,
|
||||
b: Attributes,
|
||||
keep_empty: bool,
|
||||
) -> Option<Attributes> {
|
||||
a.extend(b);
|
||||
let mut result = a;
|
||||
if !keep_empty {
|
||||
result.remove_empty_value()
|
||||
pub struct AttributesBuilder {
|
||||
inner: Attributes,
|
||||
}
|
||||
|
||||
return if result.is_empty() {
|
||||
impl AttributesBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: Attributes::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bold(mut self) -> Self {
|
||||
self.inner.insert("bold".to_owned(), "true".to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn italic(mut self) -> Self {
|
||||
self.inner.insert("italic".to_owned(), "true".to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn underline(mut self) -> Self {
|
||||
self.inner.insert("underline".to_owned(), "true".to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Attributes { self.inner }
|
||||
}
|
||||
|
||||
pub fn compose_attributes(
|
||||
a: Option<Attributes>,
|
||||
b: Option<Attributes>,
|
||||
keep_empty: bool,
|
||||
) -> Option<Attributes> {
|
||||
if a.is_none() {
|
||||
return b;
|
||||
}
|
||||
|
||||
if b.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut attrs_a = a.unwrap_or(Attributes::default());
|
||||
let attrs_b = b.unwrap_or(Attributes::default());
|
||||
attrs_a.extend(attrs_b);
|
||||
|
||||
if !keep_empty {
|
||||
attrs_a.remove_empty_value()
|
||||
}
|
||||
|
||||
return if attrs_a.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(result)
|
||||
Some(attrs_a)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn transform_attributes(a: Attributes, b: Attributes, priority: bool) -> Option<Attributes> {
|
||||
if a.is_empty() {
|
||||
return Some(b);
|
||||
pub fn transform_attributes(
|
||||
a: Option<Attributes>,
|
||||
b: Option<Attributes>,
|
||||
priority: bool,
|
||||
) -> Option<Attributes> {
|
||||
if a.is_none() {
|
||||
return b;
|
||||
}
|
||||
|
||||
if b.is_empty() {
|
||||
if b.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if !priority {
|
||||
return Some(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
let result = b.iter().fold(Attributes::new(), |mut attributes, (k, v)| {
|
||||
if a.contains_key(k) == false {
|
||||
let attrs_a = a.unwrap_or(Attributes::default());
|
||||
let attrs_b = b.unwrap_or(Attributes::default());
|
||||
|
||||
let result = attrs_b
|
||||
.iter()
|
||||
.fold(Attributes::new(), |mut attributes, (k, v)| {
|
||||
if attrs_a.contains_key(k) == false {
|
||||
attributes.insert(k.clone(), v.clone());
|
||||
}
|
||||
attributes
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{errors::OTError, operation::*};
|
||||
use crate::{attributes::*, errors::OTError, operation::*};
|
||||
use bytecount::num_chars;
|
||||
use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator};
|
||||
|
||||
@ -19,15 +19,15 @@ impl Default for Delta {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<OpType> for Delta {
|
||||
fn from_iter<T: IntoIterator<Item = OpType>>(ops: T) -> Self {
|
||||
let mut operations = Delta::default();
|
||||
for op in ops {
|
||||
operations.add(op);
|
||||
}
|
||||
operations
|
||||
}
|
||||
}
|
||||
// impl FromIterator<OpType> for Delta {
|
||||
// fn from_iter<T: IntoIterator<Item = OpType>>(ops: T) -> Self {
|
||||
// let mut operations = Delta::default();
|
||||
// for op in ops {
|
||||
// operations.add(op);
|
||||
// }
|
||||
// operations
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Delta {
|
||||
#[inline]
|
||||
@ -39,13 +39,13 @@ impl Delta {
|
||||
}
|
||||
}
|
||||
|
||||
fn add(&mut self, op: OpType) {
|
||||
match op {
|
||||
OpType::Delete(i) => self.delete(i),
|
||||
OpType::Insert(s) => self.insert(&s),
|
||||
OpType::Retain(i) => self.retain(i),
|
||||
}
|
||||
}
|
||||
// fn add(&mut self, op: OpType) {
|
||||
// match op {
|
||||
// OpType::Delete(i) => self.delete(i),
|
||||
// OpType::Insert(s) => self.insert(&s),
|
||||
// OpType::Retain(i) => self.retain(i),
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn delete(&mut self, n: u64) {
|
||||
if n == 0 {
|
||||
@ -63,7 +63,7 @@ impl Delta {
|
||||
self.ops.push(OperationBuilder::delete(n).build());
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, s: &str) {
|
||||
pub fn insert(&mut self, s: &str, attrs: Option<Attributes>) {
|
||||
if s.is_empty() {
|
||||
return;
|
||||
}
|
||||
@ -90,10 +90,11 @@ impl Delta {
|
||||
},
|
||||
_ => OpType::Insert(s.to_owned()),
|
||||
};
|
||||
self.ops.push(OperationBuilder::new(new_last).build());
|
||||
self.ops
|
||||
.push(OperationBuilder::new(new_last).with_attrs(attrs).build());
|
||||
}
|
||||
|
||||
pub fn retain(&mut self, n: u64) {
|
||||
pub fn retain(&mut self, n: u64, attrs: Option<Attributes>) {
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
@ -103,11 +104,13 @@ impl Delta {
|
||||
if let Some(operation) = self.ops.last_mut() {
|
||||
if operation.ty.is_retain() {
|
||||
operation.retain(n);
|
||||
operation.set_attrs(attrs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.ops.push(OperationBuilder::retain(n).build());
|
||||
self.ops
|
||||
.push(OperationBuilder::retain(n).with_attrs(attrs).build());
|
||||
}
|
||||
|
||||
/// Merges the operation with `other` into one operation while preserving
|
||||
@ -142,28 +145,35 @@ impl Delta {
|
||||
maybe_op1 = ops1.next();
|
||||
},
|
||||
(_, Some(OpType::Insert(s))) => {
|
||||
new_delta.insert(s);
|
||||
new_delta.insert(s, operation_attrs(&maybe_op2));
|
||||
maybe_op2 = ops2.next();
|
||||
},
|
||||
(None, _) | (_, None) => {
|
||||
return Err(OTError);
|
||||
},
|
||||
(Some(OpType::Retain(i)), Some(OpType::Retain(j))) => match i.cmp(&j) {
|
||||
(Some(OpType::Retain(i)), Some(OpType::Retain(j))) => {
|
||||
let new_attrs = compose_attributes(
|
||||
operation_attrs(&maybe_op1),
|
||||
operation_attrs(&maybe_op2),
|
||||
true,
|
||||
);
|
||||
match i.cmp(&j) {
|
||||
Ordering::Less => {
|
||||
new_delta.retain(*i);
|
||||
new_delta.retain(*i, new_attrs);
|
||||
maybe_op2 = Some(OperationBuilder::retain(*j - *i).build());
|
||||
maybe_op1 = ops1.next();
|
||||
},
|
||||
std::cmp::Ordering::Equal => {
|
||||
new_delta.retain(*i);
|
||||
new_delta.retain(*i, new_attrs);
|
||||
maybe_op1 = ops1.next();
|
||||
maybe_op2 = ops2.next();
|
||||
},
|
||||
std::cmp::Ordering::Greater => {
|
||||
new_delta.retain(*j);
|
||||
new_delta.retain(*j, new_attrs);
|
||||
maybe_op1 = Some(OperationBuilder::retain(*i - *j).build());
|
||||
maybe_op2 = ops2.next();
|
||||
},
|
||||
}
|
||||
},
|
||||
(Some(OpType::Insert(s)), Some(OpType::Delete(j))) => {
|
||||
match (num_chars(s.as_bytes()) as u64).cmp(j) {
|
||||
@ -188,9 +198,14 @@ impl Delta {
|
||||
}
|
||||
},
|
||||
(Some(OpType::Insert(s)), Some(OpType::Retain(j))) => {
|
||||
let new_attrs = compose_attributes(
|
||||
operation_attrs(&maybe_op1),
|
||||
operation_attrs(&maybe_op2),
|
||||
false,
|
||||
);
|
||||
match (num_chars(s.as_bytes()) as u64).cmp(j) {
|
||||
Ordering::Less => {
|
||||
new_delta.insert(s);
|
||||
new_delta.insert(s, new_attrs);
|
||||
maybe_op2 = Some(
|
||||
OperationBuilder::retain(*j - num_chars(s.as_bytes()) as u64)
|
||||
.build(),
|
||||
@ -198,13 +213,14 @@ impl Delta {
|
||||
maybe_op1 = ops1.next();
|
||||
},
|
||||
Ordering::Equal => {
|
||||
new_delta.insert(s);
|
||||
new_delta.insert(s, new_attrs);
|
||||
maybe_op1 = ops1.next();
|
||||
maybe_op2 = ops2.next();
|
||||
},
|
||||
Ordering::Greater => {
|
||||
let chars = &mut s.chars();
|
||||
new_delta.insert(&chars.take(*j as usize).collect::<String>());
|
||||
new_delta
|
||||
.insert(&chars.take(*j as usize).collect::<String>(), new_attrs);
|
||||
maybe_op1 = Some(OperationBuilder::insert(chars.collect()).build());
|
||||
maybe_op2 = ops2.next();
|
||||
},
|
||||
@ -261,13 +277,23 @@ impl Delta {
|
||||
) {
|
||||
(None, None) => break,
|
||||
(Some(OpType::Insert(s)), _) => {
|
||||
a_prime.insert(s);
|
||||
b_prime.retain(num_chars(s.as_bytes()) as _);
|
||||
let new_attrs = compose_attributes(
|
||||
operation_attrs(&maybe_op1),
|
||||
operation_attrs(&maybe_op2),
|
||||
true,
|
||||
);
|
||||
a_prime.insert(s, new_attrs.clone());
|
||||
b_prime.retain(num_chars(s.as_bytes()) as _, new_attrs.clone());
|
||||
maybe_op1 = ops1.next();
|
||||
},
|
||||
(_, Some(OpType::Insert(s))) => {
|
||||
a_prime.retain(num_chars(s.as_bytes()) as _);
|
||||
b_prime.insert(s);
|
||||
let new_attrs = compose_attributes(
|
||||
operation_attrs(&maybe_op1),
|
||||
operation_attrs(&maybe_op2),
|
||||
true,
|
||||
);
|
||||
a_prime.retain(num_chars(s.as_bytes()) as _, new_attrs.clone());
|
||||
b_prime.insert(s, new_attrs.clone());
|
||||
maybe_op2 = ops2.next();
|
||||
},
|
||||
(None, _) => {
|
||||
@ -277,22 +303,27 @@ impl Delta {
|
||||
return Err(OTError);
|
||||
},
|
||||
(Some(OpType::Retain(i)), Some(OpType::Retain(j))) => {
|
||||
let new_attrs = compose_attributes(
|
||||
operation_attrs(&maybe_op1),
|
||||
operation_attrs(&maybe_op2),
|
||||
true,
|
||||
);
|
||||
match i.cmp(&j) {
|
||||
Ordering::Less => {
|
||||
a_prime.retain(*i);
|
||||
b_prime.retain(*i);
|
||||
a_prime.retain(*i, new_attrs.clone());
|
||||
b_prime.retain(*i, new_attrs.clone());
|
||||
maybe_op2 = Some(OperationBuilder::retain(*j - *i).build());
|
||||
maybe_op1 = ops1.next();
|
||||
},
|
||||
Ordering::Equal => {
|
||||
a_prime.retain(*i);
|
||||
b_prime.retain(*i);
|
||||
a_prime.retain(*i, new_attrs.clone());
|
||||
b_prime.retain(*i, new_attrs.clone());
|
||||
maybe_op1 = ops1.next();
|
||||
maybe_op2 = ops2.next();
|
||||
},
|
||||
Ordering::Greater => {
|
||||
a_prime.retain(*j);
|
||||
b_prime.retain(*j);
|
||||
a_prime.retain(*j, new_attrs.clone());
|
||||
b_prime.retain(*j, new_attrs.clone());
|
||||
maybe_op1 = Some(OperationBuilder::retain(*i - *j).build());
|
||||
maybe_op2 = ops2.next();
|
||||
},
|
||||
@ -396,7 +427,7 @@ impl Delta {
|
||||
for op in &self.ops {
|
||||
match &op.ty {
|
||||
OpType::Retain(retain) => {
|
||||
inverse.retain(*retain);
|
||||
inverse.retain(*retain, op.attrs.clone());
|
||||
for _ in 0..*retain {
|
||||
chars.next();
|
||||
}
|
||||
@ -405,7 +436,10 @@ impl Delta {
|
||||
inverse.delete(num_chars(insert.as_bytes()) as u64);
|
||||
},
|
||||
OpType::Delete(delete) => {
|
||||
inverse.insert(&chars.take(*delete as usize).collect::<String>());
|
||||
inverse.insert(
|
||||
&chars.take(*delete as usize).collect::<String>(),
|
||||
op.attrs.clone(),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -441,3 +475,10 @@ impl Delta {
|
||||
#[inline]
|
||||
pub fn ops(&self) -> &[Operation] { &self.ops }
|
||||
}
|
||||
|
||||
pub fn operation_attrs(operation: &Option<Operation>) -> Option<Attributes> {
|
||||
match operation {
|
||||
None => None,
|
||||
Some(operation) => operation.attrs.clone(),
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
mod attributes;
|
||||
pub mod attributes;
|
||||
pub mod delta;
|
||||
pub mod errors;
|
||||
pub mod operation;
|
||||
|
@ -8,7 +8,7 @@ use std::{
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Operation {
|
||||
pub ty: OpType,
|
||||
pub attrs: Attributes,
|
||||
pub attrs: Option<Attributes>,
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
@ -16,7 +16,14 @@ impl Operation {
|
||||
|
||||
pub fn retain(&mut self, n: u64) { self.ty.retain(n); }
|
||||
|
||||
pub fn is_plain(&self) -> bool { self.attrs.is_empty() }
|
||||
pub fn set_attrs(&mut self, attrs: Option<Attributes>) { self.attrs = attrs; }
|
||||
|
||||
pub fn is_plain(&self) -> bool {
|
||||
match self.attrs {
|
||||
None => true,
|
||||
Some(ref attrs) => attrs.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_noop(&self) -> bool {
|
||||
match self.ty {
|
||||
@ -72,16 +79,11 @@ impl OpType {
|
||||
|
||||
pub struct OperationBuilder {
|
||||
ty: OpType,
|
||||
attrs: Attributes,
|
||||
attrs: Option<Attributes>,
|
||||
}
|
||||
|
||||
impl OperationBuilder {
|
||||
pub fn new(ty: OpType) -> OperationBuilder {
|
||||
OperationBuilder {
|
||||
ty,
|
||||
attrs: Attributes::default(),
|
||||
}
|
||||
}
|
||||
pub fn new(ty: OpType) -> OperationBuilder { OperationBuilder { ty, attrs: None } }
|
||||
|
||||
pub fn retain(n: u64) -> OperationBuilder { OperationBuilder::new(OpType::Retain(n)) }
|
||||
|
||||
@ -89,7 +91,7 @@ impl OperationBuilder {
|
||||
|
||||
pub fn insert(s: String) -> OperationBuilder { OperationBuilder::new(OpType::Insert(s)) }
|
||||
|
||||
pub fn with_attrs(mut self, attrs: Attributes) -> OperationBuilder {
|
||||
pub fn with_attrs(mut self, attrs: Option<Attributes>) -> OperationBuilder {
|
||||
self.attrs = attrs;
|
||||
self
|
||||
}
|
||||
|
@ -15,9 +15,9 @@ impl Rng {
|
||||
}
|
||||
|
||||
pub fn gen_delta(&mut self, s: &str) -> Delta {
|
||||
let mut op = Delta::default();
|
||||
let mut delta = Delta::default();
|
||||
loop {
|
||||
let left = s.chars().count() - op.base_len();
|
||||
let left = s.chars().count() - delta.base_len();
|
||||
if left == 0 {
|
||||
break;
|
||||
}
|
||||
@ -28,19 +28,19 @@ impl Rng {
|
||||
};
|
||||
match self.0.gen_range(0.0, 1.0) {
|
||||
f if f < 0.2 => {
|
||||
op.insert(&self.gen_string(i));
|
||||
delta.insert(&self.gen_string(i), None);
|
||||
},
|
||||
f if f < 0.4 => {
|
||||
op.delete(i as u64);
|
||||
delta.delete(i as u64);
|
||||
},
|
||||
_ => {
|
||||
op.retain(i as u64);
|
||||
delta.retain(i as u64, None);
|
||||
},
|
||||
}
|
||||
}
|
||||
if self.0.gen_range(0.0, 1.0) < 0.3 {
|
||||
op.insert(&("1".to_owned() + &self.gen_string(10)));
|
||||
delta.insert(&("1".to_owned() + &self.gen_string(10)), None);
|
||||
}
|
||||
op
|
||||
delta
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ mod helper;
|
||||
use crate::helper::Rng;
|
||||
use bytecount::num_chars;
|
||||
use flowy_ot::{
|
||||
attributes::*,
|
||||
delta::Delta,
|
||||
operation::{OpType, OperationBuilder},
|
||||
};
|
||||
@ -12,13 +13,13 @@ fn lengths() {
|
||||
let mut delta = Delta::default();
|
||||
assert_eq!(delta.base_len, 0);
|
||||
assert_eq!(delta.target_len, 0);
|
||||
delta.retain(5);
|
||||
delta.retain(5, None);
|
||||
assert_eq!(delta.base_len, 5);
|
||||
assert_eq!(delta.target_len, 5);
|
||||
delta.insert("abc");
|
||||
delta.insert("abc", None);
|
||||
assert_eq!(delta.base_len, 5);
|
||||
assert_eq!(delta.target_len, 8);
|
||||
delta.retain(2);
|
||||
delta.retain(2, None);
|
||||
assert_eq!(delta.base_len, 7);
|
||||
assert_eq!(delta.target_len, 10);
|
||||
delta.delete(2);
|
||||
@ -28,17 +29,17 @@ fn lengths() {
|
||||
#[test]
|
||||
fn sequence() {
|
||||
let mut delta = Delta::default();
|
||||
delta.retain(5);
|
||||
delta.retain(0);
|
||||
delta.insert("lorem");
|
||||
delta.insert("");
|
||||
delta.retain(5, None);
|
||||
delta.retain(0, None);
|
||||
delta.insert("lorem", None);
|
||||
delta.insert("", None);
|
||||
delta.delete(3);
|
||||
delta.delete(0);
|
||||
assert_eq!(delta.ops.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply() {
|
||||
fn apply_1000() {
|
||||
for _ in 0..1000 {
|
||||
let mut rng = Rng::default();
|
||||
let s = rng.gen_string(50);
|
||||
@ -47,6 +48,22 @@ fn apply() {
|
||||
assert_eq!(delta.apply(&s).unwrap().chars().count(), delta.target_len);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply() {
|
||||
let s = "hello world,".to_owned();
|
||||
let mut delta_a = Delta::default();
|
||||
delta_a.insert(&s, None);
|
||||
|
||||
let mut delta_b = Delta::default();
|
||||
delta_b.retain(s.len() as u64, None);
|
||||
delta_b.insert("appflowy", None);
|
||||
|
||||
let after_a = delta_a.apply("").unwrap();
|
||||
let after_b = delta_b.apply(&after_a).unwrap();
|
||||
assert_eq!("hello world,appflowy", &after_b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invert() {
|
||||
for _ in 0..1000 {
|
||||
@ -62,8 +79,8 @@ fn invert() {
|
||||
#[test]
|
||||
fn empty_ops() {
|
||||
let mut delta = Delta::default();
|
||||
delta.retain(0);
|
||||
delta.insert("");
|
||||
delta.retain(0, None);
|
||||
delta.insert("", None);
|
||||
delta.delete(0);
|
||||
assert_eq!(delta.ops.len(), 0);
|
||||
}
|
||||
@ -71,36 +88,36 @@ fn empty_ops() {
|
||||
fn eq() {
|
||||
let mut delta_a = Delta::default();
|
||||
delta_a.delete(1);
|
||||
delta_a.insert("lo");
|
||||
delta_a.retain(2);
|
||||
delta_a.retain(3);
|
||||
delta_a.insert("lo", None);
|
||||
delta_a.retain(2, None);
|
||||
delta_a.retain(3, None);
|
||||
let mut delta_b = Delta::default();
|
||||
delta_b.delete(1);
|
||||
delta_b.insert("l");
|
||||
delta_b.insert("o");
|
||||
delta_b.retain(5);
|
||||
delta_b.insert("l", None);
|
||||
delta_b.insert("o", None);
|
||||
delta_b.retain(5, None);
|
||||
assert_eq!(delta_a, delta_b);
|
||||
delta_a.delete(1);
|
||||
delta_b.retain(1);
|
||||
delta_b.retain(1, None);
|
||||
assert_ne!(delta_a, delta_b);
|
||||
}
|
||||
#[test]
|
||||
fn ops_merging() {
|
||||
let mut delta = Delta::default();
|
||||
assert_eq!(delta.ops.len(), 0);
|
||||
delta.retain(2);
|
||||
delta.retain(2, None);
|
||||
assert_eq!(delta.ops.len(), 1);
|
||||
assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(2).build()));
|
||||
delta.retain(3);
|
||||
delta.retain(3, None);
|
||||
assert_eq!(delta.ops.len(), 1);
|
||||
assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(5).build()));
|
||||
delta.insert("abc");
|
||||
delta.insert("abc", None);
|
||||
assert_eq!(delta.ops.len(), 2);
|
||||
assert_eq!(
|
||||
delta.ops.last(),
|
||||
Some(&OperationBuilder::insert("abc".to_owned()).build())
|
||||
);
|
||||
delta.insert("xyz");
|
||||
delta.insert("xyz", None);
|
||||
assert_eq!(delta.ops.len(), 2);
|
||||
assert_eq!(
|
||||
delta.ops.last(),
|
||||
@ -117,11 +134,11 @@ fn ops_merging() {
|
||||
fn is_noop() {
|
||||
let mut delta = Delta::default();
|
||||
assert!(delta.is_noop());
|
||||
delta.retain(5);
|
||||
delta.retain(5, None);
|
||||
assert!(delta.is_noop());
|
||||
delta.retain(3);
|
||||
delta.retain(3, None);
|
||||
assert!(delta.is_noop());
|
||||
delta.insert("lorem");
|
||||
delta.insert("lorem", None);
|
||||
assert!(!delta.is_noop());
|
||||
}
|
||||
#[test]
|
||||
|
Loading…
Reference in New Issue
Block a user