Gumbo.Patches.Diff (gumbo v0.3.0)

Copy Markdown View Source

Unified diffs for the vendored-parser patch generators.

A port of Python's difflib.SequenceMatcher and difflib.unified_diff, autojunk included. It exists for one reason: a regenerated .patch must be the same file the Python generators produced, byte for byte. A diff that is only equivalent would rewrite every patch the first time it ran, and those patches are both the build's input and the record of what each one changes and why.

Only the parts unified_diff uses are ported, with the simplification that follows from our callers passing isjunk = None: nothing is junk, so the junk bookkeeping is absent and the loops of find_longest_match that extend a match over junk collapse into equality checks. autojunk is kept, because it is what makes difflib ignore lines appearing in more than 1% of a long file, and the vendored sources are long enough for it to matter.

Each function names the difflib function it ports. The port is verified against the committed patches: regenerating them must reproduce the files byte for byte, and make check-generators fails when it does not.

Summary

Functions

Splits text into lines with their terminators, as str.splitlines(keepends=True) does.

The unified diff from a to b, rendered as difflib.unified_diff renders it: ---/+++ labels, then one @@ hunk per change cluster, with context lines around each.

Functions

format_range(start, stop)

grouped_opcodes(a, b, n)

index(b)

longest_match(a, b, index, alo, ahi, blo, bhi)

matching_blocks(a, b)

opcodes(a, b)

splitlines(text)

@spec splitlines(binary()) :: [binary()]

Splits text into lines with their terminators, as str.splitlines(keepends=True) does.

That function also breaks on \v, \f, \x1c-\x1e, \x85 and \u2028/\u2029, which this does not. The vendored C sources contain none of them, and the generators refuse to run when a file gains one rather than quietly producing a different diff.

unified(a, b, from_file, to_file, context \\ 3)

@spec unified([binary()], [binary()], binary(), binary(), pos_integer()) :: binary()

The unified diff from a to b, rendered as difflib.unified_diff renders it: ---/+++ labels, then one @@ hunk per change cluster, with context lines around each.

Lines carry their terminators, as splitlines(keepends=True) gives them, so a final line without a newline stays without one. splitlines/1 produces that shape.