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) {
|
if (stack.undo.isNotEmpty) {
|
||||||
final lastDelta = stack.undo.removeLast();
|
final lastDelta = stack.undo.removeLast();
|
||||||
print("undoDelta: $undoDelta");
|
|
||||||
print("lastDelta: $lastDelta");
|
|
||||||
undoDelta = undoDelta.compose(lastDelta);
|
undoDelta = undoDelta.compose(lastDelta);
|
||||||
print("compose result: $undoDelta");
|
|
||||||
} else {
|
} else {
|
||||||
lastRecorded = timestamp;
|
lastRecorded = timestamp;
|
||||||
}
|
}
|
||||||
|
@ -40,21 +40,21 @@ class Rules {
|
|||||||
final List<Rule> _rules;
|
final List<Rule> _rules;
|
||||||
|
|
||||||
static final Rules _instance = Rules([
|
static final Rules _instance = Rules([
|
||||||
const FormatLinkAtCaretPositionRule(),
|
// const FormatLinkAtCaretPositionRule(),
|
||||||
const ResolveLineFormatRule(),
|
// const ResolveLineFormatRule(),
|
||||||
const ResolveInlineFormatRule(),
|
// const ResolveInlineFormatRule(),
|
||||||
const InsertEmbedsRule(),
|
// const InsertEmbedsRule(),
|
||||||
const ForceNewlineForInsertsAroundEmbedRule(),
|
// const ForceNewlineForInsertsAroundEmbedRule(),
|
||||||
const AutoExitBlockRule(),
|
// const AutoExitBlockRule(),
|
||||||
const PreserveBlockStyleOnInsertRule(),
|
// const PreserveBlockStyleOnInsertRule(),
|
||||||
const PreserveLineStyleOnSplitRule(),
|
// const PreserveLineStyleOnSplitRule(),
|
||||||
const ResetLineFormatOnNewLineRule(),
|
// const ResetLineFormatOnNewLineRule(),
|
||||||
const AutoFormatLinksRule(),
|
// const AutoFormatLinksRule(),
|
||||||
const PreserveInlineStylesRule(),
|
// const PreserveInlineStylesRule(),
|
||||||
const CatchAllInsertRule(),
|
// const CatchAllInsertRule(),
|
||||||
const EnsureEmbedLineRule(),
|
// const EnsureEmbedLineRule(),
|
||||||
const PreserveLineStyleOnMergeRule(),
|
// const PreserveLineStyleOnMergeRule(),
|
||||||
const CatchAllDeleteRule(),
|
// const CatchAllDeleteRule(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
static Rules getInstance() => _instance;
|
static Rules getInstance() => _instance;
|
||||||
|
@ -13,6 +13,7 @@ derive_more = {version = "0.99", features = ["display"]}
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
color-eyre = { version = "0.5", default-features = false }
|
color-eyre = { version = "0.5", default-features = false }
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
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 {
|
if self.data.target_len < index {
|
||||||
log::error!(
|
log::error!(
|
||||||
"{} out of bounds. should 0..{}",
|
"{} out of bounds. should 0..{}",
|
||||||
@ -59,6 +59,21 @@ impl Document {
|
|||||||
self.update_with_attribute(attributes, interval)
|
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_undo(&self) -> bool { self.history.can_undo() }
|
||||||
|
|
||||||
pub fn can_redo(&self) -> bool { self.history.can_redo() }
|
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_json(&self) -> String { self.data.to_json() }
|
||||||
|
|
||||||
pub fn to_string(&self) -> String { self.data.apply("").unwrap() }
|
pub fn to_string(&self) -> String { self.data.apply("").unwrap() }
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
mod document;
|
mod document;
|
||||||
mod history;
|
mod history;
|
||||||
|
mod view;
|
||||||
|
|
||||||
pub use document::*;
|
pub use document::*;
|
||||||
pub use history::*;
|
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::{
|
use crate::{
|
||||||
core::{attributes::*, operation::*, Interval},
|
core::{attributes::*, operation::*, DeltaIter, Interval},
|
||||||
errors::{ErrorBuilder, OTError, OTErrorCode},
|
errors::{ErrorBuilder, OTError, OTErrorCode},
|
||||||
};
|
};
|
||||||
use bytecount::num_chars;
|
use bytecount::num_chars;
|
||||||
@ -530,59 +530,6 @@ impl Delta {
|
|||||||
|
|
||||||
pub fn is_empty(&self) -> bool { self.ops.is_empty() }
|
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 {
|
pub fn get_attributes(&self, interval: Interval) -> Attributes {
|
||||||
let mut attributes_data = AttributesData::new();
|
let mut attributes_data = AttributesData::new();
|
||||||
let mut offset: usize = 0;
|
let mut offset: usize = 0;
|
||||||
@ -626,7 +573,7 @@ fn invert_from_other(
|
|||||||
end: usize,
|
end: usize,
|
||||||
) {
|
) {
|
||||||
log::debug!("invert op: {} [{}:{}]", operation, start, end);
|
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 {
|
other_ops.into_iter().for_each(|other_op| match operation {
|
||||||
Operation::Delete(n) => {
|
Operation::Delete(n) => {
|
||||||
log::debug!("invert delete: {} by add {}", n, other_op);
|
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 {
|
match op {
|
||||||
TestOp::Insert(delta_i, s, index) => {
|
TestOp::Insert(delta_i, s, index) => {
|
||||||
let document = &mut self.documents[*delta_i];
|
let document = &mut self.documents[*delta_i];
|
||||||
document.edit(*index, s).unwrap();
|
document.insert(*index, s).unwrap();
|
||||||
},
|
},
|
||||||
TestOp::Delete(delta_i, interval) => {
|
TestOp::Delete(delta_i, interval) => {
|
||||||
let document = &mut self.documents[*delta_i];
|
let document = &mut self.documents[*delta_i];
|
||||||
@ -85,7 +85,7 @@ impl OpTester {
|
|||||||
},
|
},
|
||||||
TestOp::InsertBold(delta_i, s, interval) => {
|
TestOp::InsertBold(delta_i, s, interval) => {
|
||||||
let document = &mut self.documents[*delta_i];
|
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();
|
document.format(*interval, Attribute::Bold, true).unwrap();
|
||||||
},
|
},
|
||||||
TestOp::Bold(delta_i, interval, enable) => {
|
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_a.clone());
|
||||||
delta.add(insert_b.clone());
|
delta.add(insert_b.clone());
|
||||||
|
|
||||||
assert_eq!(
|
let mut iterator = DeltaIter::new(&delta, Interval::new(0, 4));
|
||||||
delta.ops_in_interval(Interval::new(0, 4)),
|
assert_eq!(iterator.ops(), delta.ops);
|
||||||
vec![delta.ops.last().unwrap().clone()]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -34,27 +32,27 @@ fn delta_get_ops_in_interval_2() {
|
|||||||
delta.add(insert_c.clone());
|
delta.add(insert_c.clone());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
delta.ops_in_interval(Interval::new(0, 2)),
|
DeltaIter::new(&delta, Interval::new(0, 2)).ops(),
|
||||||
vec![Builder::insert("12").build()]
|
vec![Builder::insert("12").build()]
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
delta.ops_in_interval(Interval::new(0, 3)),
|
DeltaIter::new(&delta, Interval::new(0, 3)).ops(),
|
||||||
vec![insert_a.clone()]
|
vec![insert_a.clone()]
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
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()]
|
vec![insert_a.clone(), Builder::retain(1).build()]
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
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()]
|
vec![insert_a.clone(), retain_a.clone()]
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
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()]
|
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();
|
let insert_a = Builder::insert("123456").build();
|
||||||
delta.add(insert_a.clone());
|
delta.add(insert_a.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
delta.ops_in_interval(Interval::new(3, 5)),
|
DeltaIter::new(&delta, Interval::new(3, 5)).ops(),
|
||||||
vec![Builder::insert("45").build()]
|
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_b.clone());
|
||||||
delta.ops.push(insert_c.clone());
|
delta.ops.push(insert_c.clone());
|
||||||
|
|
||||||
assert_eq!(delta.ops_in_interval(Interval::new(0, 2)), vec![insert_a]);
|
assert_eq!(
|
||||||
assert_eq!(delta.ops_in_interval(Interval::new(2, 4)), vec![insert_b]);
|
DeltaIter::new(&delta, Interval::new(0, 2)).ops(),
|
||||||
assert_eq!(delta.ops_in_interval(Interval::new(4, 6)), vec![insert_c]);
|
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!(
|
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()]
|
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_a.clone());
|
||||||
delta.ops.push(insert_b.clone());
|
delta.ops.push(insert_b.clone());
|
||||||
assert_eq!(
|
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()]
|
vec![Builder::insert("56").build(), Builder::insert("78").build()]
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
delta.ops_in_interval(Interval::new(8, 9)),
|
DeltaIter::new(&delta, Interval::new(8, 9)).ops(),
|
||||||
vec![Builder::insert("9").build()]
|
vec![Builder::insert("9").build()]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -115,7 +122,7 @@ fn delta_get_ops_in_interval_6() {
|
|||||||
let insert_a = Builder::insert("12345678").build();
|
let insert_a = Builder::insert("12345678").build();
|
||||||
delta.add(insert_a.clone());
|
delta.add(insert_a.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
delta.ops_in_interval(Interval::new(4, 6)),
|
DeltaIter::new(&delta, Interval::new(4, 6)).ops(),
|
||||||
vec![Builder::insert("56").build()]
|
vec![Builder::insert("56").build()]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -336,3 +343,156 @@ fn delta_transform_test() {
|
|||||||
serde_json::to_string(&b_prime).unwrap()
|
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…
x
Reference in New Issue
Block a user