mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
add extensions
This commit is contained in:
parent
1490d5c1b0
commit
15c3a821ec
@ -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;
|
||||
}
|
||||
|
@ -40,21 +40,21 @@ class Rules {
|
||||
final List<Rule> _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;
|
||||
|
@ -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"
|
||||
|
@ -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() }
|
||||
|
@ -1,5 +1,6 @@
|
||||
mod document;
|
||||
mod history;
|
||||
mod view;
|
||||
|
||||
pub use document::*;
|
||||
pub use history::*;
|
||||
|
23
rust-lib/flowy-ot/src/client/view/extension.rs
Normal file
23
rust-lib/flowy-ot/src/client/view/extension.rs
Normal file
@ -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<Box<InsertExt>> = vec![PreserveInlineStyleExt::new(),];
|
||||
static ref FORMAT_EXT: Vec<Box<FormatExt>> = vec![];
|
||||
static ref DELETE_EXT: Vec<Box<DeleteExt>> = vec![];
|
||||
}
|
14
rust-lib/flowy-ot/src/client/view/insert_ext.rs
Normal file
14
rust-lib/flowy-ot/src/client/view/insert_ext.rs
Normal file
@ -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!() }
|
||||
}
|
6
rust-lib/flowy-ot/src/client/view/mod.rs
Normal file
6
rust-lib/flowy-ot/src/client/view/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
mod extension;
|
||||
mod insert_ext;
|
||||
|
||||
pub use extension::*;
|
||||
|
||||
pub use insert_ext::*;
|
98
rust-lib/flowy-ot/src/core/delta/cursor.rs
Normal file
98
rust-lib/flowy-ot/src/core/delta/cursor.rs
Normal file
@ -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<Operation> {
|
||||
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<Operation> { self.collect::<Vec<_>>() }
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DeltaIter<'a> {
|
||||
type Item = Operation;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> { self.cursor.next_op() }
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[test]
|
||||
fn test() {}
|
||||
}
|
@ -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<Operation> {
|
||||
log::debug!("try get ops in delta: {} at {}", self, interval);
|
||||
|
||||
let mut ops: Vec<Operation> = 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);
|
5
rust-lib/flowy-ot/src/core/delta/mod.rs
Normal file
5
rust-lib/flowy-ot/src/core/delta/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
mod cursor;
|
||||
mod delta;
|
||||
|
||||
pub use cursor::*;
|
||||
pub use delta::*;
|
@ -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) => {
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user