summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/sergi
diff options
context:
space:
mode:
authorMura Li <typeless@users.noreply.github.com>2019-03-27 19:15:23 +0800
committerLunny Xiao <xiaolunwen@gmail.com>2019-03-27 19:15:23 +0800
commitd77176912bccf1dc0ad93366df55f00fee23b498 (patch)
tree309fc6350f77f4061360160b88343360d45d5d24 /vendor/github.com/sergi
parentd578b71d61ee8131e8abf7f538b93d8c6cc6fe6d (diff)
downloadgitea-d77176912bccf1dc0ad93366df55f00fee23b498.tar.gz
gitea-d77176912bccf1dc0ad93366df55f00fee23b498.zip
Use Go1.11 module (#5743)
* Migrate to go modules * make vendor * Update mvdan.cc/xurls * make vendor * Update code.gitea.io/git * make fmt-check * Update github.com/go-sql-driver/mysql * make vendor
Diffstat (limited to 'vendor/github.com/sergi')
-rw-r--r--vendor/github.com/sergi/go-diff/AUTHORS25
-rw-r--r--vendor/github.com/sergi/go-diff/CONTRIBUTORS32
-rw-r--r--vendor/github.com/sergi/go-diff/LICENSE (renamed from vendor/github.com/sergi/go-diff/LICENSE.txt)2
-rw-r--r--vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go (renamed from vendor/github.com/sergi/go-diff/diffmatchpatch/dmp.go)1160
-rw-r--r--vendor/github.com/sergi/go-diff/diffmatchpatch/diffmatchpatch.go46
-rw-r--r--vendor/github.com/sergi/go-diff/diffmatchpatch/match.go160
-rw-r--r--vendor/github.com/sergi/go-diff/diffmatchpatch/mathutil.go23
-rw-r--r--vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go556
-rw-r--r--vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go88
9 files changed, 1061 insertions, 1031 deletions
diff --git a/vendor/github.com/sergi/go-diff/AUTHORS b/vendor/github.com/sergi/go-diff/AUTHORS
new file mode 100644
index 0000000000..2d7bb2bf57
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/AUTHORS
@@ -0,0 +1,25 @@
+# This is the official list of go-diff authors for copyright purposes.
+# This file is distinct from the CONTRIBUTORS files.
+# See the latter for an explanation.
+
+# Names should be added to this file as
+# Name or Organization <email address>
+# The email address is not required for organizations.
+
+# Please keep the list sorted.
+
+Danny Yoo <dannyyoo@google.com>
+James Kolb <jkolb@google.com>
+Jonathan Amsterdam <jba@google.com>
+Markus Zimmermann <markus.zimmermann@nethead.at> <markus.zimmermann@symflower.com> <zimmski@gmail.com>
+Matt Kovars <akaskik@gmail.com>
+Örjan Persson <orjan@spotify.com>
+Osman Masood <oamasood@gmail.com>
+Robert Carlsen <rwcarlsen@gmail.com>
+Rory Flynn <roryflynn@users.noreply.github.com>
+Sergi Mansilla <sergi.mansilla@gmail.com>
+Shatrugna Sadhu <ssadhu@apcera.com>
+Shawn Smith <shawnpsmith@gmail.com>
+Stas Maksimov <maksimov@gmail.com>
+Tor Arvid Lund <torarvid@gmail.com>
+Zac Bergquist <zbergquist99@gmail.com>
diff --git a/vendor/github.com/sergi/go-diff/CONTRIBUTORS b/vendor/github.com/sergi/go-diff/CONTRIBUTORS
new file mode 100644
index 0000000000..369e3d5519
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/CONTRIBUTORS
@@ -0,0 +1,32 @@
+# This is the official list of people who can contribute
+# (and typically have contributed) code to the go-diff
+# repository.
+#
+# The AUTHORS file lists the copyright holders; this file
+# lists people. For example, ACME Inc. employees would be listed here
+# but not in AUTHORS, because ACME Inc. would hold the copyright.
+#
+# When adding J Random Contributor's name to this file,
+# either J's name or J's organization's name should be
+# added to the AUTHORS file.
+#
+# Names should be added to this file like so:
+# Name <email address>
+#
+# Please keep the list sorted.
+
+Danny Yoo <dannyyoo@google.com>
+James Kolb <jkolb@google.com>
+Jonathan Amsterdam <jba@google.com>
+Markus Zimmermann <markus.zimmermann@nethead.at> <markus.zimmermann@symflower.com> <zimmski@gmail.com>
+Matt Kovars <akaskik@gmail.com>
+Örjan Persson <orjan@spotify.com>
+Osman Masood <oamasood@gmail.com>
+Robert Carlsen <rwcarlsen@gmail.com>
+Rory Flynn <roryflynn@users.noreply.github.com>
+Sergi Mansilla <sergi.mansilla@gmail.com>
+Shatrugna Sadhu <ssadhu@apcera.com>
+Shawn Smith <shawnpsmith@gmail.com>
+Stas Maksimov <maksimov@gmail.com>
+Tor Arvid Lund <torarvid@gmail.com>
+Zac Bergquist <zbergquist99@gmail.com>
diff --git a/vendor/github.com/sergi/go-diff/LICENSE.txt b/vendor/github.com/sergi/go-diff/LICENSE
index eeb91026ac..937942c2b2 100644
--- a/vendor/github.com/sergi/go-diff/LICENSE.txt
+++ b/vendor/github.com/sergi/go-diff/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2012 Sergi Mansilla <sergi.mansilla@gmail.com>
+Copyright (c) 2012-2016 The go-diff Authors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/dmp.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go
index 4e61821dd6..82ad7bc8f1 100644
--- a/vendor/github.com/sergi/go-diff/diffmatchpatch/dmp.go
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go
@@ -1,19 +1,11 @@
-/**
- * dmp.go
- *
- * Go language implementation of Google Diff, Match, and Patch library
- *
- * Original library is Copyright (c) 2006 Google Inc.
- * http://code.google.com/p/google-diff-match-patch/
- *
- * Copyright (c) 2012 Sergi Mansilla <sergi.mansilla@gmail.com>
- * https://github.com/sergi/go-diff
- *
- * See included LICENSE file for license details.
- */
-
-// Package diffmatchpatch offers robust algorithms to perform the
-// operations required for synchronizing plain text.
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
package diffmatchpatch
import (
@@ -30,10 +22,6 @@ import (
"unicode/utf8"
)
-// The data structure representing a diff is an array of tuples:
-// [[DiffDelete, 'Hello'], [DiffInsert, 'Goodbye'], [DiffEqual, ' world.']]
-// which means: delete 'Hello', add 'Goodbye' and keep ' world.'
-
// Operation defines the operation of a diff item.
type Operation int8
@@ -46,214 +34,24 @@ const (
DiffEqual Operation = 0
)
-// unescaper unescapes selected chars for compatibility with JavaScript's encodeURI.
-// In speed critical applications this could be dropped since the
-// receiving application will certainly decode these fine.
-// Note that this function is case-sensitive. Thus "%3F" would not be
-// unescaped. But this is ok because it is only called with the output of
-// HttpUtility.UrlEncode which returns lowercase hex.
-//
-// Example: "%3f" -> "?", "%24" -> "$", etc.
-var unescaper = strings.NewReplacer(
- "%21", "!", "%7E", "~", "%27", "'",
- "%28", "(", "%29", ")", "%3B", ";",
- "%2F", "/", "%3F", "?", "%3A", ":",
- "%40", "@", "%26", "&", "%3D", "=",
- "%2B", "+", "%24", "$", "%2C", ",", "%23", "#", "%2A", "*")
-
-// Define some regex patterns for matching boundaries.
-var (
- nonAlphaNumericRegex = regexp.MustCompile(`[^a-zA-Z0-9]`)
- whitespaceRegex = regexp.MustCompile(`\s`)
- linebreakRegex = regexp.MustCompile(`[\r\n]`)
- blanklineEndRegex = regexp.MustCompile(`\n\r?\n$`)
- blanklineStartRegex = regexp.MustCompile(`^\r?\n\r?\n`)
-)
-
-func splice(slice []Diff, index int, amount int, elements ...Diff) []Diff {
- return append(slice[:index], append(elements, slice[index+amount:]...)...)
-}
-
-// indexOf returns the first index of pattern in str, starting at str[i].
-func indexOf(str string, pattern string, i int) int {
- if i > len(str)-1 {
- return -1
- }
- if i <= 0 {
- return strings.Index(str, pattern)
- }
- ind := strings.Index(str[i:], pattern)
- if ind == -1 {
- return -1
- }
- return ind + i
-}
-
-// lastIndexOf returns the last index of pattern in str, starting at str[i].
-func lastIndexOf(str string, pattern string, i int) int {
- if i < 0 {
- return -1
- }
- if i >= len(str) {
- return strings.LastIndex(str, pattern)
- }
- _, size := utf8.DecodeRuneInString(str[i:])
- return strings.LastIndex(str[:i+size], pattern)
-}
-
-// Return the index of pattern in target, starting at target[i].
-func runesIndexOf(target, pattern []rune, i int) int {
- if i > len(target)-1 {
- return -1
- }
- if i <= 0 {
- return runesIndex(target, pattern)
- }
- ind := runesIndex(target[i:], pattern)
- if ind == -1 {
- return -1
- }
- return ind + i
-}
-
-func min(x, y int) int {
- if x < y {
- return x
- }
- return y
-}
-
-func max(x, y int) int {
- if x > y {
- return x
- }
- return y
-}
-
-func runesEqual(r1, r2 []rune) bool {
- if len(r1) != len(r2) {
- return false
- }
- for i, c := range r1 {
- if c != r2[i] {
- return false
- }
- }
- return true
-}
-
-// The equivalent of strings.Index for rune slices.
-func runesIndex(r1, r2 []rune) int {
- last := len(r1) - len(r2)
- for i := 0; i <= last; i++ {
- if runesEqual(r1[i:i+len(r2)], r2) {
- return i
- }
- }
- return -1
-}
-
// Diff represents one diff operation
type Diff struct {
Type Operation
Text string
}
-// Patch represents one patch operation.
-type Patch struct {
- diffs []Diff
- start1 int
- start2 int
- length1 int
- length2 int
-}
-
-// String emulates GNU diff's format.
-// Header: @@ -382,8 +481,9 @@
-// Indicies are printed as 1-based, not 0-based.
-func (p *Patch) String() string {
- var coords1, coords2 string
-
- if p.length1 == 0 {
- coords1 = strconv.Itoa(p.start1) + ",0"
- } else if p.length1 == 1 {
- coords1 = strconv.Itoa(p.start1 + 1)
- } else {
- coords1 = strconv.Itoa(p.start1+1) + "," + strconv.Itoa(p.length1)
- }
-
- if p.length2 == 0 {
- coords2 = strconv.Itoa(p.start2) + ",0"
- } else if p.length2 == 1 {
- coords2 = strconv.Itoa(p.start2 + 1)
- } else {
- coords2 = strconv.Itoa(p.start2+1) + "," + strconv.Itoa(p.length2)
- }
-
- var text bytes.Buffer
- _, _ = text.WriteString("@@ -" + coords1 + " +" + coords2 + " @@\n")
-
- // Escape the body of the patch with %xx notation.
- for _, aDiff := range p.diffs {
- switch aDiff.Type {
- case DiffInsert:
- _, _ = text.WriteString("+")
- case DiffDelete:
- _, _ = text.WriteString("-")
- case DiffEqual:
- _, _ = text.WriteString(" ")
- }
-
- _, _ = text.WriteString(strings.Replace(url.QueryEscape(aDiff.Text), "+", " ", -1))
- _, _ = text.WriteString("\n")
- }
-
- return unescaper.Replace(text.String())
-}
-
-// DiffMatchPatch holds the configuration for diff-match-patch operations.
-type DiffMatchPatch struct {
- // Number of seconds to map a diff before giving up (0 for infinity).
- DiffTimeout time.Duration
- // Cost of an empty edit operation in terms of edit characters.
- DiffEditCost int
- // How far to search for a match (0 = exact location, 1000+ = broad match).
- // A match this many characters away from the expected location will add
- // 1.0 to the score (0.0 is a perfect match).
- MatchDistance int
- // When deleting a large block of text (over ~64 characters), how close do
- // the contents have to be to match the expected contents. (0.0 = perfection,
- // 1.0 = very loose). Note that MatchThreshold controls how closely the
- // end points of a delete need to match.
- PatchDeleteThreshold float64
- // Chunk size for context length.
- PatchMargin int
- // The number of bits in an int.
- MatchMaxBits int
- // At what point is no match declared (0.0 = perfection, 1.0 = very loose).
- MatchThreshold float64
-}
-
-// New creates a new DiffMatchPatch object with default parameters.
-func New() *DiffMatchPatch {
- // Defaults.
- return &DiffMatchPatch{
- DiffTimeout: time.Second,
- DiffEditCost: 4,
- MatchThreshold: 0.5,
- MatchDistance: 1000,
- PatchDeleteThreshold: 0.5,
- PatchMargin: 4,
- MatchMaxBits: 32,
- }
+func splice(slice []Diff, index int, amount int, elements ...Diff) []Diff {
+ return append(slice[:index], append(elements, slice[index+amount:]...)...)
}
// DiffMain finds the differences between two texts.
+// If an invalid UTF-8 sequence is encountered, it will be replaced by the Unicode replacement character.
func (dmp *DiffMatchPatch) DiffMain(text1, text2 string, checklines bool) []Diff {
return dmp.DiffMainRunes([]rune(text1), []rune(text2), checklines)
}
// DiffMainRunes finds the differences between two rune sequences.
+// If an invalid UTF-8 sequence is encountered, it will be replaced by the Unicode replacement character.
func (dmp *DiffMatchPatch) DiffMainRunes(text1, text2 []rune, checklines bool) []Diff {
var deadline time.Time
if dmp.DiffTimeout > 0 {
@@ -296,8 +94,7 @@ func (dmp *DiffMatchPatch) diffMainRunes(text1, text2 []rune, checklines bool, d
return dmp.DiffCleanupMerge(diffs)
}
-// diffCompute finds the differences between two rune slices. Assumes that the texts do not
-// have any common prefix or suffix.
+// diffCompute finds the differences between two rune slices. Assumes that the texts do not have any common prefix or suffix.
func (dmp *DiffMatchPatch) diffCompute(text1, text2 []rune, checklines bool, deadline time.Time) []Diff {
diffs := []Diff{}
if len(text1) == 0 {
@@ -355,8 +152,7 @@ func (dmp *DiffMatchPatch) diffCompute(text1, text2 []rune, checklines bool, dea
return dmp.diffBisect(text1, text2, deadline)
}
-// diffLineMode does a quick line-level diff on both []runes, then rediff the parts for
-// greater accuracy. This speedup can produce non-minimal diffs.
+// diffLineMode does a quick line-level diff on both []runes, then rediff the parts for greater accuracy. This speedup can produce non-minimal diffs.
func (dmp *DiffMatchPatch) diffLineMode(text1, text2 []rune, deadline time.Time) []Diff {
// Scan the text on a line-by-line basis first.
text1, text2, linearray := dmp.diffLinesToRunes(text1, text2)
@@ -414,16 +210,15 @@ func (dmp *DiffMatchPatch) diffLineMode(text1, text2 []rune, deadline time.Time)
return diffs[:len(diffs)-1] // Remove the dummy entry at the end.
}
-// DiffBisect finds the 'middle snake' of a diff, split the problem in two
-// and return the recursively constructed diff.
+// DiffBisect finds the 'middle snake' of a diff, split the problem in two and return the recursively constructed diff.
+// If an invalid UTF-8 sequence is encountered, it will be replaced by the Unicode replacement character.
// See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
func (dmp *DiffMatchPatch) DiffBisect(text1, text2 string, deadline time.Time) []Diff {
// Unused in this code, but retained for interface compatibility.
return dmp.diffBisect([]rune(text1), []rune(text2), deadline)
}
-// diffBisect finds the 'middle snake' of a diff, splits the problem in two
-// and returns the recursively constructed diff.
+// diffBisect finds the 'middle snake' of a diff, splits the problem in two and returns the recursively constructed diff.
// See Myers's 1986 paper: An O(ND) Difference Algorithm and Its Variations.
func (dmp *DiffMatchPatch) diffBisect(runes1, runes2 []rune, deadline time.Time) []Diff {
// Cache the text lengths to prevent multiple calls.
@@ -443,11 +238,9 @@ func (dmp *DiffMatchPatch) diffBisect(runes1, runes2 []rune, deadline time.Time)
v2[vOffset+1] = 0
delta := runes1Len - runes2Len
- // If the total number of characters is odd, then the front path will collide
- // with the reverse path.
+ // If the total number of characters is odd, then the front path will collide with the reverse path.
front := (delta%2 != 0)
- // Offsets for start and end of k loop.
- // Prevents mapping of space beyond the grid.
+ // Offsets for start and end of k loop. Prevents mapping of space beyond the grid.
k1start := 0
k1end := 0
k2start := 0
@@ -535,8 +328,7 @@ func (dmp *DiffMatchPatch) diffBisect(runes1, runes2 []rune, deadline time.Time)
}
}
}
- // Diff took too long and hit the deadline or
- // number of diffs equals number of characters, no commonality at all.
+ // Diff took too long and hit the deadline or number of diffs equals number of characters, no commonality at all.
return []Diff{
Diff{DiffDelete, string(runes1)},
Diff{DiffInsert, string(runes2)},
@@ -557,18 +349,16 @@ func (dmp *DiffMatchPatch) diffBisectSplit(runes1, runes2 []rune, x, y int,
return append(diffs, diffsb...)
}
-// DiffLinesToChars splits two texts into a list of strings. Reduces the texts to a string of
-// hashes where each Unicode character represents one line.
+// DiffLinesToChars splits two texts into a list of strings, and educes the texts to a string of hashes where each Unicode character represents one line.
// It's slightly faster to call DiffLinesToRunes first, followed by DiffMainRunes.
func (dmp *DiffMatchPatch) DiffLinesToChars(text1, text2 string) (string, string, []string) {
chars1, chars2, lineArray := dmp.DiffLinesToRunes(text1, text2)
return string(chars1), string(chars2), lineArray
}
-// DiffLinesToRunes splits two texts into a list of runes. Each rune represents one line.
+// DiffLinesToRunes splits two texts into a list of runes. Each rune represents one line.
func (dmp *DiffMatchPatch) DiffLinesToRunes(text1, text2 string) ([]rune, []rune, []string) {
- // '\x00' is a valid character, but various debuggers don't like it.
- // So we'll insert a junk entry to avoid generating a null character.
+ // '\x00' is a valid character, but various debuggers don't like it. So we'll insert a junk entry to avoid generating a null character.
lineArray := []string{""} // e.g. lineArray[4] == 'Hello\n'
lineHash := map[string]int{} // e.g. lineHash['Hello\n'] == 4
@@ -582,13 +372,10 @@ func (dmp *DiffMatchPatch) diffLinesToRunes(text1, text2 []rune) ([]rune, []rune
return dmp.DiffLinesToRunes(string(text1), string(text2))
}
-// diffLinesToRunesMunge splits a text into an array of strings. Reduces the
-// texts to a []rune where each Unicode character represents one line.
+// diffLinesToRunesMunge splits a text into an array of strings, and reduces the texts to a []rune where each Unicode character represents one line.
// We use strings instead of []runes as input mainly because you can't use []rune as a map key.
func (dmp *DiffMatchPatch) diffLinesToRunesMunge(text string, lineArray *[]string, lineHash map[string]int) []rune {
- // Walk the text, pulling out a substring for each line.
- // text.split('\n') would would temporarily double our memory footprint.
- // Modifying text would create many large strings to garbage collect.
+ // Walk the text, pulling out a substring for each line. text.split('\n') would would temporarily double our memory footprint. Modifying text would create many large strings to garbage collect.
lineStart := 0
lineEnd := -1
runes := []rune{}
@@ -616,8 +403,7 @@ func (dmp *DiffMatchPatch) diffLinesToRunesMunge(text string, lineArray *[]strin
return runes
}
-// DiffCharsToLines rehydrates the text in a diff from a string of line hashes to real lines of
-// text.
+// DiffCharsToLines rehydrates the text in a diff from a string of line hashes to real lines of text.
func (dmp *DiffMatchPatch) DiffCharsToLines(diffs []Diff, lineArray []string) []Diff {
hydrated := make([]Diff, 0, len(diffs))
for _, aDiff := range diffs {
@@ -670,6 +456,7 @@ func commonSuffixLength(text1, text2 []rune) int {
}
return n
+ // TODO research and benchmark this, why is it not activated? https://github.com/sergi/go-diff/issues/54
// Binary search.
// Performance analysis: http://neil.fraser.name/news/2007/10/09/
/*
@@ -712,9 +499,7 @@ func (dmp *DiffMatchPatch) DiffCommonOverlap(text1 string, text2 string) int {
return textLength
}
- // Start by looking for a single character match
- // and increase length until no match is found.
- // Performance analysis: http://neil.fraser.name/news/2010/11/04/
+ // Start by looking for a single character match and increase length until no match is found. Performance analysis: http://neil.fraser.name/news/2010/11/04/
best := 0
length := 1
for {
@@ -733,8 +518,7 @@ func (dmp *DiffMatchPatch) DiffCommonOverlap(text1 string, text2 string) int {
return best
}
-// DiffHalfMatch checks whether the two texts share a substring which is at
-// least half the length of the longer text. This speedup can produce non-minimal diffs.
+// DiffHalfMatch checks whether the two texts share a substring which is at least half the length of the longer text. This speedup can produce non-minimal diffs.
func (dmp *DiffMatchPatch) DiffHalfMatch(text1, text2 string) []string {
// Unused in this code, but retained for interface compatibility.
runeSlices := dmp.diffHalfMatch([]rune(text1), []rune(text2))
@@ -799,12 +583,7 @@ func (dmp *DiffMatchPatch) diffHalfMatch(text1, text2 []rune) [][]rune {
}
// diffHalfMatchI checks if a substring of shorttext exist within longtext such that the substring is at least half the length of longtext?
-// @param {string} longtext Longer string.
-// @param {string} shorttext Shorter string.
-// @param {number} i Start index of quarter length substring within longtext.
-// @return {Array.<string>} Five element Array, containing the prefix of
-// longtext, the suffix of longtext, the prefix of shorttext, the suffix
-// of shorttext and the common middle. Or null if there was no match.
+// Returns a slice containing the prefix of longtext, the suffix of longtext, the prefix of shorttext, the suffix of shorttext and the common middle, or null if there was no match.
func (dmp *DiffMatchPatch) diffHalfMatchI(l, s []rune, i int) [][]rune {
var bestCommonA []rune
var bestCommonB []rune
@@ -845,8 +624,7 @@ func (dmp *DiffMatchPatch) diffHalfMatchI(l, s []rune, i int) [][]rune {
}
}
-// DiffCleanupSemantic reduces the number of edits by eliminating
-// semantically trivial equalities.
+// DiffCleanupSemantic reduces the number of edits by eliminating semantically trivial equalities.
func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff {
changes := false
// Stack of indices where equalities are found.
@@ -865,7 +643,9 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff {
var lengthInsertions2, lengthDeletions2 int
for pointer < len(diffs) {
- if diffs[pointer].Type == DiffEqual { // Equality found.
+ if diffs[pointer].Type == DiffEqual {
+ // Equality found.
+
equalities = &equality{
data: pointer,
next: equalities,
@@ -875,14 +655,15 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff {
lengthInsertions2 = 0
lengthDeletions2 = 0
lastequality = diffs[pointer].Text
- } else { // An insertion or deletion.
+ } else {
+ // An insertion or deletion.
+
if diffs[pointer].Type == DiffInsert {
lengthInsertions2 += len(diffs[pointer].Text)
} else {
lengthDeletions2 += len(diffs[pointer].Text)
}
- // Eliminate an equality that is smaller or equal to the edits on both
- // sides of it.
+ // Eliminate an equality that is smaller or equal to the edits on both sides of it.
difference1 := int(math.Max(float64(lengthInsertions1), float64(lengthDeletions1)))
difference2 := int(math.Max(float64(lengthInsertions2), float64(lengthDeletions2)))
if len(lastequality) > 0 &&
@@ -942,12 +723,11 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff {
if float64(overlapLength1) >= float64(len(deletion))/2 ||
float64(overlapLength1) >= float64(len(insertion))/2 {
- // Overlap found. Insert an equality and trim the surrounding edits.
+ // Overlap found. Insert an equality and trim the surrounding edits.
diffs = append(
diffs[:pointer],
append([]Diff{Diff{DiffEqual, insertion[:overlapLength1]}}, diffs[pointer:]...)...)
- //diffs.splice(pointer, 0,
- // [DiffEqual, insertion[0 : overlapLength1)]]
+
diffs[pointer-1].Text =
deletion[0 : len(deletion)-overlapLength1]
diffs[pointer+1].Text = insertion[overlapLength1:]
@@ -956,14 +736,12 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff {
} else {
if float64(overlapLength2) >= float64(len(deletion))/2 ||
float64(overlapLength2) >= float64(len(insertion))/2 {
- // Reverse overlap found.
- // Insert an equality and swap and trim the surrounding edits.
+ // Reverse overlap found. Insert an equality and swap and trim the surrounding edits.
overlap := Diff{DiffEqual, deletion[:overlapLength2]}
diffs = append(
diffs[:pointer],
append([]Diff{overlap}, diffs[pointer:]...)...)
- // diffs.splice(pointer, 0,
- // [DiffEqual, deletion[0 : overlapLength2)]]
+
diffs[pointer-1].Type = DiffInsert
diffs[pointer-1].Text = insertion[0 : len(insertion)-overlapLength2]
diffs[pointer+1].Type = DiffDelete
@@ -979,65 +757,60 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff {
return diffs
}
-// DiffCleanupSemanticLossless looks for single edits surrounded on both sides by equalities
-// which can be shifted sideways to align the edit to a word boundary.
-// e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.
-func (dmp *DiffMatchPatch) DiffCleanupSemanticLossless(diffs []Diff) []Diff {
-
- /**
- * Given two strings, compute a score representing whether the internal
- * boundary falls on logical boundaries.
- * Scores range from 6 (best) to 0 (worst).
- * Closure, but does not reference any external variables.
- * @param {string} one First string.
- * @param {string} two Second string.
- * @return {number} The score.
- * @private
- */
- diffCleanupSemanticScore := func(one, two string) int {
- if len(one) == 0 || len(two) == 0 {
- // Edges are the best.
- return 6
- }
+// Define some regex patterns for matching boundaries.
+var (
+ nonAlphaNumericRegex = regexp.MustCompile(`[^a-zA-Z0-9]`)
+ whitespaceRegex = regexp.MustCompile(`\s`)
+ linebreakRegex = regexp.MustCompile(`[\r\n]`)
+ blanklineEndRegex = regexp.MustCompile(`\n\r?\n$`)
+ blanklineStartRegex = regexp.MustCompile(`^\r?\n\r?\n`)
+)
- // Each port of this function behaves slightly differently due to
- // subtle differences in each language's definition of things like
- // 'whitespace'. Since this function's purpose is largely cosmetic,
- // the choice has been made to use each language's native features
- // rather than force total conformity.
- rune1, _ := utf8.DecodeLastRuneInString(one)
- rune2, _ := utf8.DecodeRuneInString(two)
- char1 := string(rune1)
- char2 := string(rune2)
-
- nonAlphaNumeric1 := nonAlphaNumericRegex.MatchString(char1)
- nonAlphaNumeric2 := nonAlphaNumericRegex.MatchString(char2)
- whitespace1 := nonAlphaNumeric1 && whitespaceRegex.MatchString(char1)
- whitespace2 := nonAlphaNumeric2 && whitespaceRegex.MatchString(char2)
- lineBreak1 := whitespace1 && linebreakRegex.MatchString(char1)
- lineBreak2 := whitespace2 && linebreakRegex.MatchString(char2)
- blankLine1 := lineBreak1 && blanklineEndRegex.MatchString(one)
- blankLine2 := lineBreak2 && blanklineEndRegex.MatchString(two)
-
- if blankLine1 || blankLine2 {
- // Five points for blank lines.
- return 5
- } else if lineBreak1 || lineBreak2 {
- // Four points for line breaks.
- return 4
- } else if nonAlphaNumeric1 && !whitespace1 && whitespace2 {
- // Three points for end of sentences.
- return 3
- } else if whitespace1 || whitespace2 {
- // Two points for whitespace.
- return 2
- } else if nonAlphaNumeric1 || nonAlphaNumeric2 {
- // One point for non-alphanumeric.
- return 1
- }
- return 0
- }
+// diffCleanupSemanticScore computes a score representing whether the internal boundary falls on logical boundaries.
+// Scores range from 6 (best) to 0 (worst). Closure, but does not reference any external variables.
+func diffCleanupSemanticScore(one, two string) int {
+ if len(one) == 0 || len(two) == 0 {
+ // Edges are the best.
+ return 6
+ }
+
+ // Each port of this function behaves slightly differently due to subtle differences in each language's definition of things like 'whitespace'. Since this function's purpose is largely cosmetic, the choice has been made to use each language's native features rather than force total conformity.
+ rune1, _ := utf8.DecodeLastRuneInString(one)
+ rune2, _ := utf8.DecodeRuneInString(two)
+ char1 := string(rune1)
+ char2 := string(rune2)
+
+ nonAlphaNumeric1 := nonAlphaNumericRegex.MatchString(char1)
+ nonAlphaNumeric2 := nonAlphaNumericRegex.MatchString(char2)
+ whitespace1 := nonAlphaNumeric1 && whitespaceRegex.MatchString(char1)
+ whitespace2 := nonAlphaNumeric2 && whitespaceRegex.MatchString(char2)
+ lineBreak1 := whitespace1 && linebreakRegex.MatchString(char1)
+ lineBreak2 := whitespace2 && linebreakRegex.MatchString(char2)
+ blankLine1 := lineBreak1 && blanklineEndRegex.MatchString(one)
+ blankLine2 := lineBreak2 && blanklineEndRegex.MatchString(two)
+
+ if blankLine1 || blankLine2 {
+ // Five points for blank lines.
+ return 5
+ } else if lineBreak1 || lineBreak2 {
+ // Four points for line breaks.
+ return 4
+ } else if nonAlphaNumeric1 && !whitespace1 && whitespace2 {
+ // Three points for end of sentences.
+ return 3
+ } else if whitespace1 || whitespace2 {
+ // Two points for whitespace.
+ return 2
+ } else if nonAlphaNumeric1 || nonAlphaNumeric2 {
+ // One point for non-alphanumeric.
+ return 1
+ }
+ return 0
+}
+// DiffCleanupSemanticLossless looks for single edits surrounded on both sides by equalities which can be shifted sideways to align the edit to a word boundary.
+// E.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.
+func (dmp *DiffMatchPatch) DiffCleanupSemanticLossless(diffs []Diff) []Diff {
pointer := 1
// Intentionally ignore the first and last element (don't need checking).
@@ -1076,8 +849,7 @@ func (dmp *DiffMatchPatch) DiffCleanupSemanticLossless(diffs []Diff) []Diff {
equality2 = equality2[sz:]
score := diffCleanupSemanticScore(equality1, edit) +
diffCleanupSemanticScore(edit, equality2)
- // The >= encourages trailing rather than leading whitespace on
- // edits.
+ // The >= encourages trailing rather than leading whitespace on edits.
if score >= bestScore {
bestScore = score
bestEquality1 = equality1
@@ -1099,7 +871,6 @@ func (dmp *DiffMatchPatch) DiffCleanupSemanticLossless(diffs []Diff) []Diff {
if len(bestEquality2) != 0 {
diffs[pointer+1].Text = bestEquality2
} else {
- //splice(diffs, pointer+1, 1)
diffs = append(diffs[:pointer+1], diffs[pointer+2:]...)
pointer--
}
@@ -1111,8 +882,7 @@ func (dmp *DiffMatchPatch) DiffCleanupSemanticLossless(diffs []Diff) []Diff {
return diffs
}
-// DiffCleanupEfficiency reduces the number of edits by eliminating
-// operationally trivial equalities.
+// DiffCleanupEfficiency reduces the number of edits by eliminating operationally trivial equalities.
func (dmp *DiffMatchPatch) DiffCleanupEfficiency(diffs []Diff) []Diff {
changes := false
// Stack of indices where equalities are found.
@@ -1157,14 +927,13 @@ func (dmp *DiffMatchPatch) DiffCleanupEfficiency(diffs []Diff) []Diff {
} else {
postIns = true
}
- /*
- * Five types to be split:
- * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
- * <ins>A</ins>X<ins>C</ins><del>D</del>
- * <ins>A</ins><del>B</del>X<ins>C</ins>
- * <ins>A</del>X<ins>C</ins><del>D</del>
- * <ins>A</ins><del>B</del>X<del>C</del>
- */
+
+ // Five types to be split:
+ // <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
+ // <ins>A</ins>X<ins>C</ins><del>D</del>
+ // <ins>A</ins><del>B</del>X<ins>C</ins>
+ // <ins>A</del>X<ins>C</ins><del>D</del>
+ // <ins>A</ins><del>B</del>X<del>C</del>
var sumPres int
if preIns {
sumPres++
@@ -1224,7 +993,7 @@ func (dmp *DiffMatchPatch) DiffCleanupEfficiency(diffs []Diff) []Diff {
return diffs
}
-// DiffCleanupMerge reorders and merges like edit sections. Merge equalities.
+// DiffCleanupMerge reorders and merges like edit sections. Merge equalities.
// Any edit section can move as long as it doesn't cross an equality.
func (dmp *DiffMatchPatch) DiffCleanupMerge(diffs []Diff) []Diff {
// Add a dummy entry at the end.
@@ -1317,9 +1086,7 @@ func (dmp *DiffMatchPatch) DiffCleanupMerge(diffs []Diff) []Diff {
diffs = diffs[0 : len(diffs)-1] // Remove the dummy entry at the end.
}
- // Second pass: look for single edits surrounded on both sides by
- // equalities which can be shifted sideways to eliminate an equality.
- // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
+ // Second pass: look for single edits surrounded on both sides by equalities which can be shifted sideways to eliminate an equality. E.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
changes := false
pointer = 1
// Intentionally ignore the first and last element (don't need checking).
@@ -1355,9 +1122,6 @@ func (dmp *DiffMatchPatch) DiffCleanupMerge(diffs []Diff) []Diff {
}
// DiffXIndex returns the equivalent location in s2.
-// loc is a location in text1, comAdde and return the equivalent location in
-// text2.
-// e.g. "The cat" vs "The big cat", 1->1, 5->8
func (dmp *DiffMatchPatch) DiffXIndex(diffs []Diff, loc int) int {
chars1 := 0
chars2 := 0
@@ -1391,8 +1155,7 @@ func (dmp *DiffMatchPatch) DiffXIndex(diffs []Diff, loc int) int {
}
// DiffPrettyHtml converts a []Diff into a pretty HTML report.
-// It is intended as an example from which to write one's own
-// display functions.
+// It is intended as an example from which to write one's own display functions.
func (dmp *DiffMatchPatch) DiffPrettyHtml(diffs []Diff) string {
var buff bytes.Buffer
for _, diff := range diffs {
@@ -1463,8 +1226,7 @@ func (dmp *DiffMatchPatch) DiffText2(diffs []Diff) string {
return text.String()
}
-// DiffLevenshtein computes the Levenshtein distance; the number of inserted, deleted or
-// substituted characters.
+// DiffLevenshtein computes the Levenshtein distance that is the number of inserted, deleted or substituted characters.
func (dmp *DiffMatchPatch) DiffLevenshtein(diffs []Diff) int {
levenshtein := 0
insertions := 0
@@ -1488,11 +1250,8 @@ func (dmp *DiffMatchPatch) DiffLevenshtein(diffs []Diff) int {
return levenshtein
}
-// DiffToDelta crushes the diff into an encoded string which describes the operations
-// required to transform text1 into text2.
-// E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'.
-// Operations are tab-separated. Inserted text is escaped using %xx
-// notation.
+// DiffToDelta crushes the diff into an encoded string which describes the operations required to transform text1 into text2.
+// E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'. Operations are tab-separated. Inserted text is escaped using %xx notation.
func (dmp *DiffMatchPatch) DiffToDelta(diffs []Diff) string {
var text bytes.Buffer
for _, aDiff := range diffs {
@@ -1523,33 +1282,23 @@ func (dmp *DiffMatchPatch) DiffToDelta(diffs []Diff) string {
return delta
}
-// DiffFromDelta given the original text1, and an encoded string which describes the
-// operations required to transform text1 into text2, comAdde the full diff.
-func (dmp *DiffMatchPatch) DiffFromDelta(text1, delta string) (diffs []Diff, err error) {
- diffs = []Diff{}
-
- defer func() {
- if r := recover(); r != nil {
- err = r.(error)
- }
- }()
-
- pointer := 0 // Cursor in text1
- tokens := strings.Split(delta, "\t")
+// DiffFromDelta given the original text1, and an encoded string which describes the operations required to transform text1 into text2, comAdde the full diff.
+func (dmp *DiffMatchPatch) DiffFromDelta(text1 string, delta string) (diffs []Diff, err error) {
+ i := 0
+ runes := []rune(text1)
- for _, token := range tokens {
+ for _, token := range strings.Split(delta, "\t") {
if len(token) == 0 {
// Blank tokens are ok (from a trailing \t).
continue
}
- // Each token begins with a one character parameter which specifies the
- // operation of this token (delete, insert, equality).
+ // Each token begins with a one character parameter which specifies the operation of this token (delete, insert, equality).
param := token[1:]
switch op := token[0]; op {
case '+':
- // decode would Diff all "+" to " "
+ // Decode would Diff all "+" to " "
param = strings.Replace(param, "+", "%2b", -1)
param, err = url.QueryUnescape(param)
if err != nil {
@@ -1558,18 +1307,23 @@ func (dmp *DiffMatchPatch) DiffFromDelta(text1, delta string) (diffs []Diff, err
if !utf8.ValidString(param) {
return nil, fmt.Errorf("invalid UTF-8 token: %q", param)
}
+
diffs = append(diffs, Diff{DiffInsert, param})
case '=', '-':
n, err := strconv.ParseInt(param, 10, 0)
if err != nil {
- return diffs, err
+ return nil, err
} else if n < 0 {
- return diffs, errors.New("Negative number in DiffFromDelta: " + param)
+ return nil, errors.New("Negative number in DiffFromDelta: " + param)
}
- // remember that string slicing is by byte - we want by rune here.
- text := string([]rune(text1)[pointer : pointer+int(n)])
- pointer += int(n)
+ i += int(n)
+ // Break out if we are out of bounds, go1.6 can't handle this very well
+ if i > len(runes) {
+ break
+ }
+ // Remember that string slicing is by byte - we want by rune here.
+ text := string(runes[i-int(n) : i])
if op == '=' {
diffs = append(diffs, Diff{DiffEqual, text})
@@ -1578,667 +1332,13 @@ func (dmp *DiffMatchPatch) DiffFromDelta(text1, delta string) (diffs []Diff, err
}
default:
// Anything else is an error.
- return diffs, errors.New("Invalid diff operation in DiffFromDelta: " + string(token[0]))
+ return nil, errors.New("Invalid diff operation in DiffFromDelta: " + string(token[0]))
}
}
- if pointer != len([]rune(text1)) {
- return diffs, fmt.Errorf("Delta length (%v) smaller than source text length (%v)", pointer, len(text1))
+ if i != len(runes) {
+ return nil, fmt.Errorf("Delta length (%v) is different from source text length (%v)", i, len(text1))
}
- return diffs, err
-}
-
-// MATCH FUNCTIONS
-
-// MatchMain locates the best instance of 'pattern' in 'text' near 'loc'.
-// Returns -1 if no match found.
-func (dmp *DiffMatchPatch) MatchMain(text, pattern string, loc int) int {
- // Check for null inputs not needed since null can't be passed in C#.
- loc = int(math.Max(0, math.Min(float64(loc), float64(len(text)))))
- if text == pattern {
- // Shortcut (potentially not guaranteed by the algorithm)
- return 0
- } else if len(text) == 0 {
- // Nothing to match.
- return -1
- } else if loc+len(pattern) <= len(text) && text[loc:loc+len(pattern)] == pattern {
- // Perfect match at the perfect spot! (Includes case of null pattern)
- return loc
- }
- // Do a fuzzy compare.
- return dmp.MatchBitap(text, pattern, loc)
-}
-
-// MatchBitap locates the best instance of 'pattern' in 'text' near 'loc' using the
-// Bitap algorithm. Returns -1 if no match found.
-func (dmp *DiffMatchPatch) MatchBitap(text, pattern string, loc int) int {
- // Initialise the alphabet.
- s := dmp.MatchAlphabet(pattern)
-
- // Highest score beyond which we give up.
- scoreThreshold := dmp.MatchThreshold
- // Is there a nearby exact match? (speedup)
- bestLoc := indexOf(text, pattern, loc)
- if bestLoc != -1 {
- scoreThreshold = math.Min(dmp.matchBitapScore(0, bestLoc, loc,
- pattern), scoreThreshold)
- // What about in the other direction? (speedup)
- bestLoc = lastIndexOf(text, pattern, loc+len(pattern))
- if bestLoc != -1 {
- scoreThreshold = math.Min(dmp.matchBitapScore(0, bestLoc, loc,
- pattern), scoreThreshold)
- }
- }
-
- // Initialise the bit arrays.
- matchmask := 1 << uint((len(pattern) - 1))
- bestLoc = -1
-
- var binMin, binMid int
- binMax := len(pattern) + len(text)
- lastRd := []int{}
- for d := 0; d < len(pattern); d++ {
- // Scan for the best match; each iteration allows for one more error.
- // Run a binary search to determine how far from 'loc' we can stray at
- // this error level.
- binMin = 0
- binMid = binMax
- for binMin < binMid {
- if dmp.matchBitapScore(d, loc+binMid, loc, pattern) <= scoreThreshold {
- binMin = binMid
- } else {
- binMax = binMid
- }
- binMid = (binMax-binMin)/2 + binMin
- }
- // Use the result from this iteration as the maximum for the next.
- binMax = binMid
- start := int(math.Max(1, float64(loc-binMid+1)))
- finish := int(math.Min(float64(loc+binMid), float64(len(text))) + float64(len(pattern)))
-
- rd := make([]int, finish+2)
- rd[finish+1] = (1 << uint(d)) - 1
-
- for j := finish; j >= start; j-- {
- var charMatch int
- if len(text) <= j-1 {
- // Out of range.
- charMatch = 0
- } else if _, ok := s[text[j-1]]; !ok {
- charMatch = 0
- } else {
- charMatch = s[text[j-1]]
- }
-
- if d == 0 {
- // First pass: exact match.
- rd[j] = ((rd[j+1] << 1) | 1) & charMatch
- } else {
- // Subsequent passes: fuzzy match.
- rd[j] = ((rd[j+1]<<1)|1)&charMatch | (((lastRd[j+1] | lastRd[j]) << 1) | 1) | lastRd[j+1]
- }
- if (rd[j] & matchmask) != 0 {
- score := dmp.matchBitapScore(d, j-1, loc, pattern)
- // This match will almost certainly be better than any existing
- // match. But check anyway.
- if score <= scoreThreshold {
- // Told you so.
- scoreThreshold = score
- bestLoc = j - 1
- if bestLoc > loc {
- // When passing loc, don't exceed our current distance from loc.
- start = int(math.Max(1, float64(2*loc-bestLoc)))
- } else {
- // Already passed loc, downhill from here on in.
- break
- }
- }
- }
- }
- if dmp.matchBitapScore(d+1, loc, loc, pattern) > scoreThreshold {
- // No hope for a (better) match at greater error levels.
- break
- }
- lastRd = rd
- }
- return bestLoc
-}
-
-// matchBitapScore computes and returns the score for a match with e errors and x location.
-func (dmp *DiffMatchPatch) matchBitapScore(e, x, loc int, pattern string) float64 {
- accuracy := float64(e) / float64(len(pattern))
- proximity := math.Abs(float64(loc - x))
- if dmp.MatchDistance == 0 {
- // Dodge divide by zero error.
- if proximity == 0 {
- return accuracy
- }
-
- return 1.0
- }
- return accuracy + (proximity / float64(dmp.MatchDistance))
-}
-
-// MatchAlphabet initialises the alphabet for the Bitap algorithm.
-func (dmp *DiffMatchPatch) MatchAlphabet(pattern string) map[byte]int {
- s := map[byte]int{}
- charPattern := []byte(pattern)
- for _, c := range charPattern {
- _, ok := s[c]
- if !ok {
- s[c] = 0
- }
- }
- i := 0
-
- for _, c := range charPattern {
- value := s[c] | int(uint(1)<<uint((len(pattern)-i-1)))
- s[c] = value
- i++
- }
- return s
-}
-
-// PATCH FUNCTIONS
-
-// PatchAddContext increases the context until it is unique,
-// but doesn't let the pattern expand beyond MatchMaxBits.
-func (dmp *DiffMatchPatch) PatchAddContext(patch Patch, text string) Patch {
- if len(text) == 0 {
- return patch
- }
-
- pattern := text[patch.start2 : patch.start2+patch.length1]
- padding := 0
-
- // Look for the first and last matches of pattern in text. If two
- // different matches are found, increase the pattern length.
- for strings.Index(text, pattern) != strings.LastIndex(text, pattern) &&
- len(pattern) < dmp.MatchMaxBits-2*dmp.PatchMargin {
- padding += dmp.PatchMargin
- maxStart := max(0, patch.start2-padding)
- minEnd := min(len(text), patch.start2+patch.length1+padding)
- pattern = text[maxStart:minEnd]
- }
- // Add one chunk for good luck.
- padding += dmp.PatchMargin
-
- // Add the prefix.
- prefix := text[max(0, patch.start2-padding):patch.start2]
- if len(prefix) != 0 {
- patch.diffs = append([]Diff{Diff{DiffEqual, prefix}}, patch.diffs...)
- }
- // Add the suffix.
- suffix := text[patch.start2+patch.length1 : min(len(text), patch.start2+patch.length1+padding)]
- if len(suffix) != 0 {
- patch.diffs = append(patch.diffs, Diff{DiffEqual, suffix})
- }
-
- // Roll back the start points.
- patch.start1 -= len(prefix)
- patch.start2 -= len(prefix)
- // Extend the lengths.
- patch.length1 += len(prefix) + len(suffix)
- patch.length2 += len(prefix) + len(suffix)
-
- return patch
-}
-
-// PatchMake computes a list of patches.
-func (dmp *DiffMatchPatch) PatchMake(opt ...interface{}) []Patch {
- if len(opt) == 1 {
- diffs, _ := opt[0].([]Diff)
- text1 := dmp.DiffText1(diffs)
- return dmp.PatchMake(text1, diffs)
- } else if len(opt) == 2 {
- text1 := opt[0].(string)
- switch t := opt[1].(type) {
- case string:
- diffs := dmp.DiffMain(text1, t, true)
- if len(diffs) > 2 {
- diffs = dmp.DiffCleanupSemantic(diffs)
- diffs = dmp.DiffCleanupEfficiency(diffs)
- }
- return dmp.PatchMake(text1, diffs)
- case []Diff:
- return dmp.patchMake2(text1, t)
- }
- } else if len(opt) == 3 {
- return dmp.PatchMake(opt[0], opt[2])
- }
- return []Patch{}
-}
-
-// patchMake2 computes a list of patches to turn text1 into text2.
-// text2 is not provided, diffs are the delta between text1 and text2.
-func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch {
- // Check for null inputs not needed since null can't be passed in C#.
- patches := []Patch{}
- if len(diffs) == 0 {
- return patches // Get rid of the null case.
- }
-
- patch := Patch{}
- charCount1 := 0 // Number of characters into the text1 string.
- charCount2 := 0 // Number of characters into the text2 string.
- // Start with text1 (prepatchText) and apply the diffs until we arrive at
- // text2 (postpatchText). We recreate the patches one by one to determine
- // context info.
- prepatchText := text1
- postpatchText := text1
-
- for i, aDiff := range diffs {
- if len(patch.diffs) == 0 && aDiff.Type != DiffEqual {
- // A new patch starts here.
- patch.start1 = charCount1
- patch.start2 = charCount2
- }
-
- switch aDiff.Type {
- case DiffInsert:
- patch.diffs = append(patch.diffs, aDiff)
- patch.length2 += len(aDiff.Text)
- postpatchText = postpatchText[:charCount2] +
- aDiff.Text + postpatchText[charCount2:]
- case DiffDelete:
- patch.length1 += len(aDiff.Text)
- patch.diffs = append(patch.diffs, aDiff)
- postpatchText = postpatchText[:charCount2] + postpatchText[charCount2+len(aDiff.Text):]
- case DiffEqual:
- if len(aDiff.Text) <= 2*dmp.PatchMargin &&
- len(patch.diffs) != 0 && i != len(diffs)-1 {
- // Small equality inside a patch.
- patch.diffs = append(patch.diffs, aDiff)
- patch.length1 += len(aDiff.Text)
- patch.length2 += len(aDiff.Text)
- }
- if len(aDiff.Text) >= 2*dmp.PatchMargin {
- // Time for a new patch.
- if len(patch.diffs) != 0 {
- patch = dmp.PatchAddContext(patch, prepatchText)
- patches = append(patches, patch)
- patch = Patch{}
- // Unlike Unidiff, our patch lists have a rolling context.
- // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff
- // Update prepatch text & pos to reflect the application of the
- // just completed patch.
- prepatchText = postpatchText
- charCount1 = charCount2
- }
- }
- }
-
- // Update the current character count.
- if aDiff.Type != DiffInsert {
- charCount1 += len(aDiff.Text)
- }
- if aDiff.Type != DiffDelete {
- charCount2 += len(aDiff.Text)
- }
- }
-
- // Pick up the leftover patch if not empty.
- if len(patch.diffs) != 0 {
- patch = dmp.PatchAddContext(patch, prepatchText)
- patches = append(patches, patch)
- }
-
- return patches
-}
-
-// PatchDeepCopy returns an array that is identical to a
-// given an array of patches.
-func (dmp *DiffMatchPatch) PatchDeepCopy(patches []Patch) []Patch {
- patchesCopy := []Patch{}
- for _, aPatch := range patches {
- patchCopy := Patch{}
- for _, aDiff := range aPatch.diffs {
- patchCopy.diffs = append(patchCopy.diffs, Diff{
- aDiff.Type,
- aDiff.Text,
- })
- }
- patchCopy.start1 = aPatch.start1
- patchCopy.start2 = aPatch.start2
- patchCopy.length1 = aPatch.length1
- patchCopy.length2 = aPatch.length2
- patchesCopy = append(patchesCopy, patchCopy)
- }
- return patchesCopy
-}
-
-// PatchApply merges a set of patches onto the text. Returns a patched text, as well
-// as an array of true/false values indicating which patches were applied.
-func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []bool) {
- if len(patches) == 0 {
- return text, []bool{}
- }
-
- // Deep copy the patches so that no changes are made to originals.
- patches = dmp.PatchDeepCopy(patches)
-
- nullPadding := dmp.PatchAddPadding(patches)
- text = nullPadding + text + nullPadding
- patches = dmp.PatchSplitMax(patches)
-
- x := 0
- // delta keeps track of the offset between the expected and actual
- // location of the previous patch. If there are patches expected at
- // positions 10 and 20, but the first patch was found at 12, delta is 2
- // and the second patch has an effective expected position of 22.
- delta := 0
- results := make([]bool, len(patches))
- for _, aPatch := range patches {
- expectedLoc := aPatch.start2 + delta
- text1 := dmp.DiffText1(aPatch.diffs)
- var startLoc int
- endLoc := -1
- if len(text1) > dmp.MatchMaxBits {
- // PatchSplitMax will only provide an oversized pattern
- // in the case of a monster delete.
- startLoc = dmp.MatchMain(text, text1[:dmp.MatchMaxBits], expectedLoc)
- if startLoc != -1 {
- endLoc = dmp.MatchMain(text,
- text1[len(text1)-dmp.MatchMaxBits:], expectedLoc+len(text1)-dmp.MatchMaxBits)
- if endLoc == -1 || startLoc >= endLoc {
- // Can't find valid trailing context. Drop this patch.
- startLoc = -1
- }
- }
- } else {
- startLoc = dmp.MatchMain(text, text1, expectedLoc)
- }
- if startLoc == -1 {
- // No match found. :(
- results[x] = false
- // Subtract the delta for this failed patch from subsequent patches.
- delta -= aPatch.length2 - aPatch.length1
- } else {
- // Found a match. :)
- results[x] = true
- delta = startLoc - expectedLoc
- var text2 string
- if endLoc == -1 {
- text2 = text[startLoc:int(math.Min(float64(startLoc+len(text1)), float64(len(text))))]
- } else {
- text2 = text[startLoc:int(math.Min(float64(endLoc+dmp.MatchMaxBits), float64(len(text))))]
- }
- if text1 == text2 {
- // Perfect match, just shove the Replacement text in.
- text = text[:startLoc] + dmp.DiffText2(aPatch.diffs) + text[startLoc+len(text1):]
- } else {
- // Imperfect match. Run a diff to get a framework of equivalent
- // indices.
- diffs := dmp.DiffMain(text1, text2, false)
- if len(text1) > dmp.MatchMaxBits && float64(dmp.DiffLevenshtein(diffs))/float64(len(text1)) > dmp.PatchDeleteThreshold {
- // The end points match, but the content is unacceptably bad.
- results[x] = false
- } else {
- diffs = dmp.DiffCleanupSemanticLossless(diffs)
- index1 := 0
- for _, aDiff := range aPatch.diffs {
- if aDiff.Type != DiffEqual {
- index2 := dmp.DiffXIndex(diffs, index1)
- if aDiff.Type == DiffInsert {
- // Insertion
- text = text[:startLoc+index2] + aDiff.Text + text[startLoc+index2:]
- } else if aDiff.Type == DiffDelete {
- // Deletion
- startIndex := startLoc + index2
- text = text[:startIndex] +
- text[startIndex+dmp.DiffXIndex(diffs, index1+len(aDiff.Text))-index2:]
- }
- }
- if aDiff.Type != DiffDelete {
- index1 += len(aDiff.Text)
- }
- }
- }
- }
- }
- x++
- }
- // Strip the padding off.
- text = text[len(nullPadding) : len(nullPadding)+(len(text)-2*len(nullPadding))]
- return text, results
-}
-
-// PatchAddPadding adds some padding on text start and end so that edges can match something.
-// Intended to be called only from within patchApply.
-func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string {
- paddingLength := dmp.PatchMargin
- nullPadding := ""
- for x := 1; x <= paddingLength; x++ {
- nullPadding += string(x)
- }
-
- // Bump all the patches forward.
- for i := range patches {
- patches[i].start1 += paddingLength
- patches[i].start2 += paddingLength
- }
-
- // Add some padding on start of first diff.
- if len(patches[0].diffs) == 0 || patches[0].diffs[0].Type != DiffEqual {
- // Add nullPadding equality.
- patches[0].diffs = append([]Diff{Diff{DiffEqual, nullPadding}}, patches[0].diffs...)
- patches[0].start1 -= paddingLength // Should be 0.
- patches[0].start2 -= paddingLength // Should be 0.
- patches[0].length1 += paddingLength
- patches[0].length2 += paddingLength
- } else if paddingLength > len(patches[0].diffs[0].Text) {
- // Grow first equality.
- extraLength := paddingLength - len(patches[0].diffs[0].Text)
- patches[0].diffs[0].Text = nullPadding[len(patches[0].diffs[0].Text):] + patches[0].diffs[0].Text
- patches[0].start1 -= extraLength
- patches[0].start2 -= extraLength
- patches[0].length1 += extraLength
- patches[0].length2 += extraLength
- }
-
- // Add some padding on end of last diff.
- last := len(patches) - 1
- if len(patches[last].diffs) == 0 || patches[last].diffs[len(patches[last].diffs)-1].Type != DiffEqual {
- // Add nullPadding equality.
- patches[last].diffs = append(patches[last].diffs, Diff{DiffEqual, nullPadding})
- patches[last].length1 += paddingLength
- patches[last].length2 += paddingLength
- } else if paddingLength > len(patches[last].diffs[len(patches[last].diffs)-1].Text) {
- // Grow last equality.
- lastDiff := patches[last].diffs[len(patches[last].diffs)-1]
- extraLength := paddingLength - len(lastDiff.Text)
- patches[last].diffs[len(patches[last].diffs)-1].Text += nullPadding[:extraLength]
- patches[last].length1 += extraLength
- patches[last].length2 += extraLength
- }
-
- return nullPadding
-}
-
-// PatchSplitMax looks through the patches and breaks up any which are longer than the
-// maximum limit of the match algorithm.
-// Intended to be called only from within patchApply.
-func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) []Patch {
- patchSize := dmp.MatchMaxBits
- for x := 0; x < len(patches); x++ {
- if patches[x].length1 <= patchSize {
- continue
- }
- bigpatch := patches[x]
- // Remove the big old patch.
- patches = append(patches[:x], patches[x+1:]...)
- x--
-
- start1 := bigpatch.start1
- start2 := bigpatch.start2
- precontext := ""
- for len(bigpatch.diffs) != 0 {
- // Create one of several smaller patches.
- patch := Patch{}
- empty := true
- patch.start1 = start1 - len(precontext)
- patch.start2 = start2 - len(precontext)
- if len(precontext) != 0 {
- patch.length1 = len(precontext)
- patch.length2 = len(precontext)
- patch.diffs = append(patch.diffs, Diff{DiffEqual, precontext})
- }
- for len(bigpatch.diffs) != 0 && patch.length1 < patchSize-dmp.PatchMargin {
- diffType := bigpatch.diffs[0].Type
- diffText := bigpatch.diffs[0].Text
- if diffType == DiffInsert {
- // Insertions are harmless.
- patch.length2 += len(diffText)
- start2 += len(diffText)
- patch.diffs = append(patch.diffs, bigpatch.diffs[0])
- bigpatch.diffs = bigpatch.diffs[1:]
- empty = false
- } else if diffType == DiffDelete && len(patch.diffs) == 1 && patch.diffs[0].Type == DiffEqual && len(diffText) > 2*patchSize {
- // This is a large deletion. Let it pass in one chunk.
- patch.length1 += len(diffText)
- start1 += len(diffText)
- empty = false
- patch.diffs = append(patch.diffs, Diff{diffType, diffText})
- bigpatch.diffs = bigpatch.diffs[1:]
- } else {
- // Deletion or equality. Only take as much as we can stomach.
- diffText = diffText[:min(len(diffText), patchSize-patch.length1-dmp.PatchMargin)]
-
- patch.length1 += len(diffText)
- start1 += len(diffText)
- if diffType == DiffEqual {
- patch.length2 += len(diffText)
- start2 += len(diffText)
- } else {
- empty = false
- }
- patch.diffs = append(patch.diffs, Diff{diffType, diffText})
- if diffText == bigpatch.diffs[0].Text {
- bigpatch.diffs = bigpatch.diffs[1:]
- } else {
- bigpatch.diffs[0].Text =
- bigpatch.diffs[0].Text[len(diffText):]
- }
- }
- }
- // Compute the head context for the next patch.
- precontext = dmp.DiffText2(patch.diffs)
- precontext = precontext[max(0, len(precontext)-dmp.PatchMargin):]
-
- postcontext := ""
- // Append the end context for this patch.
- if len(dmp.DiffText1(bigpatch.diffs)) > dmp.PatchMargin {
- postcontext = dmp.DiffText1(bigpatch.diffs)[:dmp.PatchMargin]
- } else {
- postcontext = dmp.DiffText1(bigpatch.diffs)
- }
-
- if len(postcontext) != 0 {
- patch.length1 += len(postcontext)
- patch.length2 += len(postcontext)
- if len(patch.diffs) != 0 && patch.diffs[len(patch.diffs)-1].Type == DiffEqual {
- patch.diffs[len(patch.diffs)-1].Text += postcontext
- } else {
- patch.diffs = append(patch.diffs, Diff{DiffEqual, postcontext})
- }
- }
- if !empty {
- x++
- patches = append(patches[:x], append([]Patch{patch}, patches[x:]...)...)
- }
- }
- }
- return patches
-}
-
-// PatchToText takes a list of patches and returns a textual representation.
-func (dmp *DiffMatchPatch) PatchToText(patches []Patch) string {
- var text bytes.Buffer
- for _, aPatch := range patches {
- _, _ = text.WriteString(aPatch.String())
- }
- return text.String()
-}
-
-// PatchFromText parses a textual representation of patches and returns a List of Patch
-// objects.
-func (dmp *DiffMatchPatch) PatchFromText(textline string) ([]Patch, error) {
- patches := []Patch{}
- if len(textline) == 0 {
- return patches, nil
- }
- text := strings.Split(textline, "\n")
- textPointer := 0
- patchHeader := regexp.MustCompile("^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$")
-
- var patch Patch
- var sign uint8
- var line string
- for textPointer < len(text) {
-
- if !patchHeader.MatchString(text[textPointer]) {
- return patches, errors.New("Invalid patch string: " + text[textPointer])
- }
-
- patch = Patch{}
- m := patchHeader.FindStringSubmatch(text[textPointer])
-
- patch.start1, _ = strconv.Atoi(m[1])
- if len(m[2]) == 0 {
- patch.start1--
- patch.length1 = 1
- } else if m[2] == "0" {
- patch.length1 = 0
- } else {
- patch.start1--
- patch.length1, _ = strconv.Atoi(m[2])
- }
-
- patch.start2, _ = strconv.Atoi(m[3])
-
- if len(m[4]) == 0 {
- patch.start2--
- patch.length2 = 1
- } else if m[4] == "0" {
- patch.length2 = 0
- } else {
- patch.start2--
- patch.length2, _ = strconv.Atoi(m[4])
- }
- textPointer++
-
- for textPointer < len(text) {
- if len(text[textPointer]) > 0 {
- sign = text[textPointer][0]
- } else {
- textPointer++
- continue
- }
-
- line = text[textPointer][1:]
- line = strings.Replace(line, "+", "%2b", -1)
- line, _ = url.QueryUnescape(line)
- if sign == '-' {
- // Deletion.
- patch.diffs = append(patch.diffs, Diff{DiffDelete, line})
- } else if sign == '+' {
- // Insertion.
- patch.diffs = append(patch.diffs, Diff{DiffInsert, line})
- } else if sign == ' ' {
- // Minor equality.
- patch.diffs = append(patch.diffs, Diff{DiffEqual, line})
- } else if sign == '@' {
- // Start of next patch.
- break
- } else {
- // WTF?
- return patches, errors.New("Invalid patch mode '" + string(sign) + "' in: " + string(line))
- }
- textPointer++
- }
-
- patches = append(patches, patch)
- }
- return patches, nil
+ return diffs, nil
}
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/diffmatchpatch.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/diffmatchpatch.go
new file mode 100644
index 0000000000..d3acc32ce1
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/diffmatchpatch.go
@@ -0,0 +1,46 @@
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
+// Package diffmatchpatch offers robust algorithms to perform the operations required for synchronizing plain text.
+package diffmatchpatch
+
+import (
+ "time"
+)
+
+// DiffMatchPatch holds the configuration for diff-match-patch operations.
+type DiffMatchPatch struct {
+ // Number of seconds to map a diff before giving up (0 for infinity).
+ DiffTimeout time.Duration
+ // Cost of an empty edit operation in terms of edit characters.
+ DiffEditCost int
+ // How far to search for a match (0 = exact location, 1000+ = broad match). A match this many characters away from the expected location will add 1.0 to the score (0.0 is a perfect match).
+ MatchDistance int
+ // When deleting a large block of text (over ~64 characters), how close do the contents have to be to match the expected contents. (0.0 = perfection, 1.0 = very loose). Note that MatchThreshold controls how closely the end points of a delete need to match.
+ PatchDeleteThreshold float64
+ // Chunk size for context length.
+ PatchMargin int
+ // The number of bits in an int.
+ MatchMaxBits int
+ // At what point is no match declared (0.0 = perfection, 1.0 = very loose).
+ MatchThreshold float64
+}
+
+// New creates a new DiffMatchPatch object with default parameters.
+func New() *DiffMatchPatch {
+ // Defaults.
+ return &DiffMatchPatch{
+ DiffTimeout: time.Second,
+ DiffEditCost: 4,
+ MatchThreshold: 0.5,
+ MatchDistance: 1000,
+ PatchDeleteThreshold: 0.5,
+ PatchMargin: 4,
+ MatchMaxBits: 32,
+ }
+}
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/match.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/match.go
new file mode 100644
index 0000000000..17374e109f
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/match.go
@@ -0,0 +1,160 @@
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
+package diffmatchpatch
+
+import (
+ "math"
+)
+
+// MatchMain locates the best instance of 'pattern' in 'text' near 'loc'.
+// Returns -1 if no match found.
+func (dmp *DiffMatchPatch) MatchMain(text, pattern string, loc int) int {
+ // Check for null inputs not needed since null can't be passed in C#.
+
+ loc = int(math.Max(0, math.Min(float64(loc), float64(len(text)))))
+ if text == pattern {
+ // Shortcut (potentially not guaranteed by the algorithm)
+ return 0
+ } else if len(text) == 0 {
+ // Nothing to match.
+ return -1
+ } else if loc+len(pattern) <= len(text) && text[loc:loc+len(pattern)] == pattern {
+ // Perfect match at the perfect spot! (Includes case of null pattern)
+ return loc
+ }
+ // Do a fuzzy compare.
+ return dmp.MatchBitap(text, pattern, loc)
+}
+
+// MatchBitap locates the best instance of 'pattern' in 'text' near 'loc' using the Bitap algorithm.
+// Returns -1 if no match was found.
+func (dmp *DiffMatchPatch) MatchBitap(text, pattern string, loc int) int {
+ // Initialise the alphabet.
+ s := dmp.MatchAlphabet(pattern)
+
+ // Highest score beyond which we give up.
+ scoreThreshold := dmp.MatchThreshold
+ // Is there a nearby exact match? (speedup)
+ bestLoc := indexOf(text, pattern, loc)
+ if bestLoc != -1 {
+ scoreThreshold = math.Min(dmp.matchBitapScore(0, bestLoc, loc,
+ pattern), scoreThreshold)
+ // What about in the other direction? (speedup)
+ bestLoc = lastIndexOf(text, pattern, loc+len(pattern))
+ if bestLoc != -1 {
+ scoreThreshold = math.Min(dmp.matchBitapScore(0, bestLoc, loc,
+ pattern), scoreThreshold)
+ }
+ }
+
+ // Initialise the bit arrays.
+ matchmask := 1 << uint((len(pattern) - 1))
+ bestLoc = -1
+
+ var binMin, binMid int
+ binMax := len(pattern) + len(text)
+ lastRd := []int{}
+ for d := 0; d < len(pattern); d++ {
+ // Scan for the best match; each iteration allows for one more error. Run a binary search to determine how far from 'loc' we can stray at this error level.
+ binMin = 0
+ binMid = binMax
+ for binMin < binMid {
+ if dmp.matchBitapScore(d, loc+binMid, loc, pattern) <= scoreThreshold {
+ binMin = binMid
+ } else {
+ binMax = binMid
+ }
+ binMid = (binMax-binMin)/2 + binMin
+ }
+ // Use the result from this iteration as the maximum for the next.
+ binMax = binMid
+ start := int(math.Max(1, float64(loc-binMid+1)))
+ finish := int(math.Min(float64(loc+binMid), float64(len(text))) + float64(len(pattern)))
+
+ rd := make([]int, finish+2)
+ rd[finish+1] = (1 << uint(d)) - 1
+
+ for j := finish; j >= start; j-- {
+ var charMatch int
+ if len(text) <= j-1 {
+ // Out of range.
+ charMatch = 0
+ } else if _, ok := s[text[j-1]]; !ok {
+ charMatch = 0
+ } else {
+ charMatch = s[text[j-1]]
+ }
+
+ if d == 0 {
+ // First pass: exact match.
+ rd[j] = ((rd[j+1] << 1) | 1) & charMatch
+ } else {
+ // Subsequent passes: fuzzy match.
+ rd[j] = ((rd[j+1]<<1)|1)&charMatch | (((lastRd[j+1] | lastRd[j]) << 1) | 1) | lastRd[j+1]
+ }
+ if (rd[j] & matchmask) != 0 {
+ score := dmp.matchBitapScore(d, j-1, loc, pattern)
+ // This match will almost certainly be better than any existing match. But check anyway.
+ if score <= scoreThreshold {
+ // Told you so.
+ scoreThreshold = score
+ bestLoc = j - 1
+ if bestLoc > loc {
+ // When passing loc, don't exceed our current distance from loc.
+ start = int(math.Max(1, float64(2*loc-bestLoc)))
+ } else {
+ // Already passed loc, downhill from here on in.
+ break
+ }
+ }
+ }
+ }
+ if dmp.matchBitapScore(d+1, loc, loc, pattern) > scoreThreshold {
+ // No hope for a (better) match at greater error levels.
+ break
+ }
+ lastRd = rd
+ }
+ return bestLoc
+}
+
+// matchBitapScore computes and returns the score for a match with e errors and x location.
+func (dmp *DiffMatchPatch) matchBitapScore(e, x, loc int, pattern string) float64 {
+ accuracy := float64(e) / float64(len(pattern))
+ proximity := math.Abs(float64(loc - x))
+ if dmp.MatchDistance == 0 {
+ // Dodge divide by zero error.
+ if proximity == 0 {
+ return accuracy
+ }
+
+ return 1.0
+ }
+ return accuracy + (proximity / float64(dmp.MatchDistance))
+}
+
+// MatchAlphabet initialises the alphabet for the Bitap algorithm.
+func (dmp *DiffMatchPatch) MatchAlphabet(pattern string) map[byte]int {
+ s := map[byte]int{}
+ charPattern := []byte(pattern)
+ for _, c := range charPattern {
+ _, ok := s[c]
+ if !ok {
+ s[c] = 0
+ }
+ }
+ i := 0
+
+ for _, c := range charPattern {
+ value := s[c] | int(uint(1)<<uint((len(pattern)-i-1)))
+ s[c] = value
+ i++
+ }
+ return s
+}
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/mathutil.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/mathutil.go
new file mode 100644
index 0000000000..aed242bc6e
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/mathutil.go
@@ -0,0 +1,23 @@
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
+package diffmatchpatch
+
+func min(x, y int) int {
+ if x < y {
+ return x
+ }
+ return y
+}
+
+func max(x, y int) int {
+ if x > y {
+ return x
+ }
+ return y
+}
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go
new file mode 100644
index 0000000000..223c43c426
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go
@@ -0,0 +1,556 @@
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
+package diffmatchpatch
+
+import (
+ "bytes"
+ "errors"
+ "math"
+ "net/url"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// Patch represents one patch operation.
+type Patch struct {
+ diffs []Diff
+ Start1 int
+ Start2 int
+ Length1 int
+ Length2 int
+}
+
+// String emulates GNU diff's format.
+// Header: @@ -382,8 +481,9 @@
+// Indices are printed as 1-based, not 0-based.
+func (p *Patch) String() string {
+ var coords1, coords2 string
+
+ if p.Length1 == 0 {
+ coords1 = strconv.Itoa(p.Start1) + ",0"
+ } else if p.Length1 == 1 {
+ coords1 = strconv.Itoa(p.Start1 + 1)
+ } else {
+ coords1 = strconv.Itoa(p.Start1+1) + "," + strconv.Itoa(p.Length1)
+ }
+
+ if p.Length2 == 0 {
+ coords2 = strconv.Itoa(p.Start2) + ",0"
+ } else if p.Length2 == 1 {
+ coords2 = strconv.Itoa(p.Start2 + 1)
+ } else {
+ coords2 = strconv.Itoa(p.Start2+1) + "," + strconv.Itoa(p.Length2)
+ }
+
+ var text bytes.Buffer
+ _, _ = text.WriteString("@@ -" + coords1 + " +" + coords2 + " @@\n")
+
+ // Escape the body of the patch with %xx notation.
+ for _, aDiff := range p.diffs {
+ switch aDiff.Type {
+ case DiffInsert:
+ _, _ = text.WriteString("+")
+ case DiffDelete:
+ _, _ = text.WriteString("-")
+ case DiffEqual:
+ _, _ = text.WriteString(" ")
+ }
+
+ _, _ = text.WriteString(strings.Replace(url.QueryEscape(aDiff.Text), "+", " ", -1))
+ _, _ = text.WriteString("\n")
+ }
+
+ return unescaper.Replace(text.String())
+}
+
+// PatchAddContext increases the context until it is unique, but doesn't let the pattern expand beyond MatchMaxBits.
+func (dmp *DiffMatchPatch) PatchAddContext(patch Patch, text string) Patch {
+ if len(text) == 0 {
+ return patch
+ }
+
+ pattern := text[patch.Start2 : patch.Start2+patch.Length1]
+ padding := 0
+
+ // Look for the first and last matches of pattern in text. If two different matches are found, increase the pattern length.
+ for strings.Index(text, pattern) != strings.LastIndex(text, pattern) &&
+ len(pattern) < dmp.MatchMaxBits-2*dmp.PatchMargin {
+ padding += dmp.PatchMargin
+ maxStart := max(0, patch.Start2-padding)
+ minEnd := min(len(text), patch.Start2+patch.Length1+padding)
+ pattern = text[maxStart:minEnd]
+ }
+ // Add one chunk for good luck.
+ padding += dmp.PatchMargin
+
+ // Add the prefix.
+ prefix := text[max(0, patch.Start2-padding):patch.Start2]
+ if len(prefix) != 0 {
+ patch.diffs = append([]Diff{Diff{DiffEqual, prefix}}, patch.diffs...)
+ }
+ // Add the suffix.
+ suffix := text[patch.Start2+patch.Length1 : min(len(text), patch.Start2+patch.Length1+padding)]
+ if len(suffix) != 0 {
+ patch.diffs = append(patch.diffs, Diff{DiffEqual, suffix})
+ }
+
+ // Roll back the start points.
+ patch.Start1 -= len(prefix)
+ patch.Start2 -= len(prefix)
+ // Extend the lengths.
+ patch.Length1 += len(prefix) + len(suffix)
+ patch.Length2 += len(prefix) + len(suffix)
+
+ return patch
+}
+
+// PatchMake computes a list of patches.
+func (dmp *DiffMatchPatch) PatchMake(opt ...interface{}) []Patch {
+ if len(opt) == 1 {
+ diffs, _ := opt[0].([]Diff)
+ text1 := dmp.DiffText1(diffs)
+ return dmp.PatchMake(text1, diffs)
+ } else if len(opt) == 2 {
+ text1 := opt[0].(string)
+ switch t := opt[1].(type) {
+ case string:
+ diffs := dmp.DiffMain(text1, t, true)
+ if len(diffs) > 2 {
+ diffs = dmp.DiffCleanupSemantic(diffs)
+ diffs = dmp.DiffCleanupEfficiency(diffs)
+ }
+ return dmp.PatchMake(text1, diffs)
+ case []Diff:
+ return dmp.patchMake2(text1, t)
+ }
+ } else if len(opt) == 3 {
+ return dmp.PatchMake(opt[0], opt[2])
+ }
+ return []Patch{}
+}
+
+// patchMake2 computes a list of patches to turn text1 into text2.
+// text2 is not provided, diffs are the delta between text1 and text2.
+func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch {
+ // Check for null inputs not needed since null can't be passed in C#.
+ patches := []Patch{}
+ if len(diffs) == 0 {
+ return patches // Get rid of the null case.
+ }
+
+ patch := Patch{}
+ charCount1 := 0 // Number of characters into the text1 string.
+ charCount2 := 0 // Number of characters into the text2 string.
+ // Start with text1 (prepatchText) and apply the diffs until we arrive at text2 (postpatchText). We recreate the patches one by one to determine context info.
+ prepatchText := text1
+ postpatchText := text1
+
+ for i, aDiff := range diffs {
+ if len(patch.diffs) == 0 && aDiff.Type != DiffEqual {
+ // A new patch starts here.
+ patch.Start1 = charCount1
+ patch.Start2 = charCount2
+ }
+
+ switch aDiff.Type {
+ case DiffInsert:
+ patch.diffs = append(patch.diffs, aDiff)
+ patch.Length2 += len(aDiff.Text)
+ postpatchText = postpatchText[:charCount2] +
+ aDiff.Text + postpatchText[charCount2:]
+ case DiffDelete:
+ patch.Length1 += len(aDiff.Text)
+ patch.diffs = append(patch.diffs, aDiff)
+ postpatchText = postpatchText[:charCount2] + postpatchText[charCount2+len(aDiff.Text):]
+ case DiffEqual:
+ if len(aDiff.Text) <= 2*dmp.PatchMargin &&
+ len(patch.diffs) != 0 && i != len(diffs)-1 {
+ // Small equality inside a patch.
+ patch.diffs = append(patch.diffs, aDiff)
+ patch.Length1 += len(aDiff.Text)
+ patch.Length2 += len(aDiff.Text)
+ }
+ if len(aDiff.Text) >= 2*dmp.PatchMargin {
+ // Time for a new patch.
+ if len(patch.diffs) != 0 {
+ patch = dmp.PatchAddContext(patch, prepatchText)
+ patches = append(patches, patch)
+ patch = Patch{}
+ // Unlike Unidiff, our patch lists have a rolling context. http://code.google.com/p/google-diff-match-patch/wiki/Unidiff Update prepatch text & pos to reflect the application of the just completed patch.
+ prepatchText = postpatchText
+ charCount1 = charCount2
+ }
+ }
+ }
+
+ // Update the current character count.
+ if aDiff.Type != DiffInsert {
+ charCount1 += len(aDiff.Text)
+ }
+ if aDiff.Type != DiffDelete {
+ charCount2 += len(aDiff.Text)
+ }
+ }
+
+ // Pick up the leftover patch if not empty.
+ if len(patch.diffs) != 0 {
+ patch = dmp.PatchAddContext(patch, prepatchText)
+ patches = append(patches, patch)
+ }
+
+ return patches
+}
+
+// PatchDeepCopy returns an array that is identical to a given an array of patches.
+func (dmp *DiffMatchPatch) PatchDeepCopy(patches []Patch) []Patch {
+ patchesCopy := []Patch{}
+ for _, aPatch := range patches {
+ patchCopy := Patch{}
+ for _, aDiff := range aPatch.diffs {
+ patchCopy.diffs = append(patchCopy.diffs, Diff{
+ aDiff.Type,
+ aDiff.Text,
+ })
+ }
+ patchCopy.Start1 = aPatch.Start1
+ patchCopy.Start2 = aPatch.Start2
+ patchCopy.Length1 = aPatch.Length1
+ patchCopy.Length2 = aPatch.Length2
+ patchesCopy = append(patchesCopy, patchCopy)
+ }
+ return patchesCopy
+}
+
+// PatchApply merges a set of patches onto the text. Returns a patched text, as well as an array of true/false values indicating which patches were applied.
+func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []bool) {
+ if len(patches) == 0 {
+ return text, []bool{}
+ }
+
+ // Deep copy the patches so that no changes are made to originals.
+ patches = dmp.PatchDeepCopy(patches)
+
+ nullPadding := dmp.PatchAddPadding(patches)
+ text = nullPadding + text + nullPadding
+ patches = dmp.PatchSplitMax(patches)
+
+ x := 0
+ // delta keeps track of the offset between the expected and actual location of the previous patch. If there are patches expected at positions 10 and 20, but the first patch was found at 12, delta is 2 and the second patch has an effective expected position of 22.
+ delta := 0
+ results := make([]bool, len(patches))
+ for _, aPatch := range patches {
+ expectedLoc := aPatch.Start2 + delta
+ text1 := dmp.DiffText1(aPatch.diffs)
+ var startLoc int
+ endLoc := -1
+ if len(text1) > dmp.MatchMaxBits {
+ // PatchSplitMax will only provide an oversized pattern in the case of a monster delete.
+ startLoc = dmp.MatchMain(text, text1[:dmp.MatchMaxBits], expectedLoc)
+ if startLoc != -1 {
+ endLoc = dmp.MatchMain(text,
+ text1[len(text1)-dmp.MatchMaxBits:], expectedLoc+len(text1)-dmp.MatchMaxBits)
+ if endLoc == -1 || startLoc >= endLoc {
+ // Can't find valid trailing context. Drop this patch.
+ startLoc = -1
+ }
+ }
+ } else {
+ startLoc = dmp.MatchMain(text, text1, expectedLoc)
+ }
+ if startLoc == -1 {
+ // No match found. :(
+ results[x] = false
+ // Subtract the delta for this failed patch from subsequent patches.
+ delta -= aPatch.Length2 - aPatch.Length1
+ } else {
+ // Found a match. :)
+ results[x] = true
+ delta = startLoc - expectedLoc
+ var text2 string
+ if endLoc == -1 {
+ text2 = text[startLoc:int(math.Min(float64(startLoc+len(text1)), float64(len(text))))]
+ } else {
+ text2 = text[startLoc:int(math.Min(float64(endLoc+dmp.MatchMaxBits), float64(len(text))))]
+ }
+ if text1 == text2 {
+ // Perfect match, just shove the Replacement text in.
+ text = text[:startLoc] + dmp.DiffText2(aPatch.diffs) + text[startLoc+len(text1):]
+ } else {
+ // Imperfect match. Run a diff to get a framework of equivalent indices.
+ diffs := dmp.DiffMain(text1, text2, false)
+ if len(text1) > dmp.MatchMaxBits && float64(dmp.DiffLevenshtein(diffs))/float64(len(text1)) > dmp.PatchDeleteThreshold {
+ // The end points match, but the content is unacceptably bad.
+ results[x] = false
+ } else {
+ diffs = dmp.DiffCleanupSemanticLossless(diffs)
+ index1 := 0
+ for _, aDiff := range aPatch.diffs {
+ if aDiff.Type != DiffEqual {
+ index2 := dmp.DiffXIndex(diffs, index1)
+ if aDiff.Type == DiffInsert {
+ // Insertion
+ text = text[:startLoc+index2] + aDiff.Text + text[startLoc+index2:]
+ } else if aDiff.Type == DiffDelete {
+ // Deletion
+ startIndex := startLoc + index2
+ text = text[:startIndex] +
+ text[startIndex+dmp.DiffXIndex(diffs, index1+len(aDiff.Text))-index2:]
+ }
+ }
+ if aDiff.Type != DiffDelete {
+ index1 += len(aDiff.Text)
+ }
+ }
+ }
+ }
+ }
+ x++
+ }
+ // Strip the padding off.
+ text = text[len(nullPadding) : len(nullPadding)+(len(text)-2*len(nullPadding))]
+ return text, results
+}
+
+// PatchAddPadding adds some padding on text start and end so that edges can match something.
+// Intended to be called only from within patchApply.
+func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string {
+ paddingLength := dmp.PatchMargin
+ nullPadding := ""
+ for x := 1; x <= paddingLength; x++ {
+ nullPadding += string(x)
+ }
+
+ // Bump all the patches forward.
+ for i := range patches {
+ patches[i].Start1 += paddingLength
+ patches[i].Start2 += paddingLength
+ }
+
+ // Add some padding on start of first diff.
+ if len(patches[0].diffs) == 0 || patches[0].diffs[0].Type != DiffEqual {
+ // Add nullPadding equality.
+ patches[0].diffs = append([]Diff{Diff{DiffEqual, nullPadding}}, patches[0].diffs...)
+ patches[0].Start1 -= paddingLength // Should be 0.
+ patches[0].Start2 -= paddingLength // Should be 0.
+ patches[0].Length1 += paddingLength
+ patches[0].Length2 += paddingLength
+ } else if paddingLength > len(patches[0].diffs[0].Text) {
+ // Grow first equality.
+ extraLength := paddingLength - len(patches[0].diffs[0].Text)
+ patches[0].diffs[0].Text = nullPadding[len(patches[0].diffs[0].Text):] + patches[0].diffs[0].Text
+ patches[0].Start1 -= extraLength
+ patches[0].Start2 -= extraLength
+ patches[0].Length1 += extraLength
+ patches[0].Length2 += extraLength
+ }
+
+ // Add some padding on end of last diff.
+ last := len(patches) - 1
+ if len(patches[last].diffs) == 0 || patches[last].diffs[len(patches[last].diffs)-1].Type != DiffEqual {
+ // Add nullPadding equality.
+ patches[last].diffs = append(patches[last].diffs, Diff{DiffEqual, nullPadding})
+ patches[last].Length1 += paddingLength
+ patches[last].Length2 += paddingLength
+ } else if paddingLength > len(patches[last].diffs[len(patches[last].diffs)-1].Text) {
+ // Grow last equality.
+ lastDiff := patches[last].diffs[len(patches[last].diffs)-1]
+ extraLength := paddingLength - len(lastDiff.Text)
+ patches[last].diffs[len(patches[last].diffs)-1].Text += nullPadding[:extraLength]
+ patches[last].Length1 += extraLength
+ patches[last].Length2 += extraLength
+ }
+
+ return nullPadding
+}
+
+// PatchSplitMax looks through the patches and breaks up any which are longer than the maximum limit of the match algorithm.
+// Intended to be called only from within patchApply.
+func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) []Patch {
+ patchSize := dmp.MatchMaxBits
+ for x := 0; x < len(patches); x++ {
+ if patches[x].Length1 <= patchSize {
+ continue
+ }
+ bigpatch := patches[x]
+ // Remove the big old patch.
+ patches = append(patches[:x], patches[x+1:]...)
+ x--
+
+ Start1 := bigpatch.Start1
+ Start2 := bigpatch.Start2
+ precontext := ""
+ for len(bigpatch.diffs) != 0 {
+ // Create one of several smaller patches.
+ patch := Patch{}
+ empty := true
+ patch.Start1 = Start1 - len(precontext)
+ patch.Start2 = Start2 - len(precontext)
+ if len(precontext) != 0 {
+ patch.Length1 = len(precontext)
+ patch.Length2 = len(precontext)
+ patch.diffs = append(patch.diffs, Diff{DiffEqual, precontext})
+ }
+ for len(bigpatch.diffs) != 0 && patch.Length1 < patchSize-dmp.PatchMargin {
+ diffType := bigpatch.diffs[0].Type
+ diffText := bigpatch.diffs[0].Text
+ if diffType == DiffInsert {
+ // Insertions are harmless.
+ patch.Length2 += len(diffText)
+ Start2 += len(diffText)
+ patch.diffs = append(patch.diffs, bigpatch.diffs[0])
+ bigpatch.diffs = bigpatch.diffs[1:]
+ empty = false
+ } else if diffType == DiffDelete && len(patch.diffs) == 1 && patch.diffs[0].Type == DiffEqual && len(diffText) > 2*patchSize {
+ // This is a large deletion. Let it pass in one chunk.
+ patch.Length1 += len(diffText)
+ Start1 += len(diffText)
+ empty = false
+ patch.diffs = append(patch.diffs, Diff{diffType, diffText})
+ bigpatch.diffs = bigpatch.diffs[1:]
+ } else {
+ // Deletion or equality. Only take as much as we can stomach.
+ diffText = diffText[:min(len(diffText), patchSize-patch.Length1-dmp.PatchMargin)]
+
+ patch.Length1 += len(diffText)
+ Start1 += len(diffText)
+ if diffType == DiffEqual {
+ patch.Length2 += len(diffText)
+ Start2 += len(diffText)
+ } else {
+ empty = false
+ }
+ patch.diffs = append(patch.diffs, Diff{diffType, diffText})
+ if diffText == bigpatch.diffs[0].Text {
+ bigpatch.diffs = bigpatch.diffs[1:]
+ } else {
+ bigpatch.diffs[0].Text =
+ bigpatch.diffs[0].Text[len(diffText):]
+ }
+ }
+ }
+ // Compute the head context for the next patch.
+ precontext = dmp.DiffText2(patch.diffs)
+ precontext = precontext[max(0, len(precontext)-dmp.PatchMargin):]
+
+ postcontext := ""
+ // Append the end context for this patch.
+ if len(dmp.DiffText1(bigpatch.diffs)) > dmp.PatchMargin {
+ postcontext = dmp.DiffText1(bigpatch.diffs)[:dmp.PatchMargin]
+ } else {
+ postcontext = dmp.DiffText1(bigpatch.diffs)
+ }
+
+ if len(postcontext) != 0 {
+ patch.Length1 += len(postcontext)
+ patch.Length2 += len(postcontext)
+ if len(patch.diffs) != 0 && patch.diffs[len(patch.diffs)-1].Type == DiffEqual {
+ patch.diffs[len(patch.diffs)-1].Text += postcontext
+ } else {
+ patch.diffs = append(patch.diffs, Diff{DiffEqual, postcontext})
+ }
+ }
+ if !empty {
+ x++
+ patches = append(patches[:x], append([]Patch{patch}, patches[x:]...)...)
+ }
+ }
+ }
+ return patches
+}
+
+// PatchToText takes a list of patches and returns a textual representation.
+func (dmp *DiffMatchPatch) PatchToText(patches []Patch) string {
+ var text bytes.Buffer
+ for _, aPatch := range patches {
+ _, _ = text.WriteString(aPatch.String())
+ }
+ return text.String()
+}
+
+// PatchFromText parses a textual representation of patches and returns a List of Patch objects.
+func (dmp *DiffMatchPatch) PatchFromText(textline string) ([]Patch, error) {
+ patches := []Patch{}
+ if len(textline) == 0 {
+ return patches, nil
+ }
+ text := strings.Split(textline, "\n")
+ textPointer := 0
+ patchHeader := regexp.MustCompile("^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$")
+
+ var patch Patch
+ var sign uint8
+ var line string
+ for textPointer < len(text) {
+
+ if !patchHeader.MatchString(text[textPointer]) {
+ return patches, errors.New("Invalid patch string: " + text[textPointer])
+ }
+
+ patch = Patch{}
+ m := patchHeader.FindStringSubmatch(text[textPointer])
+
+ patch.Start1, _ = strconv.Atoi(m[1])
+ if len(m[2]) == 0 {
+ patch.Start1--
+ patch.Length1 = 1
+ } else if m[2] == "0" {
+ patch.Length1 = 0
+ } else {
+ patch.Start1--
+ patch.Length1, _ = strconv.Atoi(m[2])
+ }
+
+ patch.Start2, _ = strconv.Atoi(m[3])
+
+ if len(m[4]) == 0 {
+ patch.Start2--
+ patch.Length2 = 1
+ } else if m[4] == "0" {
+ patch.Length2 = 0
+ } else {
+ patch.Start2--
+ patch.Length2, _ = strconv.Atoi(m[4])
+ }
+ textPointer++
+
+ for textPointer < len(text) {
+ if len(text[textPointer]) > 0 {
+ sign = text[textPointer][0]
+ } else {
+ textPointer++
+ continue
+ }
+
+ line = text[textPointer][1:]
+ line = strings.Replace(line, "+", "%2b", -1)
+ line, _ = url.QueryUnescape(line)
+ if sign == '-' {
+ // Deletion.
+ patch.diffs = append(patch.diffs, Diff{DiffDelete, line})
+ } else if sign == '+' {
+ // Insertion.
+ patch.diffs = append(patch.diffs, Diff{DiffInsert, line})
+ } else if sign == ' ' {
+ // Minor equality.
+ patch.diffs = append(patch.diffs, Diff{DiffEqual, line})
+ } else if sign == '@' {
+ // Start of next patch.
+ break
+ } else {
+ // WTF?
+ return patches, errors.New("Invalid patch mode '" + string(sign) + "' in: " + string(line))
+ }
+ textPointer++
+ }
+
+ patches = append(patches, patch)
+ }
+ return patches, nil
+}
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go
new file mode 100644
index 0000000000..265f29cc7e
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go
@@ -0,0 +1,88 @@
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
+package diffmatchpatch
+
+import (
+ "strings"
+ "unicode/utf8"
+)
+
+// unescaper unescapes selected chars for compatibility with JavaScript's encodeURI.
+// In speed critical applications this could be dropped since the receiving application will certainly decode these fine. Note that this function is case-sensitive. Thus "%3F" would not be unescaped. But this is ok because it is only called with the output of HttpUtility.UrlEncode which returns lowercase hex. Example: "%3f" -> "?", "%24" -> "$", etc.
+var unescaper = strings.NewReplacer(
+ "%21", "!", "%7E", "~", "%27", "'",
+ "%28", "(", "%29", ")", "%3B", ";",
+ "%2F", "/", "%3F", "?", "%3A", ":",
+ "%40", "@", "%26", "&", "%3D", "=",
+ "%2B", "+", "%24", "$", "%2C", ",", "%23", "#", "%2A", "*")
+
+// indexOf returns the first index of pattern in str, starting at str[i].
+func indexOf(str string, pattern string, i int) int {
+ if i > len(str)-1 {
+ return -1
+ }
+ if i <= 0 {
+ return strings.Index(str, pattern)
+ }
+ ind := strings.Index(str[i:], pattern)
+ if ind == -1 {
+ return -1
+ }
+ return ind + i
+}
+
+// lastIndexOf returns the last index of pattern in str, starting at str[i].
+func lastIndexOf(str string, pattern string, i int) int {
+ if i < 0 {
+ return -1
+ }
+ if i >= len(str) {
+ return strings.LastIndex(str, pattern)
+ }
+ _, size := utf8.DecodeRuneInString(str[i:])
+ return strings.LastIndex(str[:i+size], pattern)
+}
+
+// runesIndexOf returns the index of pattern in target, starting at target[i].
+func runesIndexOf(target, pattern []rune, i int) int {
+ if i > len(target)-1 {
+ return -1
+ }
+ if i <= 0 {
+ return runesIndex(target, pattern)
+ }
+ ind := runesIndex(target[i:], pattern)
+ if ind == -1 {
+ return -1
+ }
+ return ind + i
+}
+
+func runesEqual(r1, r2 []rune) bool {
+ if len(r1) != len(r2) {
+ return false
+ }
+ for i, c := range r1 {
+ if c != r2[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// runesIndex is the equivalent of strings.Index for rune slices.
+func runesIndex(r1, r2 []rune) int {
+ last := len(r1) - len(r2)
+ for i := 0; i <= last; i++ {
+ if runesEqual(r1[i:i+len(r2)], r2) {
+ return i
+ }
+ }
+ return -1
+}