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 OperationsMerger first to coalesce raw edits before applying the semantic passes — MergingOptimizer and OperationsSlideMerger both assume adjacent same-kind operations have already been combined.
  • Pick one EditOperationsOrder value and use it consistently across every optimizer in a pipeline; mixing DeleteFirst and InsertFirst between 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 as IDiffOptimizationOperation[] and call Execute on each in a loop rather than hardcoding each pass by name.
  • Apply OperationsSlideMerger after the merge passes — sliding an edit is only useful once adjacent runs of the same kind are already coalesced.

Common Issues

IssueCauseFix
Diff still contains tiny fragmented equalitiesOnly OperationsMerger was run; short equalities between edits were never foldedAlso run MergingOptimizer after OperationsMerger
Delete/insert order flips unexpectedly between passesDifferent EditOperationsOrder values were passed to different optimizersUse the same EditOperationsOrder value for every optimizer in the pipeline
Optimizer appears to have no effectThe diff list was already in canonical form, or a copy of the list was optimized instead of the original referenceConfirm 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 / MethodDescription
IDiffOptimizationOperationInterface 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
OperationsMergerCoalesces adjacent same-kind operations and factors common prefix/suffix into equalities
MergingOptimizerEliminates short equalities surrounded by edits, then re-merges into canonical form
OperationsSlideMergerShifts an edit surrounded by equalities to eliminate one of them
EditOperationsOrderEnum controlling whether DeleteFirst or InsertFirst is emitted at a shared position

See Also