From 15c3a821ec2da60e4b7460b8075f4dcd71139b7a Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 11 Aug 2021 08:40:58 +0800 Subject: [PATCH] add extensions --- .../lib/src/model/document/history.dart | 3 - .../lib/src/model/heuristic/rule.dart | 30 +-- rust-lib/flowy-ot/Cargo.toml | 1 + rust-lib/flowy-ot/src/client/document.rs | 32 +-- rust-lib/flowy-ot/src/client/mod.rs | 1 + .../flowy-ot/src/client/view/extension.rs | 23 +++ .../flowy-ot/src/client/view/insert_ext.rs | 14 ++ rust-lib/flowy-ot/src/client/view/mod.rs | 6 + rust-lib/flowy-ot/src/core/delta/cursor.rs | 98 +++++++++ .../flowy-ot/src/core/{ => delta}/delta.rs | 57 +---- rust-lib/flowy-ot/src/core/delta/mod.rs | 5 + rust-lib/flowy-ot/tests/helper/mod.rs | 4 +- rust-lib/flowy-ot/tests/invert_test.rs | 156 -------------- rust-lib/flowy-ot/tests/op_test.rs | 194 ++++++++++++++++-- 14 files changed, 360 insertions(+), 264 deletions(-) create mode 100644 rust-lib/flowy-ot/src/client/view/extension.rs create mode 100644 rust-lib/flowy-ot/src/client/view/insert_ext.rs create mode 100644 rust-lib/flowy-ot/src/client/view/mod.rs create mode 100644 rust-lib/flowy-ot/src/core/delta/cursor.rs rename rust-lib/flowy-ot/src/core/{ => delta}/delta.rs (89%) create mode 100644 rust-lib/flowy-ot/src/core/delta/mod.rs delete mode 100644 rust-lib/flowy-ot/tests/invert_test.rs diff --git a/app_flowy/packages/flowy_editor/lib/src/model/document/history.dart b/app_flowy/packages/flowy_editor/lib/src/model/document/history.dart index 35fef76752..1957c35d36 100644 --- a/app_flowy/packages/flowy_editor/lib/src/model/document/history.dart +++ b/app_flowy/packages/flowy_editor/lib/src/model/document/history.dart @@ -53,10 +53,7 @@ class History { if (stack.undo.isNotEmpty) { final lastDelta = stack.undo.removeLast(); - print("undoDelta: $undoDelta"); - print("lastDelta: $lastDelta"); undoDelta = undoDelta.compose(lastDelta); - print("compose result: $undoDelta"); } else { lastRecorded = timestamp; } diff --git a/app_flowy/packages/flowy_editor/lib/src/model/heuristic/rule.dart b/app_flowy/packages/flowy_editor/lib/src/model/heuristic/rule.dart index 8dad3fa964..5c96b0c15c 100644 --- a/app_flowy/packages/flowy_editor/lib/src/model/heuristic/rule.dart +++ b/app_flowy/packages/flowy_editor/lib/src/model/heuristic/rule.dart @@ -40,21 +40,21 @@ class Rules { final List _rules; static final Rules _instance = Rules([ - const FormatLinkAtCaretPositionRule(), - const ResolveLineFormatRule(), - const ResolveInlineFormatRule(), - const InsertEmbedsRule(), - const ForceNewlineForInsertsAroundEmbedRule(), - const AutoExitBlockRule(), - const PreserveBlockStyleOnInsertRule(), - const PreserveLineStyleOnSplitRule(), - const ResetLineFormatOnNewLineRule(), - const AutoFormatLinksRule(), - const PreserveInlineStylesRule(), - const CatchAllInsertRule(), - const EnsureEmbedLineRule(), - const PreserveLineStyleOnMergeRule(), - const CatchAllDeleteRule(), + // const FormatLinkAtCaretPositionRule(), + // const ResolveLineFormatRule(), + // const ResolveInlineFormatRule(), + // const InsertEmbedsRule(), + // const ForceNewlineForInsertsAroundEmbedRule(), + // const AutoExitBlockRule(), + // const PreserveBlockStyleOnInsertRule(), + // const PreserveLineStyleOnSplitRule(), + // const ResetLineFormatOnNewLineRule(), + // const AutoFormatLinksRule(), + // const PreserveInlineStylesRule(), + // const CatchAllInsertRule(), + // const EnsureEmbedLineRule(), + // const PreserveLineStyleOnMergeRule(), + // const CatchAllDeleteRule(), ]); static Rules getInstance() => _instance; diff --git a/rust-lib/flowy-ot/Cargo.toml b/rust-lib/flowy-ot/Cargo.toml index 687ee975c2..87b18ecc9e 100644 --- a/rust-lib/flowy-ot/Cargo.toml +++ b/rust-lib/flowy-ot/Cargo.toml @@ -13,6 +13,7 @@ derive_more = {version = "0.99", features = ["display"]} log = "0.4" color-eyre = { version = "0.5", default-features = false } chrono = "0.4.19" +lazy_static = "1.4.0" [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 6157deacce..0d2918ef15 100644 --- a/rust-lib/flowy-ot/src/client/document.rs +++ b/rust-lib/flowy-ot/src/client/document.rs @@ -24,7 +24,7 @@ impl Document { } } - pub fn edit(&mut self, index: usize, text: &str) -> Result<(), OTError> { + pub fn insert(&mut self, index: usize, text: &str) -> Result<(), OTError> { if self.data.target_len < index { log::error!( "{} out of bounds. should 0..{}", @@ -59,6 +59,21 @@ impl Document { self.update_with_attribute(attributes, interval) } + pub fn replace(&mut self, interval: Interval, s: &str) -> Result<(), OTError> { + let mut delta = Delta::default(); + if !s.is_empty() { + let insert = Builder::insert(s).attributes(Attributes::Follow).build(); + delta.add(insert); + } + + if !interval.is_empty() { + let delete = Builder::delete(interval.size()).build(); + delta.add(delete); + } + + self.update_with_op(&delta, interval) + } + pub fn can_undo(&self) -> bool { self.history.can_undo() } pub fn can_redo(&self) -> bool { self.history.can_redo() } @@ -93,21 +108,6 @@ impl Document { } } - pub fn replace(&mut self, interval: Interval, s: &str) -> Result<(), OTError> { - let mut delta = Delta::default(); - if !s.is_empty() { - let insert = Builder::insert(s).attributes(Attributes::Follow).build(); - delta.add(insert); - } - - if !interval.is_empty() { - let delete = Builder::delete(interval.size()).build(); - delta.add(delete); - } - - self.update_with_op(&delta, interval) - } - pub fn to_json(&self) -> String { self.data.to_json() } pub fn to_string(&self) -> String { self.data.apply("").unwrap() } diff --git a/rust-lib/flowy-ot/src/client/mod.rs b/rust-lib/flowy-ot/src/client/mod.rs index 0f154e5d72..8b810e729b 100644 --- a/rust-lib/flowy-ot/src/client/mod.rs +++ b/rust-lib/flowy-ot/src/client/mod.rs @@ -1,5 +1,6 @@ mod document; mod history; +mod view; pub use document::*; pub use history::*; diff --git a/rust-lib/flowy-ot/src/client/view/extension.rs b/rust-lib/flowy-ot/src/client/view/extension.rs new file mode 100644 index 0000000000..52ee4ac521 --- /dev/null +++ b/rust-lib/flowy-ot/src/client/view/extension.rs @@ -0,0 +1,23 @@ +use crate::{ + client::{view::insert_ext::*, Document}, + core::{Attributes, Interval}, +}; +use lazy_static::lazy_static; + +pub trait InsertExt { + fn apply(document: &Document, s: &str, interval: Interval); +} + +pub trait FormatExt { + fn apply(document: &Document, interval: Interval, attributes: Attributes); +} + +pub trait DeleteExt { + fn apply(document: &Document, interval: Interval); +} + +lazy_static! { + static ref INSERT_EXT: Vec> = vec![PreserveInlineStyleExt::new(),]; + static ref FORMAT_EXT: Vec> = vec![]; + static ref DELETE_EXT: Vec> = vec![]; +} diff --git a/rust-lib/flowy-ot/src/client/view/insert_ext.rs b/rust-lib/flowy-ot/src/client/view/insert_ext.rs new file mode 100644 index 0000000000..3904e0f541 --- /dev/null +++ b/rust-lib/flowy-ot/src/client/view/insert_ext.rs @@ -0,0 +1,14 @@ +use crate::{ + client::{view::InsertExt, Document}, + core::Interval, +}; + +pub struct PreserveInlineStyleExt {} + +impl PreserveInlineStyleExt { + pub fn new() -> Self {} +} + +impl InsertExt for PreserveInlineStyleExt { + fn apply(document: &Document, s: &str, interval: Interval) { unimplemented!() } +} diff --git a/rust-lib/flowy-ot/src/client/view/mod.rs b/rust-lib/flowy-ot/src/client/view/mod.rs new file mode 100644 index 0000000000..835ea3edc4 --- /dev/null +++ b/rust-lib/flowy-ot/src/client/view/mod.rs @@ -0,0 +1,6 @@ +mod extension; +mod insert_ext; + +pub use extension::*; + +pub use insert_ext::*; diff --git a/rust-lib/flowy-ot/src/core/delta/cursor.rs b/rust-lib/flowy-ot/src/core/delta/cursor.rs new file mode 100644 index 0000000000..04abe86186 --- /dev/null +++ b/rust-lib/flowy-ot/src/core/delta/cursor.rs @@ -0,0 +1,98 @@ +use crate::core::{Delta, Interval, Operation}; +use std::{cmp::min, slice::Iter}; + +pub struct Cursor<'a> { + delta: &'a Delta, + interval: Interval, + iterator: Iter<'a, Operation>, + offset: usize, +} + +impl<'a> Cursor<'a> { + pub fn new(delta: &'a Delta, interval: Interval) -> Cursor<'a> { + let mut cursor = Self { + delta, + interval, + iterator: delta.ops.iter(), + offset: 0, + }; + cursor + } + + pub fn next_op(&mut self) -> Option { + let mut next_op = self.iterator.next(); + let mut find_op = None; + + while find_op.is_none() && next_op.is_some() { + let op = next_op.unwrap(); + if self.offset < self.interval.start { + let intersect = + Interval::new(self.offset, self.offset + op.length()).intersect(self.interval); + if intersect.is_empty() { + self.offset += op.length(); + } else { + if let Some(new_op) = op.shrink(intersect.translate_neg(self.offset)) { + // shrink the op to fit the intersect range + // ┌──────────────┐ + // │ 1 2 3 4 5 6 │ + // └───────▲───▲──┘ + // │ │ + // [3, 5) + // op = "45" + find_op = Some(new_op); + } + self.offset = intersect.end; + } + } else { + // the interval passed in the shrink function is base on the op not the delta. + if let Some(new_op) = op.shrink(self.interval.translate_neg(self.offset)) { + find_op = Some(new_op); + } + // for example: extract the ops from three insert ops with interval [2,5). the + // interval size is larger than the op. Moving the offset to extract each part. + // Each step would be the small value between interval.size() and + // next_op.length(). Checkout the delta_get_ops_in_interval_4 for more details. + // + // ┌──────┐ ┌──────┐ ┌──────┐ + // │ 1 2 │ │ 3 4 │ │ 5 6 │ + // └──────┘ └─▲────┘ └───▲──┘ + // │ [2, 5) │ + // + self.offset += min(self.interval.size(), op.length()); + } + + match find_op { + None => next_op = self.iterator.next(), + Some(_) => self.interval.start = self.offset, + } + } + + find_op + } +} + +pub struct DeltaIter<'a> { + cursor: Cursor<'a>, + interval: Interval, +} + +impl<'a> DeltaIter<'a> { + pub fn new(delta: &'a Delta, interval: Interval) -> Self { + let cursor = Cursor::new(delta, interval); + Self { cursor, interval } + } + + pub fn ops(&mut self) -> Vec { self.collect::>() } +} + +impl<'a> Iterator for DeltaIter<'a> { + type Item = Operation; + + fn next(&mut self) -> Option { self.cursor.next_op() } +} +#[cfg(test)] +mod tests { + + #[test] + fn test() {} +} diff --git a/rust-lib/flowy-ot/src/core/delta.rs b/rust-lib/flowy-ot/src/core/delta/delta.rs similarity index 89% rename from rust-lib/flowy-ot/src/core/delta.rs rename to rust-lib/flowy-ot/src/core/delta/delta.rs index c5f5ebfac3..7ab1a59ecd 100644 --- a/rust-lib/flowy-ot/src/core/delta.rs +++ b/rust-lib/flowy-ot/src/core/delta/delta.rs @@ -1,5 +1,5 @@ use crate::{ - core::{attributes::*, operation::*, Interval}, + core::{attributes::*, operation::*, DeltaIter, Interval}, errors::{ErrorBuilder, OTError, OTErrorCode}, }; use bytecount::num_chars; @@ -530,59 +530,6 @@ impl Delta { pub fn is_empty(&self) -> bool { self.ops.is_empty() } - pub fn ops_in_interval(&self, mut interval: Interval) -> Vec { - log::debug!("try get ops in delta: {} at {}", self, interval); - - 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 { - let next_op_i = Interval::new(offset, offset + next_op.length()); - let intersect = next_op_i.intersect(interval); - if intersect.is_empty() { - offset += next_op_i.size(); - } else { - if let Some(new_op) = next_op.shrink(intersect.translate_neg(offset)) { - // shrink the op to fit the intersect range - // ┌──────────────┐ - // │ 1 2 3 4 5 6 │ - // └───────▲───▲──┘ - // │ │ - // [3, 5) - // op = "45" - ops.push(new_op); - } - offset = intersect.end; - interval = Interval::new(offset, interval.end); - } - } 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); - } - // for example: extract the ops from three insert ops with interval [2,5). the - // interval size is larger than the op. Moving the offset to extract each part. - // Each step would be the small value between interval.size() and - // next_op.length(). Checkout the delta_get_ops_in_interval_4 for more details. - // - // ┌──────┐ ┌──────┐ ┌──────┐ - // │ 1 2 │ │ 3 4 │ │ 5 6 │ - // └──────┘ └─▲────┘ └───▲──┘ - // │ [2, 5) │ - // - offset += min(interval.size(), next_op.length()); - interval = Interval::new(offset, interval.end); - } - maybe_next_op = ops_iter.next(); - } - log::debug!("did get ops : {:?}", ops); - ops - } - pub fn get_attributes(&self, interval: Interval) -> Attributes { let mut attributes_data = AttributesData::new(); let mut offset: usize = 0; @@ -626,7 +573,7 @@ fn invert_from_other( end: usize, ) { log::debug!("invert op: {} [{}:{}]", operation, start, end); - let other_ops = other.ops_in_interval(Interval::new(start, end)); + let other_ops = DeltaIter::new(other, Interval::new(start, end)).ops(); other_ops.into_iter().for_each(|other_op| match operation { Operation::Delete(n) => { log::debug!("invert delete: {} by add {}", n, other_op); diff --git a/rust-lib/flowy-ot/src/core/delta/mod.rs b/rust-lib/flowy-ot/src/core/delta/mod.rs new file mode 100644 index 0000000000..03037fb79f --- /dev/null +++ b/rust-lib/flowy-ot/src/core/delta/mod.rs @@ -0,0 +1,5 @@ +mod cursor; +mod delta; + +pub use cursor::*; +pub use delta::*; diff --git a/rust-lib/flowy-ot/tests/helper/mod.rs b/rust-lib/flowy-ot/tests/helper/mod.rs index 49b5b08874..f804c3b5e5 100644 --- a/rust-lib/flowy-ot/tests/helper/mod.rs +++ b/rust-lib/flowy-ot/tests/helper/mod.rs @@ -73,7 +73,7 @@ impl OpTester { match op { TestOp::Insert(delta_i, s, index) => { let document = &mut self.documents[*delta_i]; - document.edit(*index, s).unwrap(); + document.insert(*index, s).unwrap(); }, TestOp::Delete(delta_i, interval) => { let document = &mut self.documents[*delta_i]; @@ -85,7 +85,7 @@ impl OpTester { }, TestOp::InsertBold(delta_i, s, interval) => { let document = &mut self.documents[*delta_i]; - document.edit(interval.start, s).unwrap(); + document.insert(interval.start, s).unwrap(); document.format(*interval, Attribute::Bold, true).unwrap(); }, TestOp::Bold(delta_i, interval, enable) => { diff --git a/rust-lib/flowy-ot/tests/invert_test.rs b/rust-lib/flowy-ot/tests/invert_test.rs deleted file mode 100644 index 8ee59dddac..0000000000 --- a/rust-lib/flowy-ot/tests/invert_test.rs +++ /dev/null @@ -1,156 +0,0 @@ -pub mod helper; -use crate::helper::{TestOp::*, *}; -use flowy_ot::core::{Builder, Delta, Interval}; - -#[test] -fn delta_invert_no_attribute_delta() { - let mut delta = Delta::default(); - delta.add(Builder::insert("123").build()); - - let mut change = Delta::default(); - change.add(Builder::retain(3).build()); - change.add(Builder::insert("456").build()); - let undo = change.invert(&delta); - - let new_delta = delta.compose(&change).unwrap(); - let delta_after_undo = new_delta.compose(&undo).unwrap(); - - assert_eq!(delta_after_undo, delta); -} - -#[test] -fn delta_invert_no_attribute_delta2() { - let ops = vec![ - Insert(0, "123", 0), - Insert(1, "4567", 0), - Invert(0, 1), - AssertOpsJson(0, r#"[{"insert":"123"}]"#), - ]; - OpTester::new().run_script(ops); -} - -#[test] -fn delta_invert_attribute_delta_with_no_attribute_delta() { - let ops = vec![ - Insert(0, "123", 0), - Bold(0, Interval::new(0, 3), true), - AssertOpsJson(0, r#"[{"insert":"123","attributes":{"bold":"true"}}]"#), - Insert(1, "4567", 0), - Invert(0, 1), - AssertOpsJson(0, r#"[{"insert":"123","attributes":{"bold":"true"}}]"#), - ]; - OpTester::new().run_script(ops); -} - -#[test] -fn delta_invert_attribute_delta_with_no_attribute_delta2() { - let ops = vec![ - Insert(0, "123", 0), - Bold(0, Interval::new(0, 3), true), - Insert(0, "456", 3), - 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"}} - ]"#, - ), - 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"}} - ]"#, - ), - ]; - OpTester::new().run_script(ops); -} - -#[test] -fn delta_invert_no_attribute_delta_with_attribute_delta() { - let ops = vec![ - Insert(0, "123", 0), - Insert(1, "4567", 0), - Bold(1, Interval::new(0, 3), true), - AssertOpsJson( - 1, - r#"[{"insert":"456","attributes":{"bold":"true"}},{"insert":"7"}]"#, - ), - Invert(0, 1), - AssertOpsJson(0, r#"[{"insert":"123"}]"#), - ]; - OpTester::new().run_script(ops); -} - -#[test] -fn delta_invert_no_attribute_delta_with_attribute_delta2() { - let ops = vec![ - Insert(0, "123", 0), - AssertOpsJson(0, r#"[{"insert":"123"}]"#), - Insert(1, "abc", 0), - Bold(1, Interval::new(0, 3), true), - Insert(1, "d", 3), - 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" -}}]"#, - ), - Invert(0, 1), - AssertOpsJson(0, r#"[{"insert":"123"}]"#), - ]; - OpTester::new().run_script(ops); -} - -#[test] -fn delta_invert_attribute_delta_with_attribute_delta() { - let ops = vec![ - Insert(0, "123", 0), - Bold(0, Interval::new(0, 3), true), - Insert(0, "456", 3), - 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"}} - ]"#, - ), - Insert(1, "abc", 0), - Bold(1, Interval::new(0, 3), true), - Insert(1, "d", 3), - 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"}} - ]"#, - ), - Invert(0, 1), - AssertOpsJson( - 0, - r#"[ - {"insert":"12","attributes":{"bold":"true"}}, - {"insert":"34","attributes":{"bold":"true","italic":"true"}}, - {"insert":"56","attributes":{"bold":"true"}} - ]"#, - ), - ]; - OpTester::new().run_script(ops); -} diff --git a/rust-lib/flowy-ot/tests/op_test.rs b/rust-lib/flowy-ot/tests/op_test.rs index 6ab6d8faae..b4210fc691 100644 --- a/rust-lib/flowy-ot/tests/op_test.rs +++ b/rust-lib/flowy-ot/tests/op_test.rs @@ -14,10 +14,8 @@ fn delta_get_ops_in_interval_1() { delta.add(insert_a.clone()); delta.add(insert_b.clone()); - assert_eq!( - delta.ops_in_interval(Interval::new(0, 4)), - vec![delta.ops.last().unwrap().clone()] - ); + let mut iterator = DeltaIter::new(&delta, Interval::new(0, 4)); + assert_eq!(iterator.ops(), delta.ops); } #[test] @@ -34,27 +32,27 @@ fn delta_get_ops_in_interval_2() { delta.add(insert_c.clone()); assert_eq!( - delta.ops_in_interval(Interval::new(0, 2)), + DeltaIter::new(&delta, Interval::new(0, 2)).ops(), vec![Builder::insert("12").build()] ); assert_eq!( - delta.ops_in_interval(Interval::new(0, 3)), + DeltaIter::new(&delta, Interval::new(0, 3)).ops(), vec![insert_a.clone()] ); assert_eq!( - delta.ops_in_interval(Interval::new(0, 4)), + DeltaIter::new(&delta, Interval::new(0, 4)).ops(), vec![insert_a.clone(), Builder::retain(1).build()] ); assert_eq!( - delta.ops_in_interval(Interval::new(0, 6)), + DeltaIter::new(&delta, Interval::new(0, 6)).ops(), vec![insert_a.clone(), retain_a.clone()] ); assert_eq!( - delta.ops_in_interval(Interval::new(0, 7)), + DeltaIter::new(&delta, Interval::new(0, 7)).ops(), vec![insert_a.clone(), retain_a.clone(), insert_b.clone()] ); } @@ -65,7 +63,7 @@ fn delta_get_ops_in_interval_3() { let insert_a = Builder::insert("123456").build(); delta.add(insert_a.clone()); assert_eq!( - delta.ops_in_interval(Interval::new(3, 5)), + DeltaIter::new(&delta, Interval::new(3, 5)).ops(), vec![Builder::insert("45").build()] ); } @@ -81,12 +79,21 @@ fn delta_get_ops_in_interval_4() { 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!( + DeltaIter::new(&delta, Interval::new(0, 2)).ops(), + vec![insert_a] + ); + assert_eq!( + DeltaIter::new(&delta, Interval::new(2, 4)).ops(), + vec![insert_b] + ); + assert_eq!( + DeltaIter::new(&delta, Interval::new(4, 6)).ops(), + vec![insert_c] + ); assert_eq!( - delta.ops_in_interval(Interval::new(2, 5)), + DeltaIter::new(&delta, Interval::new(2, 5)).ops(), vec![Builder::insert("34").build(), Builder::insert("5").build()] ); } @@ -99,12 +106,12 @@ fn delta_get_ops_in_interval_5() { delta.ops.push(insert_a.clone()); delta.ops.push(insert_b.clone()); assert_eq!( - delta.ops_in_interval(Interval::new(4, 8)), + DeltaIter::new(&delta, Interval::new(4, 8)).ops(), vec![Builder::insert("56").build(), Builder::insert("78").build()] ); assert_eq!( - delta.ops_in_interval(Interval::new(8, 9)), + DeltaIter::new(&delta, Interval::new(8, 9)).ops(), vec![Builder::insert("9").build()] ); } @@ -115,7 +122,7 @@ fn delta_get_ops_in_interval_6() { let insert_a = Builder::insert("12345678").build(); delta.add(insert_a.clone()); assert_eq!( - delta.ops_in_interval(Interval::new(4, 6)), + DeltaIter::new(&delta, Interval::new(4, 6)).ops(), vec![Builder::insert("56").build()] ); } @@ -336,3 +343,156 @@ fn delta_transform_test() { serde_json::to_string(&b_prime).unwrap() ); } + +#[test] +fn delta_invert_no_attribute_delta() { + let mut delta = Delta::default(); + delta.add(Builder::insert("123").build()); + + let mut change = Delta::default(); + change.add(Builder::retain(3).build()); + change.add(Builder::insert("456").build()); + let undo = change.invert(&delta); + + let new_delta = delta.compose(&change).unwrap(); + let delta_after_undo = new_delta.compose(&undo).unwrap(); + + assert_eq!(delta_after_undo, delta); +} + +#[test] +fn delta_invert_no_attribute_delta2() { + let ops = vec![ + Insert(0, "123", 0), + Insert(1, "4567", 0), + Invert(0, 1), + AssertOpsJson(0, r#"[{"insert":"123"}]"#), + ]; + OpTester::new().run_script(ops); +} + +#[test] +fn delta_invert_attribute_delta_with_no_attribute_delta() { + let ops = vec![ + Insert(0, "123", 0), + Bold(0, Interval::new(0, 3), true), + AssertOpsJson(0, r#"[{"insert":"123","attributes":{"bold":"true"}}]"#), + Insert(1, "4567", 0), + Invert(0, 1), + AssertOpsJson(0, r#"[{"insert":"123","attributes":{"bold":"true"}}]"#), + ]; + OpTester::new().run_script(ops); +} + +#[test] +fn delta_invert_attribute_delta_with_no_attribute_delta2() { + let ops = vec![ + Insert(0, "123", 0), + Bold(0, Interval::new(0, 3), true), + Insert(0, "456", 3), + 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"}} + ]"#, + ), + 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"}} + ]"#, + ), + ]; + OpTester::new().run_script(ops); +} + +#[test] +fn delta_invert_no_attribute_delta_with_attribute_delta() { + let ops = vec![ + Insert(0, "123", 0), + Insert(1, "4567", 0), + Bold(1, Interval::new(0, 3), true), + AssertOpsJson( + 1, + r#"[{"insert":"456","attributes":{"bold":"true"}},{"insert":"7"}]"#, + ), + Invert(0, 1), + AssertOpsJson(0, r#"[{"insert":"123"}]"#), + ]; + OpTester::new().run_script(ops); +} + +#[test] +fn delta_invert_no_attribute_delta_with_attribute_delta2() { + let ops = vec![ + Insert(0, "123", 0), + AssertOpsJson(0, r#"[{"insert":"123"}]"#), + Insert(1, "abc", 0), + Bold(1, Interval::new(0, 3), true), + Insert(1, "d", 3), + 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" +}}]"#, + ), + Invert(0, 1), + AssertOpsJson(0, r#"[{"insert":"123"}]"#), + ]; + OpTester::new().run_script(ops); +} + +#[test] +fn delta_invert_attribute_delta_with_attribute_delta() { + let ops = vec![ + Insert(0, "123", 0), + Bold(0, Interval::new(0, 3), true), + Insert(0, "456", 3), + 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"}} + ]"#, + ), + Insert(1, "abc", 0), + Bold(1, Interval::new(0, 3), true), + Insert(1, "d", 3), + 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"}} + ]"#, + ), + Invert(0, 1), + AssertOpsJson( + 0, + r#"[ + {"insert":"12","attributes":{"bold":"true"}}, + {"insert":"34","attributes":{"bold":"true","italic":"true"}}, + {"insert":"56","attributes":{"bold":"true"}} + ]"#, + ), + ]; + OpTester::new().run_script(ops); +}