Skip to content

Replace DiffPlex with a text diff API in DiffEngine - #909

Merged
SimonCropp merged 5 commits into
mainfrom
remove-diffplex
Sep 30, 2026
Merged

SimonCropp merged 5 commits into
mainfrom
remove-diffplex

Conversation

@SimonCropp

Copy link
Copy Markdown
Member

Removes the DiffPlex dependency. DiffEngine now has its own public line diff, which the viewer uses, and which Verify can use to replace Verify.DiffPlex.

API

public static class TextDiff
{
    public static IReadOnlyList<DiffLine> Compute(string expected, string received);
    public static string Format(string expected, string received, TextDiffFormat format = TextDiffFormat.Compact);
}

public enum DiffLineKind { Unchanged, Removed, Added }
public record DiffLine(DiffLineKind Kind, string Text, int? ExpectedLine, int? ReceivedLine);
public enum TextDiffFormat { Full, Compact, Minimal }
  • Compute returns lines in inline order: unchanged lines, and each changed block as its removed lines followed by its added lines.
  • Format produces the three layouts Verify.DiffPlex has, including [BOF]/[EOF] and zero-padded line numbers in Compact.
  • It compiles for every DiffEngine target, net462 included.

Implementation

  • Myers O((N+M)D), linear-space middle-snake recursion.
  • Works on spans: lines are ranges of the original strings, and equal lines get the same number from a span-keyed hash table, so no line is copied to compare it.
  • Buffers come from the stack for small inputs and from ArrayPool otherwise.
  • Compute makes a string only for each line it returns, and Format appends spans directly.

Differences from Verify.DiffPlex

  • Whitespace and case count. Verify.DiffPlex ignored whitespace, so a failure caused only by whitespace showed no changed lines.
  • Only trailing line breaks are trimmed from Format output, not trailing spaces, because a trailing space can now be the difference being reported.
  • Line endings still don't count: the same lines under \r\n and \n compare as unchanged.

Packages and tests

  • DiffPlex.dll is removed from every package: six copies in DiffEngine, two in the tray, one in each viewer package. The package listing snapshots are updated to match, and nothing else in them changed.
  • Verify.DiffPlex is removed from the test projects.
  • No existing viewer snapshot changed (renderer, scroll, minimal view and pixel).
  • New TextDiffTests cover edge cases, all three formats and a 20,000-line input. A fuzz test checks 3,000 random cases against a brute-force answer, confirming every diff is as small as possible and rebuilds both texts exactly.
  • New DiffRowsTests pin how the viewer pairs lines side by side.

Follow-up (Verify repos)

  • Use TextDiff.Format as Verify's default string comparer.
  • Deprecate Verify.DiffPlex.

Add a public, dependency free line diff to DiffEngine (TextDiff.Compute and
TextDiff.Format with Full, Compact and Minimal layouts), and have the viewer
use it instead of DiffPlex. DiffPlex.dll no longer ships in any package, and
Verify.DiffPlex is removed from the test projects.

The diff is Myers in its linear space form, over spans of the original
strings: lines are ranges rather than copies, equal lines are numbered by a
span keyed table, and buffers come from the stack or ArrayPool. Lines compare
exactly, whitespace and case included.
@SimonCropp SimonCropp added this to the 20.6.0 milestone Sep 30, 2026
@SimonCropp
SimonCropp merged commit 2c38d0d into main Sep 30, 2026
9 of 10 checks passed
@SimonCropp
SimonCropp deleted the remove-diffplex branch September 30, 2026 01:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant