///| A `DiffHook` that merges adjacent `Insert` and `Delete` operations
///| into a single `Replace` operation.
pub(all) struct Replace[D] {
// The inner diff hook to which operations are reported.
d : D
// A pending delete operation, if any.
mut del : (Int, Int, Int)?
// A pending insert operation, if any.
mut ins : (Int, Int, Int)?
// A pending equal operation, if any.
mut eq : (Int, Int, Int)?
}
///| Creates a new `Replace` hook.
pub fn[D] Replace::new(d : D) -> Replace[D] {
{ d, del: None, ins: None, eq: None }
}
///| Consumes the `Replace` hook and returns the inner hook.
pub fn[D] Replace::into_inner(self : Replace[D]) -> D {
self.d
}
///| Flushes any pending `Equal` operation to the inner hook.
pub fn[D : DiffHook] Replace::flush_eq(
self : Replace[D],
) -> Result[Unit, Error] {
match self.eq {
Some((old_index, new_index, len)) => {
self.eq = None
self.d.equal(old_index, new_index, len)
}
None => Ok(())
}
}
///| Flushes any pending `Delete` and `Insert` operations to the inner hook.
///| If both are present, they are merged into a `Replace` operation.
pub fn[D : DiffHook] Replace::flush_del_ins(
self : Replace[D],
) -> Result[Unit, Error] {
match self.del {
Some((del_old_index, del_len, del_new_index)) => {
self.del = None
match self.ins {
Some((_, ins_new_index, ins_len)) => {
self.ins = None
self.d.replace(del_old_index, del_len, ins_new_index, ins_len)
}
None => self.d.delete(del_old_index, del_len, del_new_index)
}
}
None =>
match self.ins {
Some((ins_old_index, ins_new_index, ins_len)) => {
self.ins = None
self.d.insert(ins_old_index, ins_new_index, ins_len)
}
None => Ok(())
}
}
}
///| Handles an `Equal` operation.
///| Flushes any pending insert/delete ops and merges consecutive equal ops.
pub impl[D : DiffHook] DiffHook for Replace[D] with equal(
self,
old_index,
new_index,
len,
) {
let _ = self.flush_del_ins()
self.eq = match self.eq {
Some((eq_old_index, eq_new_index, eq_len)) =>
Some((eq_old_index, eq_new_index, eq_len + len))
None => Some((old_index, new_index, len))
}
Ok(())
}
///| Handles a `Delete` operation.
///| Flushes any pending equal ops and merges consecutive delete ops.
pub impl[D : DiffHook] DiffHook for Replace[D] with delete(
self,
old_index,
old_len,
new_index,
) {
let _ = self.flush_eq()
match self.del {
Some((del_old_index, del_old_len, del_new_index)) =>
self.del = Some((del_old_index, del_old_len + old_len, del_new_index))
None => self.del = Some((old_index, old_len, new_index))
}
Ok(())
}
///| Handles an `Insert` operation.
///| Flushes any pending equal ops and merges consecutive insert ops.
pub impl[D : DiffHook] DiffHook for Replace[D] with insert(
self,
old_index,
new_index,
new_len,
) {
let _ = self.flush_eq()
match self.ins {
Some((ins_old_index, ins_new_index, ins_len)) =>
self.ins = Some((ins_old_index, ins_new_index, ins_len + new_len))
None => self.ins = Some((old_index, new_index, new_len))
}
Ok(())
}
///| Handles a `Replace` operation.
///| Flushes any pending operations and forwards the replace to the inner hook.
pub impl[D : DiffHook] DiffHook for Replace[D] with replace(
self,
old_index,
old_len,
new_index,
new_len,
) {
let _ = self.flush_eq()
let _ = self.flush_del_ins()
self.d.replace(old_index, old_len, new_index, new_len)
}
///| Finishes the diffing process by flushing any remaining pending operations.
pub impl[D : DiffHook] DiffHook for Replace[D] with finish(self) {
let _ = self.flush_eq()
let _ = self.flush_del_ins()
self.d.finish()
}
///| A test for the `Replace` hook with the Myers algorithm.
test "test_myers_replace" {
let a = [
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", "a\n", "b\n", "c\n", "================================\n",
"d\n", "e\n", "f\n", "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n",
]
let b = [
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", "x\n", "b\n", "c\n", "================================\n",
"y\n", "e\n", "f\n", "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n",
]
let capture = Capture::new()
let d = Replace::new(capture)
let _ = Myers::diff(
d,
a,
Range::new(0, a.length()),
b,
Range::new(0, b.length()),
)
let ops = capture.ops()
inspect(
ops,
content="[Equal(old_index=0, new_index=0, len=1), Replace(old_index=1, old_len=1, new_index=1, new_len=1), Equal(old_index=2, new_index=2, len=3), Replace(old_index=5, old_len=1, new_index=5, new_len=1), Equal(old_index=6, new_index=6, len=3)]",
)
}
///| Another test for the `Replace` hook.
test "test_replace" {
let a = [0, 1, 2, 3, 4]
let b = [0, 1, 2, 7, 8, 9]
let capture = Capture::new()
let d = Replace::new(capture)
let _ = Myers::diff(
d,
a,
Range::new(0, a.length()),
b,
Range::new(0, b.length()),
)
let ops = capture.ops()
inspect(
ops,
content="[Equal(old_index=0, new_index=0, len=3), Replace(old_index=3, old_len=2, new_index=3, new_len=3)]",
)
}