Skip to content

Commit

Permalink
myers: create package
Browse files Browse the repository at this point in the history
Part of #18.
  • Loading branch information
josharian committed Dec 30, 2019
1 parent 7cc3ed0 commit f932f2c
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 30 deletions.
3 changes: 2 additions & 1 deletion cmd/pkg-diff-example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"

"github.com/pkg/diff"
"github.com/pkg/diff/myers"
)

var (
Expand Down Expand Up @@ -73,7 +74,7 @@ func main() {
ctx, cancel = context.WithTimeout(ctx, *timeout)
defer cancel()
}
e := diff.Myers(ctx, ab)
e := myers.Diff(ctx, ab)
e = diff.EditScriptWithContextSize(e, *unified) // limit amount of output context
opts := []diff.WriteOpt{
diff.Names(aName, bName),
Expand Down
22 changes: 2 additions & 20 deletions diff.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,11 @@
package diff

import (
"fmt"
"io"

"github.com/pkg/diff/edit"
"github.com/pkg/diff/myers"
)

// A Pair is two things that can be diffed using the Myers diff algorithm.
// A is the initial state; B is the final state.
type Pair interface {
// LenA returns the number of initial elements.
LenA() int
// LenA returns the number of final elements.
LenB() int
// Equal reports whether the ai'th element of A is equal to the bi'th element of B.
Equal(ai, bi int) bool
}

// A WriterTo type supports writing a diff, element by element.
// A is the initial state; B is the final state.
type WriterTo interface {
Expand All @@ -29,7 +17,7 @@ type WriterTo interface {

// PairWriterTo is the union of Pair and WriterTo.
type PairWriterTo interface {
Pair
myers.Pair
WriterTo
}

Expand All @@ -49,12 +37,6 @@ type PairWriterTo interface {
// If you have paid the O(n) cost to intern all strings involved in both A and B,
// then string comparisons are reduced to cheap pointer comparisons.

func rangeString(r edit.Range) string {
// This output is helpful when hacking on a Myers diff.
// In other contexts it is usually more natural to group LowA, HighA and LowB, HighB.
return fmt.Sprintf("(%d, %d) -- %s %d --> (%d, %d)", r.LowA, r.LowB, r.Op(), r.Len(), r.HighA, r.HighB)
}

// TODO: consider adding an "it just works" test helper that accepts two slices (via interface{}),
// diffs them using Strings or Bytes or Slices (using reflect.DeepEqual) as appropriate,
// and calls t.Errorf with a generated diff if they're not equal.
7 changes: 4 additions & 3 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"

"github.com/pkg/diff"
"github.com/pkg/diff/myers"
)

// TODO: use a less heavyweight output format for Example_testHelper
Expand All @@ -13,7 +14,7 @@ func Example_testHelper() {
want := []int{1, 2, 3, 4, 5}
got := []int{1, 2, 4, 5}
ab := diff.Slices(want, got, nil)
e := diff.Myers(context.Background(), ab)
e := myers.Diff(context.Background(), ab)
if e.IsIdentity() {
return
}
Expand All @@ -32,7 +33,7 @@ func Example_strings() {
a := []string{"a", "b", "c"}
b := []string{"a", "c", "d"}
ab := diff.Strings(a, b)
e := diff.Myers(context.Background(), ab)
e := myers.Diff(context.Background(), ab)
diff.WriteUnified(e, os.Stdout, ab)
// Output:
// --- a
Expand All @@ -48,7 +49,7 @@ func Example_Names() {
a := []string{"a", "b", "c"}
b := []string{"a", "c", "d"}
ab := diff.Strings(a, b)
e := diff.Myers(context.Background(), ab)
e := myers.Diff(context.Background(), ab)
diff.WriteUnified(e, os.Stdout, ab, diff.Names("before", "after"))
// Output:
// --- before
Expand Down
24 changes: 21 additions & 3 deletions myers.go → myers/myers.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package diff
// Package myers implements the Myers diff algorithm.
package myers

import (
"context"
Expand All @@ -7,9 +8,20 @@ import (
"github.com/pkg/diff/edit"
)

// Myers calculates an edit.Script (diff) for ab using the Myers diff algorithm.
// A Pair is two things that can be diffed using the Myers diff algorithm.
// A is the initial state; B is the final state.
type Pair interface {
// LenA returns the number of initial elements.
LenA() int
// LenB returns the number of final elements.
LenB() int
// Equal reports whether the aᵢ'th element of A is equal to the bᵢ'th element of B.
Equal(ai, bi int) bool
}

// Diff calculates an edit.Script for ab using the Myers diff algorithm.
// Because diff calculation can be expensive, Myers supports cancellation via ctx.
func Myers(ctx context.Context, ab Pair) edit.Script {
func Diff(ctx context.Context, ab Pair) edit.Script {
aLen := ab.LenA()
bLen := ab.LenB()
if aLen == 0 {
Expand Down Expand Up @@ -153,3 +165,9 @@ func combineRanges(s, t edit.Range) (u edit.Range, ok bool) {
}
return s, true
}

func rangeString(r edit.Range) string {
// This output is helpful when hacking on a Myers diff.
// In other contexts it is usually more natural to group LowA, HighA and LowB, HighB.
return fmt.Sprintf("(%d, %d) -- %s %d --> (%d, %d)", r.LowA, r.LowB, r.Op(), r.Len(), r.HighA, r.HighB)
}
5 changes: 3 additions & 2 deletions myers_test.go → myers/myers_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package diff
package myers_test

import (
"context"
"reflect"
"testing"

"github.com/pkg/diff/edit"
"github.com/pkg/diff/myers"
)

func TestMyers(t *testing.T) {
Expand Down Expand Up @@ -49,7 +50,7 @@ func TestMyers(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ab := &diffByByte{a: test.a, b: test.b}
got := Myers(context.Background(), ab)
got := myers.Diff(context.Background(), ab)
want := edit.Script{Ranges: test.want}

if !reflect.DeepEqual(got, want) {
Expand Down
3 changes: 2 additions & 1 deletion unified_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/pkg/diff"
"github.com/pkg/diff/myers"
"github.com/sergi/go-diff/diffmatchpatch"
)

Expand Down Expand Up @@ -80,7 +81,7 @@ func TestGolden(t *testing.T) {
// TODO: supply an edit.Script to the tests instead doing a Myers diff here.
// Doing it as I have done, the lazy way, mixes concerns: diff algorithm vs unification algorithm
// vs unified diff formatting.
e := diff.Myers(context.Background(), ab)
e := myers.Diff(context.Background(), ab)
e = diff.EditScriptWithContextSize(e, 3)
buf := new(bytes.Buffer)
diff.WriteUnified(e, buf, ab, test.opts...)
Expand Down

0 comments on commit f932f2c

Please sign in to comment.