///| Represents the different types of operations in a diff.
pub(all) enum DiffOp {
/// A sequence of items that are the same in both versions.
Equal(mut old_index~ : Int, mut new_index~ : Int, mut len~ : Int)
/// A sequence of items that were deleted from the old version.
Delete(mut old_index~ : Int, mut old_len~ : Int, mut new_index~ : Int)
/// A sequence of items that were inserted in the new version.
Insert(mut old_index~ : Int, mut new_index~ : Int, mut new_len~ : Int)
/// A sequence of items that were replaced.
Replace(
mut old_index~ : Int,
mut old_len~ : Int,
mut new_index~ : Int,
mut new_len~ : Int
)
} derive(Eq, Show, Hash, Compare)
///| Applies the given `DiffOp` to a `DiffHook`.
pub fn[D : DiffHook] DiffOp::apply_to_hook(
self : Self,
d : D,
) -> Result[Unit, Error] {
match self {
Equal(old_index~, new_index~, len~) => d.equal(old_index, new_index, len)
Delete(old_index~, old_len~, new_index~) =>
d.delete(old_index, old_len, new_index)
Insert(old_index~, new_index~, new_len~) =>
d.insert(old_index, new_index, new_len)
Replace(old_index~, old_len~, new_index~, new_len~) =>
d.replace(old_index, old_len, new_index, new_len)
}
}
///| Returns the range of the operation in the old sequence.
pub fn DiffOp::old_range(self : Self) -> Range {
match self {
Equal(old_index~, new_index=_, len~) =>
Range::new(old_index, old_index + len)
Delete(old_index~, old_len~, new_index=_) =>
Range::new(old_index, old_index + old_len)
Insert(old_index~, new_index=_, new_len=_) =>
Range::new(old_index, old_index)
Replace(old_index~, old_len~, new_index=_, new_len=_) =>
Range::new(old_index, old_index + old_len)
}
}
///| Returns the range of the operation in the new sequence.
pub fn DiffOp::new_range(self : Self) -> Range {
match self {
Equal(old_index=_, new_index~, len~) =>
Range::new(new_index, new_index + len)
Delete(old_index=_, old_len=_, new_index~) =>
Range::new(new_index, new_index)
Insert(old_index=_, new_index~, new_len~) =>
Range::new(new_index, new_index + new_len)
Replace(old_index=_, old_len=_, new_index~, new_len~) =>
Range::new(new_index, new_index + new_len)
}
}
///| Checks if the operation is empty (i.e., has a length of zero).
pub fn DiffOp::is_empty(self : Self) -> Bool {
self.old_range().is_empty() && self.new_range().is_empty()
}
///| Shifts the operation to the left by a given amount.
///| Used by the `Compact` hook to merge operations.
pub fn DiffOp::shift_left(self : Self, adjust : Int) -> Unit {
self.adjust((adjust, true), (0, false))
}
///| Shifts the operation to the right by a given amount.
///| Used by the `Compact` hook to merge operations.
pub fn DiffOp::shift_right(self : Self, adjust : Int) -> Unit {
self.adjust((adjust, false), (0, false))
}
///| Grows the operation to the left by a given amount.
///| Used by the `Compact` hook to merge operations.
pub fn DiffOp::grow_left(self : Self, adjust : Int) -> Unit {
self.adjust((adjust, true), (adjust, false))
}
///| Grows the operation to the right by a given amount.
///| Used by the `Compact` hook to merge operations.
pub fn DiffOp::grow_right(self : Self, adjust : Int) -> Unit {
self.adjust((0, false), (adjust, false))
}
///| Shrinks the operation from the left by a given amount.
///| Used by the `Compact` hook to merge operations.
pub fn DiffOp::shrink_left(self : Self, adjust : Int) -> Unit {
self.adjust((0, false), (adjust, false))
}
///| Shrinks the operation from the right by a given amount.
///| Used by the `Compact` hook to merge operations.
pub fn DiffOp::shrink_right(self : Self, adjust : Int) -> Unit {
self.adjust((adjust, false), (adjust, true))
}
///| Helper function to adjust the offsets and lengths of an operation.
///| false means grow, true means shrink
fn DiffOp::adjust(
self : Self,
adjust_offset : (Int, Bool),
adjust_len : (Int, Bool),
) -> Unit {
fn modify(val : Int, adjust : (Int, Bool)) -> Int {
if adjust.1 {
val - adjust.0
} else {
val + adjust.0
}
}
match self {
DiffOp::Equal(old_index~, new_index~, len~) as op => {
op.old_index = modify(old_index, adjust_offset)
op.new_index = modify(new_index, adjust_offset)
op.len = modify(len, adjust_len)
}
DiffOp::Delete(old_index~, old_len~, new_index~) as op => {
op.old_index = modify(old_index, adjust_offset)
op.old_len = modify(old_len, adjust_len)
op.new_index = modify(new_index, adjust_offset)
}
DiffOp::Insert(old_index~, new_index~, new_len~) as op => {
op.old_index = modify(old_index, adjust_offset)
op.new_index = modify(new_index, adjust_offset)
op.new_len = modify(new_len, adjust_len)
}
DiffOp::Replace(old_index~, old_len~, new_index~, new_len~) as op => {
op.old_index = modify(old_index, adjust_offset)
op.old_len = modify(old_len, adjust_len)
op.new_index = modify(new_index, adjust_offset)
op.new_len = modify(new_len, adjust_len)
}
}
}