mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
add redo/undo test with delete operation
This commit is contained in:
parent
1d7540c954
commit
251b065dd8
@ -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!()
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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"}}]
|
||||
"#,
|
||||
),
|
||||
];
|
||||
|
||||
|
@ -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()]
|
||||
);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user