Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add options to Diff and DiffChunk. #25

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions diff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ import (
"strings"
)

type options struct {
transform func(string) string
}

// An Option will change the behavior of the diff.
type Option func(*options)

// The Transform option alters lines before comparing.
// You can use it to ignore certain differences.
func Transform(tr func(string) string) Option {
return func(in *options) {
in.transform = tr
}
}

// Chunk represents a piece of the diff. A chunk will not have both added and
// deleted lines. Equal lines are always after any added or deleted lines.
// A Chunk may or may not have any lines in it, especially for the first or last
Expand All @@ -37,10 +52,10 @@ func (c *Chunk) empty() bool {
// Diff returns a string containing a line-by-line unified diff of the linewise
// changes required to make A into B. Each line is prefixed with '+', '-', or
// ' ' to indicate if it should be added, removed, or is correct respectively.
func Diff(A, B string) string {
func Diff(A, B string, opts ...Option) string {
aLines := strings.Split(A, "\n")
bLines := strings.Split(B, "\n")
return Render(DiffChunks(aLines, bLines))
return Render(DiffChunks(aLines, bLines, opts...))
}

// Render renders the slice of chunks into a representation that prefixes
Expand All @@ -65,12 +80,31 @@ func Render(chunks []Chunk) string {
// DiffChunks uses an O(D(N+M)) shortest-edit-script algorithm
// to compute the edits required from A to B and returns the
// edit chunks.
func DiffChunks(a, b []string) []Chunk {
func DiffChunks(a, b []string, opts ...Option) []Chunk {
// algorithm: http://www.xmailserver.org/diff2.pdf

options := &options{}
for _, o := range opts {
o(options)
}

// We'll need these quantities a lot.
alen, blen := len(a), len(b) // M, N

applyTransform := func(in []string) []string {
if options.transform == nil {
return in
}
out := make([]string, len(in))
for i, s := range in {
out[i] = options.transform(s)
}
return out
}

aa := applyTransform(a)
bb := applyTransform(b)

// At most, it will require len(a) deletions and len(b) additions
// to transform a into b.
maxPath := alen + blen // MAX
Expand Down Expand Up @@ -125,7 +159,7 @@ dLoop:
// On diagonal d, we can compute bidx from aidx.
bidx := aidx - diag // y
// See how far we can go on this diagonal before we find a difference.
for aidx < alen && bidx < blen && a[aidx] == b[bidx] {
for aidx < alen && bidx < blen && aa[aidx] == bb[bidx] {
aidx++
bidx++
}
Expand Down
28 changes: 28 additions & 0 deletions diff/diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,31 @@ States of America.
// and our Posterity, do ordain and establish this Constitution for the United
// States of America.
}

func TestDiffTransform(t *testing.T) {
a := strings.TrimSpace(`
10:01 Line 1
10:01 Line 2
10:01 Line 3
`)
b := strings.TrimSpace(`
10:15 Line 1
10:15 Line 2a
10:15 Line 3
`)
want := ` 10:01 Line 1
-10:01 Line 2
+10:15 Line 2a
10:01 Line 3`
got := Diff(a, b, Transform(
func(in string) string {
if len(in) < 6 {
return in
}
return in[6:]
}))
if got != want {
t.Errorf("GOT\n%#v\n", got)
t.Errorf("WANT\n%#v\n", want)
}
}