Diff Optimization
Diff Optimization
This guide shows how to clean up a raw diff — a list of DiffOperation
values (see Diff for how those are built) — into a canonical,
minimal form using the post-processing passes in the DiffOptimization
sub-namespace. Each pass implements
IDiffOptimizationOperation and mutates the diff list in place, so passes
can be chained in sequence to progressively simplify the result.
The optimization contract
IDiffOptimizationOperation defines a single method, Execute(diffs), that
normalizes a mutable list of DiffOperation values in place while
preserving the source and destination texts the diff represents. Every
optimizer in this sub-namespace implements this interface, so they can be
invoked interchangeably or composed in a pipeline.
List<DiffOperation> diffs = new List<DiffOperation>
{
new DiffOperation(Operation.Equal, "The quick "),
new DiffOperation(Operation.Delete, "brown "),
new DiffOperation(Operation.Insert, "red "),
new DiffOperation(Operation.Equal, "fox")
};
IDiffOptimizationOperation optimizer = new OperationsMerger(EditOperationsOrder.DeleteFirst);
optimizer.Execute(diffs);Merging adjacent operations
OperationsMerger merges a diff into its canonical minimal form: it
coalesces adjacent runs of the same operation kind, and for a mixed
delete/insert run it factors any common prefix into the preceding equality
and any common suffix into the following equality.
var merger = new OperationsMerger(EditOperationsOrder.DeleteFirst);
merger.Execute(diffs);Eliminating short equalities
MergingOptimizer performs a semantic clean-up pass: it eliminates
equalities that are no larger than the edits surrounding them, folding those
short common runs back into the adjacent delete/insert, and then re-merges
the result into canonical form.
var semanticOptimizer = new MergingOptimizer(EditOperationsOrder.InsertFirst);
semanticOptimizer.Execute(diffs);Sliding edits across equalities
OperationsSlideMerger shifts a single edit that is surrounded on both
sides by equalities sideways, eliminating one of those equalities and
further canonicalizing the diff.
IDiffOptimizationOperation slideMerger = new OperationsSlideMerger();
slideMerger.Execute(diffs);Controlling delete/insert order
When a delete and an insert are emitted for the same position, the
EditOperationsOrder enum controls which one an optimizer places first in
the resulting operation sequence: DeleteFirst or InsertFirst.
var order = EditOperationsOrder.DeleteFirst;
var optimizer = new MergingOptimizer(order);
optimizer.Execute(diffs);Tips and Best Practices
- Run
OperationsMergerfirst to coalesce raw edits before applying the semantic passes —MergingOptimizerandOperationsSlideMergerboth assume adjacent same-kind operations have already been combined. - Pick one
EditOperationsOrdervalue and use it consistently across every optimizer in a pipeline; mixingDeleteFirstandInsertFirstbetween passes can undo the ordering a previous pass established. - All three optimizers mutate the
List<DiffOperation>in place — clone the list first if you need to keep the unoptimized diff for comparison. - Because every optimizer implements
IDiffOptimizationOperation, you can hold a pipeline asIDiffOptimizationOperation[]and callExecuteon each in a loop rather than hardcoding each pass by name. - Apply
OperationsSlideMergerafter the merge passes — sliding an edit is only useful once adjacent runs of the same kind are already coalesced.
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| Diff still contains tiny fragmented equalities | Only OperationsMerger was run; short equalities between edits were never folded | Also run MergingOptimizer after OperationsMerger |
| Delete/insert order flips unexpectedly between passes | Different EditOperationsOrder values were passed to different optimizers | Use the same EditOperationsOrder value for every optimizer in the pipeline |
| Optimizer appears to have no effect | The diff list was already in canonical form, or a copy of the list was optimized instead of the original reference | Confirm you are passing the same List<DiffOperation> reference that downstream code reads |
FAQ
What does “canonical form” mean for a diff?
It means adjacent operations of the same kind have been coalesced and any common prefix/suffix in mixed delete/insert runs has been folded into the neighboring equality, so the sequence has no redundant fragmentation.
Do I have to use all three optimizers?
No. IDiffOptimizationOperation lets you apply just the passes you need,
but running OperationsMerger before the semantic passes (MergingOptimizer,
OperationsSlideMerger) gives the most consistent results.
What is the difference between OperationsMerger and MergingOptimizer?
OperationsMerger performs structural coalescing (same-kind runs, common
prefix/suffix). MergingOptimizer goes further and folds short equalities
surrounded by edits back into the adjacent delete/insert before re-merging.
Does EditOperationsOrder change what the diff represents?
No. It only controls the emission order of a delete and an insert that apply to the same position — the resulting text reconstruction is unaffected.
API Reference Summary
| Class / Method | Description |
|---|---|
IDiffOptimizationOperation | Interface for a post-processing pass that normalizes a diff in place |
IDiffOptimizationOperation.Execute(diffs) | Runs the optimization pass over a mutable list of DiffOperation values |
OperationsMerger | Coalesces adjacent same-kind operations and factors common prefix/suffix into equalities |
MergingOptimizer | Eliminates short equalities surrounded by edits, then re-merges into canonical form |
OperationsSlideMerger | Shifts an edit surrounded by equalities to eliminate one of them |
EditOperationsOrder | Enum controlling whether DeleteFirst or InsertFirst is emitted at a shared position |