From efa78dd45655ec6fed6c960b6753221d686356fa Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 8 Aug 2021 22:29:16 +0800 Subject: [PATCH] fix redo attribut bugs --- rust-lib/flowy-ot/Cargo.toml | 1 + rust-lib/flowy-ot/src/client/document.rs | 13 +- .../src/core/attributes/attributes.rs | 9 -- rust-lib/flowy-ot/src/core/attributes/data.rs | 5 +- rust-lib/flowy-ot/src/core/delta.rs | 110 ++++++++++------- .../flowy-ot/src/core/operation/builder.rs | 16 +-- .../flowy-ot/src/core/operation/operation.rs | 68 +++++----- rust-lib/flowy-ot/tests/helper/mod.rs | 3 +- rust-lib/flowy-ot/tests/invert_test.rs | 116 +++++++++++++----- rust-lib/flowy-ot/tests/op_test.rs | 12 +- rust-lib/flowy-ot/tests/serde_test.rs | 4 +- rust-lib/flowy-ot/tests/undo_redo_test.rs | 17 +++ 12 files changed, 235 insertions(+), 139 deletions(-) diff --git a/rust-lib/flowy-ot/Cargo.toml b/rust-lib/flowy-ot/Cargo.toml index c637c98b4b..9e2ff6a547 100644 --- a/rust-lib/flowy-ot/Cargo.toml +++ b/rust-lib/flowy-ot/Cargo.toml @@ -11,6 +11,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = {version = "1.0"} derive_more = {version = "0.99", features = ["display"]} log = "0.4" +color-eyre = { version = "0.5", default-features = false } [dev-dependencies] criterion = "0.3" diff --git a/rust-lib/flowy-ot/src/client/document.rs b/rust-lib/flowy-ot/src/client/document.rs index 56101b8e24..03bcca7d1e 100644 --- a/rust-lib/flowy-ot/src/client/document.rs +++ b/rust-lib/flowy-ot/src/client/document.rs @@ -5,9 +5,9 @@ use crate::{ Attributes, AttributesDataRule, AttrsBuilder, + Builder, Delta, Interval, - OpBuilder, Operation, }, errors::{ErrorBuilder, OTError, OTErrorCode::*}, @@ -42,7 +42,7 @@ impl Document { if attributes == Attributes::Empty { attributes = Attributes::Follow; } - let insert = OpBuilder::insert(text).attributes(attributes).build(); + let insert = Builder::insert(text).attributes(attributes).build(); let interval = Interval::new(index, index); self.update_with_op(insert, interval) @@ -70,8 +70,10 @@ impl Document { match self.history.undo() { None => Err(ErrorBuilder::new(UndoFail).build()), Some(undo_delta) => { + log::debug!("undo: {:?}", undo_delta); let composed_delta = self.data.compose(&undo_delta)?; let redo_delta = undo_delta.invert(&self.data); + log::debug!("computed redo: {:?}", redo_delta); let result = UndoResult::success(composed_delta.target_len as usize); self.data = composed_delta; self.history.add_redo(redo_delta); @@ -85,10 +87,13 @@ impl Document { match self.history.redo() { None => Err(ErrorBuilder::new(RedoFail).build()), Some(redo_delta) => { + log::debug!("redo: {:?}", redo_delta); let new_delta = self.data.compose(&redo_delta)?; let result = UndoResult::success(new_delta.target_len as usize); let undo_delta = redo_delta.invert(&self.data); + log::debug!("computed undo: {:?}", undo_delta); self.data = new_delta; + self.history.add_undo(undo_delta); Ok(result) }, @@ -96,7 +101,7 @@ impl Document { } pub fn delete(&mut self, interval: Interval) -> Result<(), OTError> { - let delete = OpBuilder::delete(interval.size()).build(); + let delete = Builder::delete(interval.size()).build(); self.update_with_op(delete, interval) } @@ -168,7 +173,7 @@ impl Document { }; log::debug!("new attributes: {:?}", new_attributes); - let retain = OpBuilder::retain(interval.size()) + let retain = Builder::retain(interval.size()) .attributes(new_attributes) .build(); diff --git a/rust-lib/flowy-ot/src/core/attributes/attributes.rs b/rust-lib/flowy-ot/src/core/attributes/attributes.rs index 36f1fe7b10..d3039daabd 100644 --- a/rust-lib/flowy-ot/src/core/attributes/attributes.rs +++ b/rust-lib/flowy-ot/src/core/attributes/attributes.rs @@ -19,14 +19,6 @@ impl Attributes { 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 { @@ -95,7 +87,6 @@ pub fn transform_operation(left: &Option, right: &Option) } pub fn invert_attributes(attr: Attributes, base: Attributes) -> Attributes { - log::info!("Invert attributes: {:?} : {:?}", attr, base); let attr = attr.data(); let base = base.data(); diff --git a/rust-lib/flowy-ot/src/core/attributes/data.rs b/rust-lib/flowy-ot/src/core/attributes/data.rs index 045c67cde1..6cc1d16f59 100644 --- a/rust-lib/flowy-ot/src/core/attributes/data.rs +++ b/rust-lib/flowy-ot/src/core/attributes/data.rs @@ -17,7 +17,8 @@ impl AttributesData { inner: HashMap::new(), } } - pub fn is_empty(&self) -> bool { + + pub fn is_plain(&self) -> bool { self.inner.values().filter(|v| !should_remove(v)).count() == 0 } @@ -59,7 +60,7 @@ impl AttributesDataRule for AttributesData { fn into_attributes(mut self) -> Attributes { self.apply_rule(); - if self.is_empty() { + if self.is_plain() { Attributes::Empty } else { Attributes::Custom(self) diff --git a/rust-lib/flowy-ot/src/core/delta.rs b/rust-lib/flowy-ot/src/core/delta.rs index 626a21f314..f8ad8c7385 100644 --- a/rust-lib/flowy-ot/src/core/delta.rs +++ b/rust-lib/flowy-ot/src/core/delta.rs @@ -89,7 +89,7 @@ impl Delta { if let Some(Operation::Delete(n_last)) = self.ops.last_mut() { *n_last += n; } else { - self.ops.push(OpBuilder::delete(n).build()); + self.ops.push(Builder::delete(n).build()); } } @@ -110,10 +110,10 @@ impl Delta { }, [.., op_last @ Operation::Delete(_)] => { let new_last = op_last.clone(); - *op_last = OpBuilder::insert(s).attributes(attrs).build(); + *op_last = Builder::insert(s).attributes(attrs).build(); Some(new_last) }, - _ => Some(OpBuilder::insert(s).attributes(attrs).build()), + _ => Some(Builder::insert(s).attributes(attrs).build()), }; match new_last { @@ -134,8 +134,7 @@ impl Delta { self.ops.push(new_op); } } else { - self.ops - .push(OpBuilder::retain(n).attributes(attrs).build()); + self.ops.push(Builder::retain(n).attributes(attrs).build()); } } @@ -185,7 +184,7 @@ impl Delta { match retain.cmp(&o_retain) { Ordering::Less => { new_delta.retain(retain.n, composed_attrs); - next_op2 = Some(OpBuilder::retain(o_retain.n - retain.n).build()); + next_op2 = Some(Builder::retain(o_retain.n - retain.n).build()); next_op1 = ops1.next(); }, std::cmp::Ordering::Equal => { @@ -195,7 +194,7 @@ impl Delta { }, std::cmp::Ordering::Greater => { new_delta.retain(o_retain.n, composed_attrs); - next_op1 = Some(OpBuilder::retain(retain.n - o_retain.n).build()); + next_op1 = Some(Builder::retain(retain.n - o_retain.n).build()); next_op2 = ops2.next(); }, } @@ -204,7 +203,7 @@ impl Delta { match (num_chars(insert.as_bytes()) as usize).cmp(o_num) { Ordering::Less => { next_op2 = Some( - OpBuilder::delete(*o_num - num_chars(insert.as_bytes()) as usize) + Builder::delete(*o_num - num_chars(insert.as_bytes()) as usize) .attributes(insert.attributes.clone()) .build(), ); @@ -216,7 +215,7 @@ impl Delta { }, Ordering::Greater => { next_op1 = Some( - OpBuilder::insert( + Builder::insert( &insert.chars().skip(*o_num as usize).collect::(), ) .build(), @@ -237,7 +236,7 @@ impl Delta { Ordering::Less => { new_delta.insert(&insert.s, composed_attrs.clone()); next_op2 = Some( - OpBuilder::retain(o_retain.n - insert.num_chars()) + Builder::retain(o_retain.n - insert.num_chars()) .attributes(o_retain.attributes.clone()) .build(), ); @@ -255,7 +254,7 @@ impl Delta { composed_attrs, ); next_op1 = Some( - OpBuilder::insert(&chars.collect::()) + Builder::insert(&chars.collect::()) // consider this situation: // [insert:12345678 - retain:4], // the attributes of "5678" should be empty and the following @@ -271,7 +270,7 @@ impl Delta { match retain.cmp(&o_num) { Ordering::Less => { new_delta.delete(retain.n); - next_op2 = Some(OpBuilder::delete(*o_num - retain.n).build()); + next_op2 = Some(Builder::delete(*o_num - retain.n).build()); next_op1 = ops1.next(); }, Ordering::Equal => { @@ -281,7 +280,7 @@ impl Delta { }, Ordering::Greater => { new_delta.delete(*o_num); - next_op1 = Some(OpBuilder::retain(retain.n - *o_num).build()); + next_op1 = Some(Builder::retain(retain.n - *o_num).build()); next_op2 = ops2.next(); }, } @@ -340,7 +339,7 @@ impl Delta { Ordering::Less => { a_prime.retain(retain.n, composed_attrs.clone()); b_prime.retain(retain.n, composed_attrs.clone()); - next_op2 = Some(OpBuilder::retain(o_retain.n - retain.n).build()); + next_op2 = Some(Builder::retain(o_retain.n - retain.n).build()); next_op1 = ops1.next(); }, Ordering::Equal => { @@ -352,14 +351,14 @@ impl Delta { Ordering::Greater => { a_prime.retain(o_retain.n, composed_attrs.clone()); b_prime.retain(o_retain.n, composed_attrs.clone()); - next_op1 = Some(OpBuilder::retain(retain.n - o_retain.n).build()); + next_op1 = Some(Builder::retain(retain.n - o_retain.n).build()); next_op2 = ops2.next(); }, }; }, (Some(Operation::Delete(i)), Some(Operation::Delete(j))) => match i.cmp(&j) { Ordering::Less => { - next_op2 = Some(OpBuilder::delete(*j - *i).build()); + next_op2 = Some(Builder::delete(*j - *i).build()); next_op1 = ops1.next(); }, Ordering::Equal => { @@ -367,7 +366,7 @@ impl Delta { next_op2 = ops2.next(); }, Ordering::Greater => { - next_op1 = Some(OpBuilder::delete(*i - *j).build()); + next_op1 = Some(Builder::delete(*i - *j).build()); next_op2 = ops2.next(); }, }, @@ -375,7 +374,7 @@ impl Delta { match i.cmp(&o_retain) { Ordering::Less => { a_prime.delete(*i); - next_op2 = Some(OpBuilder::retain(o_retain.n - *i).build()); + next_op2 = Some(Builder::retain(o_retain.n - *i).build()); next_op1 = ops1.next(); }, Ordering::Equal => { @@ -385,7 +384,7 @@ impl Delta { }, Ordering::Greater => { a_prime.delete(o_retain.n); - next_op1 = Some(OpBuilder::delete(*i - o_retain.n).build()); + next_op1 = Some(Builder::delete(*i - o_retain.n).build()); next_op2 = ops2.next(); }, }; @@ -394,7 +393,7 @@ impl Delta { match retain.cmp(&j) { Ordering::Less => { b_prime.delete(retain.n); - next_op2 = Some(OpBuilder::delete(*j - retain.n).build()); + next_op2 = Some(Builder::delete(*j - retain.n).build()); next_op1 = ops1.next(); }, Ordering::Equal => { @@ -404,7 +403,7 @@ impl Delta { }, Ordering::Greater => { b_prime.delete(*j); - next_op1 = Some(OpBuilder::retain(retain.n - *j).build()); + next_op1 = Some(Builder::retain(retain.n - *j).build()); next_op2 = ops2.next(); }, }; @@ -485,10 +484,13 @@ impl Delta { let inverted_from_other = |inverted: &mut Delta, operation: &Operation, start: usize, end: usize| { + log::debug!("invert op: {:?} [{}:{}]", operation, start, end); + let ops = other.ops_in_interval(Interval::new(start, end)); ops.into_iter().for_each(|other_op| { match operation { Operation::Delete(_) => { + log::debug!("add: {}", other_op); inverted.add(other_op); }, Operation::Retain(_) => { @@ -502,6 +504,7 @@ impl Delta { other_op.get_attributes(), ); log::debug!("End invert attributes: {:?}", inverted_attrs); + log::debug!("invert retain: {}, {}", other_op.length(), inverted_attrs); inverted.retain(other_op.length(), inverted_attrs); }, Operation::Insert(_) => { @@ -515,21 +518,25 @@ impl Delta { let mut index = 0; for op in &self.ops { let len: usize = op.length() as usize; - log::info!("{:?}", op); match op { - Operation::Delete(_) => { - inverted_from_other(&mut inverted, op, index, index + len); + Operation::Delete(n) => { + inverted_from_other(&mut inverted, op, index, index + *n); index += len; }, Operation::Retain(_) => { match op.has_attribute() { true => inverted_from_other(&mut inverted, op, index, index + len), - false => inverted.retain(len as usize, op.get_attributes()), + false => { + log::debug!("invert retain op: {:?}", op); + inverted.retain(len as usize, op.get_attributes()) + }, } index += len; }, - Operation::Insert(_) => { + Operation::Insert(insert) => { + log::debug!("invert insert op: {:?}", op); inverted.delete(len as usize); + // index += insert.s.len(); }, } } @@ -549,29 +556,42 @@ impl Delta { pub fn is_empty(&self) -> bool { self.ops.is_empty() } - pub fn ops_in_interval(&self, interval: Interval) -> Vec { - let mut ops: Vec = Vec::with_capacity(self.ops.len()); - let mut offset: usize = 0; - let mut ops_iter = self.ops.iter(); - let mut next_op = ops_iter.next(); + pub fn ops_in_interval(&self, mut interval: Interval) -> Vec { + log::debug!("ops in delta: {:?}, at {:?}", self, interval); - while offset < interval.end && next_op.is_some() { - let op = next_op.take().unwrap(); - let len = offset + op.length() as usize; - // log::info!("{:?}", op); - while offset < len { - if offset < interval.start { - offset += min(interval.start, op.length() as usize); - } else { - if interval.contains(offset) { - ops.push(op.shrink_to_interval(interval)); - offset += min(op.length() as usize, interval.size()); - } else { - break; + let mut ops: Vec = Vec::with_capacity(self.ops.len()); + let mut ops_iter = self.ops.iter(); + let mut maybe_next_op = ops_iter.next(); + let mut offset: usize = 0; + + while maybe_next_op.is_some() { + let next_op = maybe_next_op.take().unwrap(); + if offset < interval.start { + if next_op.length() > interval.size() { + // if the op's length larger than the interval size, just shrink the op to that + // interval. for example: delta: "123456", interval: [3,6). + // ┌──────────────┐ + // │ 1 2 3 4 5 6 │ + // └───────▲───▲──┘ + // │ │ + // [3, 6) + if let Some(new_op) = next_op.shrink(interval) { + ops.push(new_op); } + } else { + // adding the op's length to offset until the offset is contained in the + // interval + offset += next_op.length(); } + } else { + // the interval passed in the shrink function is base on the op not the delta. + if let Some(new_op) = next_op.shrink(interval.translate_neg(offset)) { + ops.push(new_op); + } + offset += min(interval.size(), next_op.length()); + interval = Interval::new(offset, interval.end); } - next_op = ops_iter.next(); + maybe_next_op = ops_iter.next(); } ops } diff --git a/rust-lib/flowy-ot/src/core/operation/builder.rs b/rust-lib/flowy-ot/src/core/operation/builder.rs index e3be98a96e..993cf213fc 100644 --- a/rust-lib/flowy-ot/src/core/operation/builder.rs +++ b/rust-lib/flowy-ot/src/core/operation/builder.rs @@ -1,25 +1,25 @@ use crate::core::{Attributes, Operation}; -pub struct OpBuilder { +pub struct Builder { ty: Operation, attrs: Attributes, } -impl OpBuilder { - pub fn new(ty: Operation) -> OpBuilder { - OpBuilder { +impl Builder { + pub fn new(ty: Operation) -> Builder { + Builder { ty, attrs: Attributes::Empty, } } - pub fn retain(n: usize) -> OpBuilder { OpBuilder::new(Operation::Retain(n.into())) } + pub fn retain(n: usize) -> Builder { Builder::new(Operation::Retain(n.into())) } - pub fn delete(n: usize) -> OpBuilder { OpBuilder::new(Operation::Delete(n)) } + pub fn delete(n: usize) -> Builder { Builder::new(Operation::Delete(n)) } - pub fn insert(s: &str) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) } + pub fn insert(s: &str) -> Builder { Builder::new(Operation::Insert(s.into())) } - pub fn attributes(mut self, attrs: Attributes) -> OpBuilder { + pub fn attributes(mut self, attrs: Attributes) -> Builder { self.attrs = attrs; self } diff --git a/rust-lib/flowy-ot/src/core/operation/operation.rs b/rust-lib/flowy-ot/src/core/operation/operation.rs index 37ca56e196..05f92d9800 100644 --- a/rust-lib/flowy-ot/src/core/operation/operation.rs +++ b/rust-lib/flowy-ot/src/core/operation/operation.rs @@ -1,4 +1,4 @@ -use crate::core::{Attributes, Interval, OpBuilder}; +use crate::core::{Attributes, Builder, Interval}; use bytecount::num_chars; use std::{ cmp::min, @@ -54,11 +54,20 @@ impl Operation { pub fn has_attribute(&self) -> bool { match self.get_attributes() { Attributes::Follow => false, - Attributes::Custom(data) => !data.is_empty(), + // Attributes::Custom(data) => !data.is_plain(), + Attributes::Custom(data) => true, Attributes::Empty => false, } } + pub fn is_plain(&self) -> bool { + match self.get_attributes() { + Attributes::Follow => true, + Attributes::Custom(data) => data.is_plain(), + Attributes::Empty => true, + } + } + pub fn length(&self) -> usize { match self { Operation::Delete(n) => *n, @@ -69,30 +78,27 @@ impl Operation { pub fn is_empty(&self) -> bool { self.length() == 0 } - pub fn shrink_to_interval(&self, interval: Interval) -> Operation { - match self { - Operation::Delete(n) => { - // - OpBuilder::delete(min(*n, interval.size())).build() - }, - Operation::Retain(retain) => { - // - OpBuilder::retain(min(retain.n, interval.size())) - .attributes(retain.attributes.clone()) - .build() - }, + pub fn shrink(&self, interval: Interval) -> Option { + let op = match self { + Operation::Delete(n) => Builder::delete(min(*n, interval.size())).build(), + Operation::Retain(retain) => Builder::retain(min(retain.n, interval.size())) + .attributes(retain.attributes.clone()) + .build(), Operation::Insert(insert) => { - // debug_assert!(insert.s.len() <= interval.size()); if interval.start > insert.s.len() { - return OpBuilder::insert("").build(); + Builder::insert("").build() + } else { + let s = &insert.s[interval.start..min(interval.end, insert.s.len())]; + Builder::insert(s) + .attributes(insert.attributes.clone()) + .build() } - - let end = min(interval.end, insert.s.len()); - let s = &insert.s[interval.start..end]; - OpBuilder::insert(s) - .attributes(insert.attributes.clone()) - .build() }, + }; + + match op.is_empty() { + true => None, + false => Some(op), } } } @@ -130,11 +136,7 @@ pub struct Retain { impl Retain { pub fn merge_or_new_op(&mut self, n: usize, attributes: Attributes) -> Option { - log::debug!( - "merge_retain_or_new_op: {:?}, {:?}", - self.attributes, - attributes - ); + log::debug!("merge_retain_or_new_op: {:?}, {:?}", n, attributes); match &attributes { Attributes::Follow => { @@ -149,7 +151,7 @@ impl Retain { None } else { log::debug!("New retain op"); - Some(OpBuilder::retain(n).attributes(attributes).build()) + Some(Builder::retain(n).attributes(attributes).build()) } }, } @@ -202,7 +204,7 @@ impl Insert { self.s += s; None } else { - Some(OpBuilder::insert(s).attributes(attributes).build()) + Some(Builder::insert(s).attributes(attributes).build()) } }, } @@ -222,4 +224,10 @@ impl std::convert::From<&str> for Insert { fn from(s: &str) -> Self { Insert::from(s.to_owned()) } } -fn is_empty(attributes: &Attributes) -> bool { attributes.is_empty() } +fn is_empty(attributes: &Attributes) -> bool { + match attributes { + Attributes::Follow => true, + Attributes::Custom(data) => data.is_plain(), + Attributes::Empty => true, + } +} diff --git a/rust-lib/flowy-ot/tests/helper/mod.rs b/rust-lib/flowy-ot/tests/helper/mod.rs index c95c034635..235620c8cd 100644 --- a/rust-lib/flowy-ot/tests/helper/mod.rs +++ b/rust-lib/flowy-ot/tests/helper/mod.rs @@ -50,7 +50,8 @@ impl OpTester { pub fn new() -> Self { static INIT: Once = Once::new(); INIT.call_once(|| { - std::env::set_var("RUST_LOG", "debug"); + color_eyre::install().unwrap(); + std::env::set_var("RUST_LOG", "info"); env_logger::init(); }); diff --git a/rust-lib/flowy-ot/tests/invert_test.rs b/rust-lib/flowy-ot/tests/invert_test.rs index 5b44ef7546..e6b84d3aac 100644 --- a/rust-lib/flowy-ot/tests/invert_test.rs +++ b/rust-lib/flowy-ot/tests/invert_test.rs @@ -1,15 +1,15 @@ pub mod helper; use crate::helper::{TestOp::*, *}; -use flowy_ot::core::{Delta, Interval, OpBuilder}; +use flowy_ot::core::{Builder, Delta, Interval}; #[test] fn delta_invert_no_attribute_delta() { let mut delta = Delta::default(); - delta.add(OpBuilder::insert("123").build()); + delta.add(Builder::insert("123").build()); let mut change = Delta::default(); - change.add(OpBuilder::retain(3).build()); - change.add(OpBuilder::insert("456").build()); + change.add(Builder::retain(3).build()); + change.add(Builder::insert("456").build()); let undo = change.invert(&delta); let new_delta = delta.compose(&change).unwrap(); @@ -48,21 +48,30 @@ fn delta_invert_attribute_delta_with_no_attribute_delta2() { Insert(0, "123", 0), Bold(0, Interval::new(0, 3), true), Insert(0, "456", 3), - AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#), + AssertOpsJson( + 0, + r#"[ + {"insert":"123456","attributes":{"bold":"true"}}] + "#, + ), Italic(0, Interval::new(2, 4), true), AssertOpsJson( 0, - r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34","attributes": -{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true" -}}]"#, + r#"[ + {"insert":"12","attributes":{"bold":"true"}}, + {"insert":"34","attributes":{"bold":"true","italic":"true"}}, + {"insert":"56","attributes":{"bold":"true"}} + ]"#, ), Insert(1, "abc", 0), Invert(0, 1), AssertOpsJson( 0, - r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34","attributes": -{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true" -}}]"#, + r#"[ + {"insert":"12","attributes":{"bold":"true"}}, + {"insert":"34","attributes":{"bold":"true","italic":"true"}}, + {"insert":"56","attributes":{"bold":"true"}} + ]"#, ), ]; OpTester::new().run_script(ops); @@ -115,9 +124,11 @@ fn delta_invert_attribute_delta_with_attribute_delta() { Italic(0, Interval::new(2, 4), true), AssertOpsJson( 0, - r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34","attributes": -{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true" -}}]"#, + r#"[ + {"insert":"12","attributes":{"bold":"true"}}, + {"insert":"34","attributes":{"bold":"true","italic":"true"}}, + {"insert":"56","attributes":{"bold":"true"}} + ]"#, ), Insert(1, "abc", 0), Bold(1, Interval::new(0, 3), true), @@ -125,16 +136,20 @@ fn delta_invert_attribute_delta_with_attribute_delta() { Italic(1, Interval::new(1, 3), true), AssertOpsJson( 1, - r#"[{"insert":"a","attributes":{"bold":"true"}},{"insert":"bc","attributes": -{"bold":"true","italic":"true"}},{"insert":"d","attributes":{"bold":"true" -}}]"#, + r#"[ + {"insert":"a","attributes":{"bold":"true"}}, + {"insert":"bc","attributes":{"bold":"true","italic":"true"}}, + {"insert":"d","attributes":{"bold":"true"}} + ]"#, ), Invert(0, 1), AssertOpsJson( 0, - r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34","attributes": -{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true" -}}]"#, + r#"[ + {"insert":"12","attributes":{"bold":"true"}}, + {"insert":"34","attributes":{"bold":"true","italic":"true"}}, + {"insert":"56","attributes":{"bold":"true"}} + ]"#, ), ]; OpTester::new().run_script(ops); @@ -143,8 +158,8 @@ fn delta_invert_attribute_delta_with_attribute_delta() { #[test] fn delta_get_ops_in_interval_1() { let mut delta = Delta::default(); - let insert_a = OpBuilder::insert("123").build(); - let insert_b = OpBuilder::insert("4").build(); + let insert_a = Builder::insert("123").build(); + let insert_b = Builder::insert("4").build(); delta.add(insert_a.clone()); delta.add(insert_b.clone()); @@ -158,16 +173,21 @@ fn delta_get_ops_in_interval_1() { #[test] fn delta_get_ops_in_interval_2() { let mut delta = Delta::default(); - let insert_a = OpBuilder::insert("123").build(); - let insert_b = OpBuilder::insert("4").build(); - let insert_c = OpBuilder::insert("5").build(); - let retain_a = OpBuilder::retain(3).build(); + let insert_a = Builder::insert("123").build(); + let insert_b = Builder::insert("4").build(); + let insert_c = Builder::insert("5").build(); + let retain_a = Builder::retain(3).build(); delta.add(insert_a.clone()); delta.add(retain_a.clone()); delta.add(insert_b.clone()); delta.add(insert_c.clone()); + assert_eq!( + delta.ops_in_interval(Interval::new(0, 2)), + vec![Builder::insert("12").build()] + ); + assert_eq!( delta.ops_in_interval(Interval::new(0, 3)), vec![insert_a.clone()] @@ -175,16 +195,48 @@ fn delta_get_ops_in_interval_2() { assert_eq!( delta.ops_in_interval(Interval::new(0, 4)), + vec![insert_a.clone(), Builder::retain(1).build()] + ); + + assert_eq!( + delta.ops_in_interval(Interval::new(0, 6)), vec![insert_a.clone(), retain_a.clone()] ); assert_eq!( delta.ops_in_interval(Interval::new(0, 7)), - vec![ - insert_a.clone(), - retain_a.clone(), - // insert_b and insert_c will be merged into one. insert: "45" - delta.ops.last().unwrap().clone() - ] + vec![insert_a.clone(), retain_a.clone(), insert_b.clone()] + ); +} + +#[test] +fn delta_get_ops_in_interval_3() { + let mut delta = Delta::default(); + let insert_a = Builder::insert("123456").build(); + delta.add(insert_a.clone()); + assert_eq!( + delta.ops_in_interval(Interval::new(3, 6)), + vec![Builder::insert("456").build()] + ); +} + +#[test] +fn delta_get_ops_in_interval_4() { + let mut delta = Delta::default(); + let insert_a = Builder::insert("12").build(); + let insert_b = Builder::insert("34").build(); + let insert_c = Builder::insert("56").build(); + + delta.ops.push(insert_a.clone()); + delta.ops.push(insert_b.clone()); + delta.ops.push(insert_c.clone()); + + assert_eq!(delta.ops_in_interval(Interval::new(0, 2)), vec![insert_a]); + assert_eq!(delta.ops_in_interval(Interval::new(2, 4)), vec![insert_b]); + assert_eq!(delta.ops_in_interval(Interval::new(4, 6)), vec![insert_c]); + + assert_eq!( + delta.ops_in_interval(Interval::new(2, 5)), + vec![Builder::insert("34").build(), Builder::insert("5").build()] ); } diff --git a/rust-lib/flowy-ot/tests/op_test.rs b/rust-lib/flowy-ot/tests/op_test.rs index 9efe1afb85..d7cc1c0286 100644 --- a/rust-lib/flowy-ot/tests/op_test.rs +++ b/rust-lib/flowy-ot/tests/op_test.rs @@ -120,22 +120,22 @@ fn ops_merging() { assert_eq!(delta.ops.len(), 0); delta.retain(2, Attributes::Empty); assert_eq!(delta.ops.len(), 1); - assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(2).build())); + assert_eq!(delta.ops.last(), Some(&Builder::retain(2).build())); delta.retain(3, Attributes::Empty); assert_eq!(delta.ops.len(), 1); - assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(5).build())); + assert_eq!(delta.ops.last(), Some(&Builder::retain(5).build())); delta.insert("abc", Attributes::Empty); assert_eq!(delta.ops.len(), 2); - assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abc").build())); + assert_eq!(delta.ops.last(), Some(&Builder::insert("abc").build())); delta.insert("xyz", Attributes::Empty); assert_eq!(delta.ops.len(), 2); - assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abcxyz").build())); + assert_eq!(delta.ops.last(), Some(&Builder::insert("abcxyz").build())); delta.delete(1); assert_eq!(delta.ops.len(), 3); - assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(1).build())); + assert_eq!(delta.ops.last(), Some(&Builder::delete(1).build())); delta.delete(1); assert_eq!(delta.ops.len(), 3); - assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(2).build())); + assert_eq!(delta.ops.last(), Some(&Builder::delete(2).build())); } #[test] fn is_noop() { diff --git a/rust-lib/flowy-ot/tests/serde_test.rs b/rust-lib/flowy-ot/tests/serde_test.rs index 2d821f4ead..a4d470706c 100644 --- a/rust-lib/flowy-ot/tests/serde_test.rs +++ b/rust-lib/flowy-ot/tests/serde_test.rs @@ -3,7 +3,7 @@ use flowy_ot::core::*; #[test] fn operation_insert_serialize_test() { let attributes = AttrsBuilder::new().bold(true).italic(true).build(); - let operation = OpBuilder::insert("123").attributes(attributes).build(); + let operation = Builder::insert("123").attributes(attributes).build(); let json = serde_json::to_string(&operation).unwrap(); eprintln!("{}", json); @@ -33,7 +33,7 @@ fn delta_serialize_test() { let mut delta = Delta::default(); let attributes = AttrsBuilder::new().bold(true).italic(true).build(); - let retain = OpBuilder::insert("123").attributes(attributes).build(); + let retain = Builder::insert("123").attributes(attributes).build(); delta.add(retain); delta.add(Operation::Retain(5.into())); diff --git a/rust-lib/flowy-ot/tests/undo_redo_test.rs b/rust-lib/flowy-ot/tests/undo_redo_test.rs index 0fdc433b04..3a5954481f 100644 --- a/rust-lib/flowy-ot/tests/undo_redo_test.rs +++ b/rust-lib/flowy-ot/tests/undo_redo_test.rs @@ -71,3 +71,20 @@ fn delta_undo_attributes() { ]; OpTester::new().run_script(ops); } + +#[test] +fn delta_redo_attributes() { + let ops = vec![ + Insert(0, "\n", 0), + Insert(0, "123", 0), + Bold(0, Interval::new(0, 3), true), + Undo(0), + AssertOpsJson(0, r#"[{"insert":"123\n"}]"#), + Redo(0), + AssertOpsJson( + 0, + r#" [{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n"}]"#, + ), + ]; + OpTester::new().run_script(ops); +}