diff options
Diffstat (limited to 'vendor/github.com/pmezard/go-difflib')
| -rw-r--r-- | vendor/github.com/pmezard/go-difflib/LICENSE | 27 | ||||
| -rw-r--r-- | vendor/github.com/pmezard/go-difflib/difflib/difflib.go | 772 | 
2 files changed, 0 insertions, 799 deletions
diff --git a/vendor/github.com/pmezard/go-difflib/LICENSE b/vendor/github.com/pmezard/go-difflib/LICENSE deleted file mode 100644 index c67dad6..0000000 --- a/vendor/github.com/pmezard/go-difflib/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2013, Patrick Mezard -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -    Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. -    Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. -    The names of its contributors may not be used to endorse or promote -products derived from this software without specific prior written -permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go b/vendor/github.com/pmezard/go-difflib/difflib/difflib.go deleted file mode 100644 index 003e99f..0000000 --- a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go +++ /dev/null @@ -1,772 +0,0 @@ -// Package difflib is a partial port of Python difflib module. -// -// It provides tools to compare sequences of strings and generate textual diffs. -// -// The following class and functions have been ported: -// -// - SequenceMatcher -// -// - unified_diff -// -// - context_diff -// -// Getting unified diffs was the main goal of the port. Keep in mind this code -// is mostly suitable to output text differences in a human friendly way, there -// are no guarantees generated diffs are consumable by patch(1). -package difflib - -import ( -	"bufio" -	"bytes" -	"fmt" -	"io" -	"strings" -) - -func min(a, b int) int { -	if a < b { -		return a -	} -	return b -} - -func max(a, b int) int { -	if a > b { -		return a -	} -	return b -} - -func calculateRatio(matches, length int) float64 { -	if length > 0 { -		return 2.0 * float64(matches) / float64(length) -	} -	return 1.0 -} - -type Match struct { -	A    int -	B    int -	Size int -} - -type OpCode struct { -	Tag byte -	I1  int -	I2  int -	J1  int -	J2  int -} - -// SequenceMatcher compares sequence of strings. The basic -// algorithm predates, and is a little fancier than, an algorithm -// published in the late 1980's by Ratcliff and Obershelp under the -// hyperbolic name "gestalt pattern matching".  The basic idea is to find -// the longest contiguous matching subsequence that contains no "junk" -// elements (R-O doesn't address junk).  The same idea is then applied -// recursively to the pieces of the sequences to the left and to the right -// of the matching subsequence.  This does not yield minimal edit -// sequences, but does tend to yield matches that "look right" to people. -// -// SequenceMatcher tries to compute a "human-friendly diff" between two -// sequences.  Unlike e.g. UNIX(tm) diff, the fundamental notion is the -// longest *contiguous* & junk-free matching subsequence.  That's what -// catches peoples' eyes.  The Windows(tm) windiff has another interesting -// notion, pairing up elements that appear uniquely in each sequence. -// That, and the method here, appear to yield more intuitive difference -// reports than does diff.  This method appears to be the least vulnerable -// to synching up on blocks of "junk lines", though (like blank lines in -// ordinary text files, or maybe "<P>" lines in HTML files).  That may be -// because this is the only method of the 3 that has a *concept* of -// "junk" <wink>. -// -// Timing:  Basic R-O is cubic time worst case and quadratic time expected -// case.  SequenceMatcher is quadratic time for the worst case and has -// expected-case behavior dependent in a complicated way on how many -// elements the sequences have in common; best case time is linear. -type SequenceMatcher struct { -	a              []string -	b              []string -	b2j            map[string][]int -	IsJunk         func(string) bool -	autoJunk       bool -	bJunk          map[string]struct{} -	matchingBlocks []Match -	fullBCount     map[string]int -	bPopular       map[string]struct{} -	opCodes        []OpCode -} - -func NewMatcher(a, b []string) *SequenceMatcher { -	m := SequenceMatcher{autoJunk: true} -	m.SetSeqs(a, b) -	return &m -} - -func NewMatcherWithJunk(a, b []string, autoJunk bool, -	isJunk func(string) bool) *SequenceMatcher { - -	m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk} -	m.SetSeqs(a, b) -	return &m -} - -// Set two sequences to be compared. -func (m *SequenceMatcher) SetSeqs(a, b []string) { -	m.SetSeq1(a) -	m.SetSeq2(b) -} - -// Set the first sequence to be compared. The second sequence to be compared is -// not changed. -// -// SequenceMatcher computes and caches detailed information about the second -// sequence, so if you want to compare one sequence S against many sequences, -// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other -// sequences. -// -// See also SetSeqs() and SetSeq2(). -func (m *SequenceMatcher) SetSeq1(a []string) { -	if &a == &m.a { -		return -	} -	m.a = a -	m.matchingBlocks = nil -	m.opCodes = nil -} - -// Set the second sequence to be compared. The first sequence to be compared is -// not changed. -func (m *SequenceMatcher) SetSeq2(b []string) { -	if &b == &m.b { -		return -	} -	m.b = b -	m.matchingBlocks = nil -	m.opCodes = nil -	m.fullBCount = nil -	m.chainB() -} - -func (m *SequenceMatcher) chainB() { -	// Populate line -> index mapping -	b2j := map[string][]int{} -	for i, s := range m.b { -		indices := b2j[s] -		indices = append(indices, i) -		b2j[s] = indices -	} - -	// Purge junk elements -	m.bJunk = map[string]struct{}{} -	if m.IsJunk != nil { -		junk := m.bJunk -		for s, _ := range b2j { -			if m.IsJunk(s) { -				junk[s] = struct{}{} -			} -		} -		for s, _ := range junk { -			delete(b2j, s) -		} -	} - -	// Purge remaining popular elements -	popular := map[string]struct{}{} -	n := len(m.b) -	if m.autoJunk && n >= 200 { -		ntest := n/100 + 1 -		for s, indices := range b2j { -			if len(indices) > ntest { -				popular[s] = struct{}{} -			} -		} -		for s, _ := range popular { -			delete(b2j, s) -		} -	} -	m.bPopular = popular -	m.b2j = b2j -} - -func (m *SequenceMatcher) isBJunk(s string) bool { -	_, ok := m.bJunk[s] -	return ok -} - -// Find longest matching block in a[alo:ahi] and b[blo:bhi]. -// -// If IsJunk is not defined: -// -// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where -//     alo <= i <= i+k <= ahi -//     blo <= j <= j+k <= bhi -// and for all (i',j',k') meeting those conditions, -//     k >= k' -//     i <= i' -//     and if i == i', j <= j' -// -// In other words, of all maximal matching blocks, return one that -// starts earliest in a, and of all those maximal matching blocks that -// start earliest in a, return the one that starts earliest in b. -// -// If IsJunk is defined, first the longest matching block is -// determined as above, but with the additional restriction that no -// junk element appears in the block.  Then that block is extended as -// far as possible by matching (only) junk elements on both sides.  So -// the resulting block never matches on junk except as identical junk -// happens to be adjacent to an "interesting" match. -// -// If no blocks match, return (alo, blo, 0). -func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match { -	// CAUTION:  stripping common prefix or suffix would be incorrect. -	// E.g., -	//    ab -	//    acab -	// Longest matching block is "ab", but if common prefix is -	// stripped, it's "a" (tied with "b").  UNIX(tm) diff does so -	// strip, so ends up claiming that ab is changed to acab by -	// inserting "ca" in the middle.  That's minimal but unintuitive: -	// "it's obvious" that someone inserted "ac" at the front. -	// Windiff ends up at the same place as diff, but by pairing up -	// the unique 'b's and then matching the first two 'a's. -	besti, bestj, bestsize := alo, blo, 0 - -	// find longest junk-free match -	// during an iteration of the loop, j2len[j] = length of longest -	// junk-free match ending with a[i-1] and b[j] -	j2len := map[int]int{} -	for i := alo; i != ahi; i++ { -		// look at all instances of a[i] in b; note that because -		// b2j has no junk keys, the loop is skipped if a[i] is junk -		newj2len := map[int]int{} -		for _, j := range m.b2j[m.a[i]] { -			// a[i] matches b[j] -			if j < blo { -				continue -			} -			if j >= bhi { -				break -			} -			k := j2len[j-1] + 1 -			newj2len[j] = k -			if k > bestsize { -				besti, bestj, bestsize = i-k+1, j-k+1, k -			} -		} -		j2len = newj2len -	} - -	// Extend the best by non-junk elements on each end.  In particular, -	// "popular" non-junk elements aren't in b2j, which greatly speeds -	// the inner loop above, but also means "the best" match so far -	// doesn't contain any junk *or* popular non-junk elements. -	for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) && -		m.a[besti-1] == m.b[bestj-1] { -		besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 -	} -	for besti+bestsize < ahi && bestj+bestsize < bhi && -		!m.isBJunk(m.b[bestj+bestsize]) && -		m.a[besti+bestsize] == m.b[bestj+bestsize] { -		bestsize += 1 -	} - -	// Now that we have a wholly interesting match (albeit possibly -	// empty!), we may as well suck up the matching junk on each -	// side of it too.  Can't think of a good reason not to, and it -	// saves post-processing the (possibly considerable) expense of -	// figuring out what to do with it.  In the case of an empty -	// interesting match, this is clearly the right thing to do, -	// because no other kind of match is possible in the regions. -	for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) && -		m.a[besti-1] == m.b[bestj-1] { -		besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 -	} -	for besti+bestsize < ahi && bestj+bestsize < bhi && -		m.isBJunk(m.b[bestj+bestsize]) && -		m.a[besti+bestsize] == m.b[bestj+bestsize] { -		bestsize += 1 -	} - -	return Match{A: besti, B: bestj, Size: bestsize} -} - -// Return list of triples describing matching subsequences. -// -// Each triple is of the form (i, j, n), and means that -// a[i:i+n] == b[j:j+n].  The triples are monotonically increasing in -// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are -// adjacent triples in the list, and the second is not the last triple in the -// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe -// adjacent equal blocks. -// -// The last triple is a dummy, (len(a), len(b), 0), and is the only -// triple with n==0. -func (m *SequenceMatcher) GetMatchingBlocks() []Match { -	if m.matchingBlocks != nil { -		return m.matchingBlocks -	} - -	var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match -	matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match { -		match := m.findLongestMatch(alo, ahi, blo, bhi) -		i, j, k := match.A, match.B, match.Size -		if match.Size > 0 { -			if alo < i && blo < j { -				matched = matchBlocks(alo, i, blo, j, matched) -			} -			matched = append(matched, match) -			if i+k < ahi && j+k < bhi { -				matched = matchBlocks(i+k, ahi, j+k, bhi, matched) -			} -		} -		return matched -	} -	matched := matchBlocks(0, len(m.a), 0, len(m.b), nil) - -	// It's possible that we have adjacent equal blocks in the -	// matching_blocks list now. -	nonAdjacent := []Match{} -	i1, j1, k1 := 0, 0, 0 -	for _, b := range matched { -		// Is this block adjacent to i1, j1, k1? -		i2, j2, k2 := b.A, b.B, b.Size -		if i1+k1 == i2 && j1+k1 == j2 { -			// Yes, so collapse them -- this just increases the length of -			// the first block by the length of the second, and the first -			// block so lengthened remains the block to compare against. -			k1 += k2 -		} else { -			// Not adjacent.  Remember the first block (k1==0 means it's -			// the dummy we started with), and make the second block the -			// new block to compare against. -			if k1 > 0 { -				nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) -			} -			i1, j1, k1 = i2, j2, k2 -		} -	} -	if k1 > 0 { -		nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) -	} - -	nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0}) -	m.matchingBlocks = nonAdjacent -	return m.matchingBlocks -} - -// Return list of 5-tuples describing how to turn a into b. -// -// Each tuple is of the form (tag, i1, i2, j1, j2).  The first tuple -// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the -// tuple preceding it, and likewise for j1 == the previous j2. -// -// The tags are characters, with these meanings: -// -// 'r' (replace):  a[i1:i2] should be replaced by b[j1:j2] -// -// 'd' (delete):   a[i1:i2] should be deleted, j1==j2 in this case. -// -// 'i' (insert):   b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case. -// -// 'e' (equal):    a[i1:i2] == b[j1:j2] -func (m *SequenceMatcher) GetOpCodes() []OpCode { -	if m.opCodes != nil { -		return m.opCodes -	} -	i, j := 0, 0 -	matching := m.GetMatchingBlocks() -	opCodes := make([]OpCode, 0, len(matching)) -	for _, m := range matching { -		//  invariant:  we've pumped out correct diffs to change -		//  a[:i] into b[:j], and the next matching block is -		//  a[ai:ai+size] == b[bj:bj+size]. So we need to pump -		//  out a diff to change a[i:ai] into b[j:bj], pump out -		//  the matching block, and move (i,j) beyond the match -		ai, bj, size := m.A, m.B, m.Size -		tag := byte(0) -		if i < ai && j < bj { -			tag = 'r' -		} else if i < ai { -			tag = 'd' -		} else if j < bj { -			tag = 'i' -		} -		if tag > 0 { -			opCodes = append(opCodes, OpCode{tag, i, ai, j, bj}) -		} -		i, j = ai+size, bj+size -		// the list of matching blocks is terminated by a -		// sentinel with size 0 -		if size > 0 { -			opCodes = append(opCodes, OpCode{'e', ai, i, bj, j}) -		} -	} -	m.opCodes = opCodes -	return m.opCodes -} - -// Isolate change clusters by eliminating ranges with no changes. -// -// Return a generator of groups with up to n lines of context. -// Each group is in the same format as returned by GetOpCodes(). -func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { -	if n < 0 { -		n = 3 -	} -	codes := m.GetOpCodes() -	if len(codes) == 0 { -		codes = []OpCode{OpCode{'e', 0, 1, 0, 1}} -	} -	// Fixup leading and trailing groups if they show no changes. -	if codes[0].Tag == 'e' { -		c := codes[0] -		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 -		codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2} -	} -	if codes[len(codes)-1].Tag == 'e' { -		c := codes[len(codes)-1] -		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 -		codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)} -	} -	nn := n + n -	groups := [][]OpCode{} -	group := []OpCode{} -	for _, c := range codes { -		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 -		// End the current group and start a new one whenever -		// there is a large range with no changes. -		if c.Tag == 'e' && i2-i1 > nn { -			group = append(group, OpCode{c.Tag, i1, min(i2, i1+n), -				j1, min(j2, j1+n)}) -			groups = append(groups, group) -			group = []OpCode{} -			i1, j1 = max(i1, i2-n), max(j1, j2-n) -		} -		group = append(group, OpCode{c.Tag, i1, i2, j1, j2}) -	} -	if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') { -		groups = append(groups, group) -	} -	return groups -} - -// Return a measure of the sequences' similarity (float in [0,1]). -// -// Where T is the total number of elements in both sequences, and -// M is the number of matches, this is 2.0*M / T. -// Note that this is 1 if the sequences are identical, and 0 if -// they have nothing in common. -// -// .Ratio() is expensive to compute if you haven't already computed -// .GetMatchingBlocks() or .GetOpCodes(), in which case you may -// want to try .QuickRatio() or .RealQuickRation() first to get an -// upper bound. -func (m *SequenceMatcher) Ratio() float64 { -	matches := 0 -	for _, m := range m.GetMatchingBlocks() { -		matches += m.Size -	} -	return calculateRatio(matches, len(m.a)+len(m.b)) -} - -// Return an upper bound on ratio() relatively quickly. -// -// This isn't defined beyond that it is an upper bound on .Ratio(), and -// is faster to compute. -func (m *SequenceMatcher) QuickRatio() float64 { -	// viewing a and b as multisets, set matches to the cardinality -	// of their intersection; this counts the number of matches -	// without regard to order, so is clearly an upper bound -	if m.fullBCount == nil { -		m.fullBCount = map[string]int{} -		for _, s := range m.b { -			m.fullBCount[s] = m.fullBCount[s] + 1 -		} -	} - -	// avail[x] is the number of times x appears in 'b' less the -	// number of times we've seen it in 'a' so far ... kinda -	avail := map[string]int{} -	matches := 0 -	for _, s := range m.a { -		n, ok := avail[s] -		if !ok { -			n = m.fullBCount[s] -		} -		avail[s] = n - 1 -		if n > 0 { -			matches += 1 -		} -	} -	return calculateRatio(matches, len(m.a)+len(m.b)) -} - -// Return an upper bound on ratio() very quickly. -// -// This isn't defined beyond that it is an upper bound on .Ratio(), and -// is faster to compute than either .Ratio() or .QuickRatio(). -func (m *SequenceMatcher) RealQuickRatio() float64 { -	la, lb := len(m.a), len(m.b) -	return calculateRatio(min(la, lb), la+lb) -} - -// Convert range to the "ed" format -func formatRangeUnified(start, stop int) string { -	// Per the diff spec at http://www.unix.org/single_unix_specification/ -	beginning := start + 1 // lines start numbering with one -	length := stop - start -	if length == 1 { -		return fmt.Sprintf("%d", beginning) -	} -	if length == 0 { -		beginning -= 1 // empty ranges begin at line just before the range -	} -	return fmt.Sprintf("%d,%d", beginning, length) -} - -// Unified diff parameters -type UnifiedDiff struct { -	A        []string // First sequence lines -	FromFile string   // First file name -	FromDate string   // First file time -	B        []string // Second sequence lines -	ToFile   string   // Second file name -	ToDate   string   // Second file time -	Eol      string   // Headers end of line, defaults to LF -	Context  int      // Number of context lines -} - -// Compare two sequences of lines; generate the delta as a unified diff. -// -// Unified diffs are a compact way of showing line changes and a few -// lines of context.  The number of context lines is set by 'n' which -// defaults to three. -// -// By default, the diff control lines (those with ---, +++, or @@) are -// created with a trailing newline.  This is helpful so that inputs -// created from file.readlines() result in diffs that are suitable for -// file.writelines() since both the inputs and outputs have trailing -// newlines. -// -// For inputs that do not have trailing newlines, set the lineterm -// argument to "" so that the output will be uniformly newline free. -// -// The unidiff format normally has a header for filenames and modification -// times.  Any or all of these may be specified using strings for -// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'. -// The modification times are normally expressed in the ISO 8601 format. -func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error { -	buf := bufio.NewWriter(writer) -	defer buf.Flush() -	wf := func(format string, args ...interface{}) error { -		_, err := buf.WriteString(fmt.Sprintf(format, args...)) -		return err -	} -	ws := func(s string) error { -		_, err := buf.WriteString(s) -		return err -	} - -	if len(diff.Eol) == 0 { -		diff.Eol = "\n" -	} - -	started := false -	m := NewMatcher(diff.A, diff.B) -	for _, g := range m.GetGroupedOpCodes(diff.Context) { -		if !started { -			started = true -			fromDate := "" -			if len(diff.FromDate) > 0 { -				fromDate = "\t" + diff.FromDate -			} -			toDate := "" -			if len(diff.ToDate) > 0 { -				toDate = "\t" + diff.ToDate -			} -			if diff.FromFile != "" || diff.ToFile != "" { -				err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol) -				if err != nil { -					return err -				} -				err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol) -				if err != nil { -					return err -				} -			} -		} -		first, last := g[0], g[len(g)-1] -		range1 := formatRangeUnified(first.I1, last.I2) -		range2 := formatRangeUnified(first.J1, last.J2) -		if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil { -			return err -		} -		for _, c := range g { -			i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 -			if c.Tag == 'e' { -				for _, line := range diff.A[i1:i2] { -					if err := ws(" " + line); err != nil { -						return err -					} -				} -				continue -			} -			if c.Tag == 'r' || c.Tag == 'd' { -				for _, line := range diff.A[i1:i2] { -					if err := ws("-" + line); err != nil { -						return err -					} -				} -			} -			if c.Tag == 'r' || c.Tag == 'i' { -				for _, line := range diff.B[j1:j2] { -					if err := ws("+" + line); err != nil { -						return err -					} -				} -			} -		} -	} -	return nil -} - -// Like WriteUnifiedDiff but returns the diff a string. -func GetUnifiedDiffString(diff UnifiedDiff) (string, error) { -	w := &bytes.Buffer{} -	err := WriteUnifiedDiff(w, diff) -	return string(w.Bytes()), err -} - -// Convert range to the "ed" format. -func formatRangeContext(start, stop int) string { -	// Per the diff spec at http://www.unix.org/single_unix_specification/ -	beginning := start + 1 // lines start numbering with one -	length := stop - start -	if length == 0 { -		beginning -= 1 // empty ranges begin at line just before the range -	} -	if length <= 1 { -		return fmt.Sprintf("%d", beginning) -	} -	return fmt.Sprintf("%d,%d", beginning, beginning+length-1) -} - -type ContextDiff UnifiedDiff - -// Compare two sequences of lines; generate the delta as a context diff. -// -// Context diffs are a compact way of showing line changes and a few -// lines of context. The number of context lines is set by diff.Context -// which defaults to three. -// -// By default, the diff control lines (those with *** or ---) are -// created with a trailing newline. -// -// For inputs that do not have trailing newlines, set the diff.Eol -// argument to "" so that the output will be uniformly newline free. -// -// The context diff format normally has a header for filenames and -// modification times.  Any or all of these may be specified using -// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate. -// The modification times are normally expressed in the ISO 8601 format. -// If not specified, the strings default to blanks. -func WriteContextDiff(writer io.Writer, diff ContextDiff) error { -	buf := bufio.NewWriter(writer) -	defer buf.Flush() -	var diffErr error -	wf := func(format string, args ...interface{}) { -		_, err := buf.WriteString(fmt.Sprintf(format, args...)) -		if diffErr == nil && err != nil { -			diffErr = err -		} -	} -	ws := func(s string) { -		_, err := buf.WriteString(s) -		if diffErr == nil && err != nil { -			diffErr = err -		} -	} - -	if len(diff.Eol) == 0 { -		diff.Eol = "\n" -	} - -	prefix := map[byte]string{ -		'i': "+ ", -		'd': "- ", -		'r': "! ", -		'e': "  ", -	} - -	started := false -	m := NewMatcher(diff.A, diff.B) -	for _, g := range m.GetGroupedOpCodes(diff.Context) { -		if !started { -			started = true -			fromDate := "" -			if len(diff.FromDate) > 0 { -				fromDate = "\t" + diff.FromDate -			} -			toDate := "" -			if len(diff.ToDate) > 0 { -				toDate = "\t" + diff.ToDate -			} -			if diff.FromFile != "" || diff.ToFile != "" { -				wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol) -				wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol) -			} -		} - -		first, last := g[0], g[len(g)-1] -		ws("***************" + diff.Eol) - -		range1 := formatRangeContext(first.I1, last.I2) -		wf("*** %s ****%s", range1, diff.Eol) -		for _, c := range g { -			if c.Tag == 'r' || c.Tag == 'd' { -				for _, cc := range g { -					if cc.Tag == 'i' { -						continue -					} -					for _, line := range diff.A[cc.I1:cc.I2] { -						ws(prefix[cc.Tag] + line) -					} -				} -				break -			} -		} - -		range2 := formatRangeContext(first.J1, last.J2) -		wf("--- %s ----%s", range2, diff.Eol) -		for _, c := range g { -			if c.Tag == 'r' || c.Tag == 'i' { -				for _, cc := range g { -					if cc.Tag == 'd' { -						continue -					} -					for _, line := range diff.B[cc.J1:cc.J2] { -						ws(prefix[cc.Tag] + line) -					} -				} -				break -			} -		} -	} -	return diffErr -} - -// Like WriteContextDiff but returns the diff a string. -func GetContextDiffString(diff ContextDiff) (string, error) { -	w := &bytes.Buffer{} -	err := WriteContextDiff(w, diff) -	return string(w.Bytes()), err -} - -// Split a string on "\n" while preserving them. The output can be used -// as input for UnifiedDiff and ContextDiff structures. -func SplitLines(s string) []string { -	lines := strings.SplitAfter(s, "\n") -	lines[len(lines)-1] += "\n" -	return lines -}  | 
