-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
package diff now has a simple, single-call API. for more control, people can use the subpackages. Part of #18.
- Loading branch information
Showing
7 changed files
with
211 additions
and
199 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,136 @@ | ||
// Package diff and its subpackages create, modify, and print diffs. | ||
// | ||
// Package diff contains high level routines that generate a textual diff. | ||
// It is implemented in terms of the diff/* subpackages. | ||
// If you want fine-grained control, | ||
// want to inspect an diff programmatically, | ||
// want to provide a context for cancellation, | ||
// need to diff gigantic files that don't fit in memory, | ||
// or want to diff unusual things, | ||
// use the subpackages. | ||
package diff | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"io" | ||
"os" | ||
"reflect" | ||
"strings" | ||
|
||
"github.com/pkg/diff/ctxt" | ||
"github.com/pkg/diff/myers" | ||
"github.com/pkg/diff/write" | ||
) | ||
|
||
// lines returns the lines contained in text/filename. | ||
// text and filename are interpreted as described in the docs for Text. | ||
func lines(filename string, text interface{}) ([]string, error) { | ||
var r io.Reader | ||
switch text := text.(type) { | ||
case nil: | ||
f, err := os.Open(filename) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer f.Close() | ||
r = f | ||
case string: | ||
r = strings.NewReader(text) | ||
case []byte: | ||
r = bytes.NewReader(text) | ||
case io.Reader: | ||
r = text | ||
default: | ||
return nil, fmt.Errorf("unexpected type %T, want string, []byte, io.Reader, or nil", text) | ||
} | ||
var x []string | ||
scan := bufio.NewScanner(r) | ||
for scan.Scan() { | ||
// TODO: intern? See intern comment in todo.go. | ||
x = append(x, scan.Text()) | ||
} | ||
return x, scan.Err() | ||
} | ||
|
||
// addNames adds a Names write.Option using aName and bName, | ||
// taking care to put it at the end, | ||
// so as not to overwrite any competing option. | ||
func addNames(aName, bName string, options []write.Option) []write.Option { | ||
opts := make([]write.Option, len(options)+1) | ||
opts[0] = write.Names(aName, bName) | ||
copy(opts[1:], options) | ||
return opts | ||
} | ||
|
||
// Text diffs a and b and writes the result to w. | ||
// It treats a and b as text, and splits them into lines using bufio.ScanLines. | ||
// aFile and bFile are filenames to use in the output. | ||
// a and b each may be nil or may have type string, []byte, or io.Reader. | ||
// If nil, the text is read from the filename. | ||
func Text(aFile, bFile string, a, b interface{}, w io.Writer, options ...write.Option) error { | ||
aLines, err := lines(aFile, a) | ||
if err != nil { | ||
return err | ||
} | ||
bLines, err := lines(bFile, b) | ||
if err != nil { | ||
return err | ||
} | ||
ab := &diffStrings{a: aLines, b: bLines} | ||
s := myers.Diff(context.Background(), ab) | ||
s = ctxt.Size(s, 3) | ||
opts := addNames(aFile, bFile, options) | ||
_, err = write.Unified(s, w, ab, opts...) | ||
return err | ||
} | ||
|
||
type diffStrings struct { | ||
a, b []string | ||
} | ||
|
||
func (ab *diffStrings) LenA() int { return len(ab.a) } | ||
func (ab *diffStrings) LenB() int { return len(ab.b) } | ||
func (ab *diffStrings) Equal(ai, bi int) bool { return ab.a[ai] == ab.b[bi] } | ||
func (ab *diffStrings) WriteATo(w io.Writer, i int) (int, error) { return io.WriteString(w, ab.a[i]) } | ||
func (ab *diffStrings) WriteBTo(w io.Writer, i int) (int, error) { return io.WriteString(w, ab.b[i]) } | ||
|
||
// Slices diffs slices a and b and writes the result to w. | ||
// It uses fmt.Print to print the elements of a and b. | ||
// It uses reflect.DeepEqual to compare elements of a and b. | ||
// It uses aName and bName as the names of a and b in the output. | ||
func Slices(aName, bName string, a, b interface{}, w io.Writer, options ...write.Option) error { | ||
ab := &diffSlices{a: reflect.ValueOf(a), b: reflect.ValueOf(b)} | ||
if err := ab.validateTypes(); err != nil { | ||
return err | ||
} | ||
s := myers.Diff(context.Background(), ab) | ||
s = ctxt.Size(s, 3) | ||
opts := addNames(aName, bName, options) | ||
_, err := write.Unified(s, w, ab, opts...) | ||
return err | ||
} | ||
|
||
type diffSlices struct { | ||
a, b reflect.Value | ||
} | ||
|
||
func (ab *diffSlices) LenA() int { return ab.a.Len() } | ||
func (ab *diffSlices) LenB() int { return ab.b.Len() } | ||
func (ab *diffSlices) atA(i int) interface{} { return ab.a.Index(i).Interface() } | ||
func (ab *diffSlices) atB(i int) interface{} { return ab.b.Index(i).Interface() } | ||
func (ab *diffSlices) Equal(ai, bi int) bool { return reflect.DeepEqual(ab.atA(ai), ab.atB(bi)) } | ||
func (ab *diffSlices) WriteATo(w io.Writer, i int) (int, error) { return fmt.Fprint(w, ab.atA(i)) } | ||
func (ab *diffSlices) WriteBTo(w io.Writer, i int) (int, error) { return fmt.Fprint(w, ab.atB(i)) } | ||
|
||
func (ab *diffSlices) validateTypes() error { | ||
if t := ab.a.Type(); t.Kind() != reflect.Slice { | ||
return fmt.Errorf("a has type %v, must be a slice", t) | ||
} | ||
if t := ab.b.Type(); t.Kind() != reflect.Slice { | ||
return fmt.Errorf("b has type %v, must be a slice", t) | ||
} | ||
return nil | ||
} |
Oops, something went wrong.