add redo/undo test with delete operation

This commit is contained in:
appflowy 2021-08-09 11:29:37 +08:00
parent 1d7540c954
commit 251b065dd8
4 changed files with 213 additions and 72 deletions

View File

@ -482,50 +482,17 @@ impl Delta {
return inverted;
}
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(_) => {
log::debug!(
"Start invert attributes: {:?}, {:?}",
operation.get_attributes(),
other_op.get_attributes()
);
let inverted_attrs = invert_attributes(
operation.get_attributes(),
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(_) => {
// Impossible to here
panic!()
},
}
});
};
let mut index = 0;
for op in &self.ops {
let len: usize = op.length() as usize;
match op {
Operation::Delete(n) => {
inverted_from_other(&mut inverted, op, index, index + *n);
invert_from_other(&mut inverted, other, op, index, index + *n);
index += len;
},
Operation::Retain(_) => {
match op.has_attribute() {
true => inverted_from_other(&mut inverted, op, index, index + len),
true => invert_from_other(&mut inverted, other, op, index, index + len),
false => {
log::debug!("invert retain op: {:?}", op);
inverted.retain(len as usize, op.get_attributes())
@ -533,10 +500,9 @@ impl Delta {
}
index += len;
},
Operation::Insert(insert) => {
Operation::Insert(_) => {
log::debug!("invert insert op: {:?}", op);
inverted.delete(len as usize);
// index += insert.s.len();
},
}
}
@ -558,7 +524,6 @@ impl Delta {
pub fn ops_in_interval(&self, mut interval: Interval) -> Vec<Operation> {
log::debug!("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();
@ -567,27 +532,39 @@ impl Delta {
while maybe_next_op.is_some() {
let next_op = maybe_next_op.take().unwrap();
if offset < interval.start {
if next_op.length() > interval.size() {
let next_op_i = Interval::new(offset, offset + next_op.length());
let intersect = next_op_i.intersect(interval);
if !intersect.is_empty() {
// if the op's length larger than the interval size, just shrink the op to that
// interval. for example: delta: "123456", interval: [3,6).
// interval Checkout the delta_get_ops_in_interval_3 test for more details.
// ┌──────────────┐
// │ 1 2 3 4 5 6 │
// └───────▲───▲──┘
// │ │
// [3, 6)
if let Some(new_op) = next_op.shrink(interval) {
// [3, 5)
// op = "45"
if let Some(new_op) = next_op.shrink(intersect) {
offset += min(intersect.end, next_op.length());
interval = Interval::new(offset, interval.end);
ops.push(new_op);
}
} else {
// adding the op's length to offset until the offset is contained in the
// interval
offset += next_op.length();
offset += next_op_i.size();
}
} 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);
}
// take the small value between interval.size() and next_op.length().
// for example: extract the ops from three insert ops with interval [2,5). the
// interval size is larger than the op. Run 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);
}
@ -635,3 +612,38 @@ impl Delta {
pub fn to_json(&self) -> String { serde_json::to_string(self).unwrap_or("".to_owned()) }
}
fn invert_from_other(
inverted: &mut Delta,
other: &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(_) => {
log::debug!(
"Start invert attributes: {:?}, {:?}",
operation.get_attributes(),
other_op.get_attributes()
);
let inverted_attrs =
invert_attributes(operation.get_attributes(), 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(_) => {
// Impossible to here
panic!()
},
}
});
}

View File

@ -154,7 +154,11 @@ fn delta_add_bold_italic() {
Italic(0, Interval::new(4, 6), false),
AssertOpsJson(
0,
r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true"}},{"insert":"78","attributes":{"bold":"true","italic":"true"}}]"#,
r#"[
{"insert":"1234","attributes":{"bold":"true","italic":"true"}},
{"insert":"56","attributes":{"bold":"true"}},
{"insert":"78","attributes":{"bold":"true","italic":"true"}}]
"#,
),
];
OpTester::new().run_script(ops);
@ -169,12 +173,19 @@ fn delta_add_bold_italic2() {
Italic(0, Interval::new(0, 2), true),
AssertOpsJson(
0,
r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"3456","attributes":{"bold":"true"}}]"#,
r#"[
{"insert":"12","attributes":{"italic":"true","bold":"true"}},
{"insert":"3456","attributes":{"bold":"true"}}]
"#,
),
Italic(0, Interval::new(4, 6), true),
AssertOpsJson(
0,
r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"34","attributes":{"bold":"true"}},{"insert":"56","attributes":{"italic":"true","bold":"true"}}]"#,
r#"[
{"insert":"12","attributes":{"italic":"true","bold":"true"}},
{"insert":"34","attributes":{"bold":"true"}},
{"insert":"56","attributes":{"italic":"true","bold":"true"}}]
"#,
),
];
@ -189,17 +200,29 @@ fn delta_add_bold_italic3() {
Italic(0, Interval::new(0, 2), true),
AssertOpsJson(
0,
r#"[{"insert":"12","attributes":{"bold":"true","italic":"true"}},{"insert":"345","attributes":{"bold":"true"}},{"insert":"6789"}]"#,
r#"[
{"insert":"12","attributes":{"bold":"true","italic":"true"}},
{"insert":"345","attributes":{"bold":"true"}},{"insert":"6789"}]
"#,
),
Italic(0, Interval::new(2, 4), true),
AssertOpsJson(
0,
r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"5","attributes":{"bold":"true"}},{"insert":"6789"}]"#,
r#"[
{"insert":"1234","attributes":{"bold":"true","italic":"true"}},
{"insert":"5","attributes":{"bold":"true"}},
{"insert":"6789"}]
"#,
),
Bold(0, Interval::new(7, 9), true),
AssertOpsJson(
0,
r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"5","attributes":{"bold":"true"}},{"insert":"67"},{"insert":"89","attributes":{"bold":"true"}}]"#,
r#"[
{"insert":"1234","attributes":{"bold":"true","italic":"true"}},
{"insert":"5","attributes":{"bold":"true"}},
{"insert":"67"},
{"insert":"89","attributes":{"bold":"true"}}]
"#,
),
];
@ -212,27 +235,35 @@ fn delta_add_bold_italic_delete() {
Insert(0, "123456789", 0),
Bold(0, Interval::new(0, 5), true),
Italic(0, Interval::new(0, 2), true),
// AssertOpsJson(
// 0,
// r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"345","
// attributes":{"bold":"true"}},{"insert":"6789"}]"#, ),
AssertOpsJson(
0,
r#"[
{"insert":"12","attributes":{"italic":"true","bold":"true"}},
{"insert":"345","attributes":{"bold":"true"}},{"insert":"6789"}]
"#,
),
Italic(0, Interval::new(2, 4), true),
AssertOpsJson(
0,
r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"5","attributes":{"bold":"true"}},{"insert":"6789"}]"#,
r#"[
{"insert":"1234","attributes":{"bold":"true","italic":"true"}}
,{"insert":"5","attributes":{"bold":"true"}},{"insert":"6789"}]"#,
),
Bold(0, Interval::new(7, 9), true),
AssertOpsJson(
0,
r#"[
{"insert":"1234","attributes":{"bold":"true","italic":"true"}},
{"insert":"5","attributes":{"bold":"true"}},{"insert":"67"},
{"insert":"89","attributes":{"bold":"true"}}]
"#,
),
Delete(0, Interval::new(0, 5)),
AssertOpsJson(
0,
r#"[{"insert":"67"},{"insert":"89","attributes":{"bold":"true"}}]"#,
),
];
// Bold(0, Interval::new(7, 9), true),
// AssertOpsJson(
// 0,
// r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"
// insert":"5","attributes":{"bold":"true"}},{"insert":"67"},{"insert":"89","
// attributes":{"bold":"true"}}]"#, ),
// Delete(0, Interval::new(0, 5)),
// AssertOpsJson(
// 0,
// r#"[{"insert":"67"},{"insert":"89","attributes":{"bold":"true"}}]"#,
// ),
OpTester::new().run_script(ops);
}
@ -272,18 +303,32 @@ fn delta_compose_attr_delta_with_attr_delta_test2() {
Italic(0, Interval::new(4, 6), true),
AssertOpsJson(
0,
r#"[{"insert":"12","attributes":{"bold":"true","italic":"true"}},{"insert":"34","attributes":{"bold":"true"}},{"insert":"56","attributes":{"italic":"true","bold":"true"}}]"#,
r#"[
{"insert":"12","attributes":{"bold":"true","italic":"true"}},
{"insert":"34","attributes":{"bold":"true"}},
{"insert":"56","attributes":{"italic":"true","bold":"true"}}]
"#,
),
InsertBold(1, "7", Interval::new(0, 1)),
AssertOpsJson(1, r#"[{"insert":"7","attributes":{"bold":"true"}}]"#),
Transform(0, 1),
AssertOpsJson(
0,
r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"34","attributes":{"bold":"true"}},{"insert":"56","attributes":{"italic":"true","bold":"true"}},{"insert":"7","attributes":{"bold":"true"}}]"#,
r#"[
{"insert":"12","attributes":{"italic":"true","bold":"true"}},
{"insert":"34","attributes":{"bold":"true"}},
{"insert":"56","attributes":{"italic":"true","bold":"true"}},
{"insert":"7","attributes":{"bold":"true"}}]
"#,
),
AssertOpsJson(
1,
r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"34","attributes":{"bold":"true"}},{"insert":"56","attributes":{"italic":"true","bold":"true"}},{"insert":"7","attributes":{"bold":"true"}}]"#,
r#"[
{"insert":"12","attributes":{"italic":"true","bold":"true"}},
{"insert":"34","attributes":{"bold":"true"}},
{"insert":"56","attributes":{"italic":"true","bold":"true"}},
{"insert":"7","attributes":{"bold":"true"}}]
"#,
),
];

View File

@ -215,8 +215,8 @@ 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, 6)),
vec![Builder::insert("456").build()]
delta.ops_in_interval(Interval::new(3, 5)),
vec![Builder::insert("45").build()]
);
}
@ -240,3 +240,27 @@ fn delta_get_ops_in_interval_4() {
vec![Builder::insert("34").build(), Builder::insert("5").build()]
);
}
#[test]
fn delta_get_ops_in_interval_5() {
let mut delta = Delta::default();
let insert_a = Builder::insert("123456").build();
let insert_b = Builder::insert("789").build();
delta.ops.push(insert_a.clone());
delta.ops.push(insert_b.clone());
assert_eq!(
delta.ops_in_interval(Interval::new(4, 8)),
vec![Builder::insert("56").build(), Builder::insert("78").build()]
);
}
#[test]
fn delta_get_ops_in_interval_6() {
let mut delta = Delta::default();
let insert_a = Builder::insert("12345678").build();
delta.add(insert_a.clone());
assert_eq!(
delta.ops_in_interval(Interval::new(4, 6)),
vec![Builder::insert("56").build()]
);
}

View File

@ -88,3 +88,63 @@ fn delta_redo_attributes() {
];
OpTester::new().run_script(ops);
}
#[test]
fn delta_undo_delete() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true),
Delete(0, Interval::new(0, 3)),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
Undo(0),
AssertOpsJson(
0,
r#"[
{"insert":"123","attributes":{"bold":"true"}},
{"insert":"\n"}]
"#,
),
];
OpTester::new().run_script(ops);
}
#[test]
fn delta_undo_delete2() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true),
Delete(0, Interval::new(0, 1)),
AssertOpsJson(
0,
r#"[
{"insert":"23","attributes":{"bold":"true"}},
{"insert":"\n"}]
"#,
),
Undo(0),
AssertOpsJson(
0,
r#"[
{"insert":"123","attributes":{"bold":"true"}},
{"insert":"\n"}]
"#,
),
];
OpTester::new().run_script(ops);
}
#[test]
fn delta_redo_delete() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Delete(0, Interval::new(0, 3)),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
Undo(0),
Redo(0),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
];
OpTester::new().run_script(ops);
}