123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- package diffmatchpatch
- import (
- "bytes"
- "errors"
- "math"
- "net/url"
- "regexp"
- "strconv"
- "strings"
- )
- type Patch struct {
- diffs []Diff
- Start1 int
- Start2 int
- Length1 int
- Length2 int
- }
- 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")
-
- 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())
- }
- 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
-
- 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]
- }
-
- padding += dmp.PatchMargin
-
- prefix := text[max(0, patch.Start2-padding):patch.Start2]
- if len(prefix) != 0 {
- patch.diffs = append([]Diff{Diff{DiffEqual, prefix}}, patch.diffs...)
- }
-
- 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})
- }
-
- patch.Start1 -= len(prefix)
- patch.Start2 -= len(prefix)
-
- patch.Length1 += len(prefix) + len(suffix)
- patch.Length2 += len(prefix) + len(suffix)
- return patch
- }
- 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{}
- }
- func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch {
-
- patches := []Patch{}
- if len(diffs) == 0 {
- return patches
- }
- patch := Patch{}
- charCount1 := 0
- charCount2 := 0
-
- prepatchText := text1
- postpatchText := text1
- for i, aDiff := range diffs {
- if len(patch.diffs) == 0 && aDiff.Type != DiffEqual {
-
- 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 {
-
- patch.diffs = append(patch.diffs, aDiff)
- patch.Length1 += len(aDiff.Text)
- patch.Length2 += len(aDiff.Text)
- }
- if len(aDiff.Text) >= 2*dmp.PatchMargin {
-
- if len(patch.diffs) != 0 {
- patch = dmp.PatchAddContext(patch, prepatchText)
- patches = append(patches, patch)
- patch = Patch{}
-
- prepatchText = postpatchText
- charCount1 = charCount2
- }
- }
- }
-
- if aDiff.Type != DiffInsert {
- charCount1 += len(aDiff.Text)
- }
- if aDiff.Type != DiffDelete {
- charCount2 += len(aDiff.Text)
- }
- }
-
- if len(patch.diffs) != 0 {
- patch = dmp.PatchAddContext(patch, prepatchText)
- patches = append(patches, patch)
- }
- return 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
- }
- func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []bool) {
- if len(patches) == 0 {
- return text, []bool{}
- }
-
- patches = dmp.PatchDeepCopy(patches)
- nullPadding := dmp.PatchAddPadding(patches)
- text = nullPadding + text + nullPadding
- patches = dmp.PatchSplitMax(patches)
- x := 0
-
- 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 {
-
- 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 {
-
- startLoc = -1
- }
- }
- } else {
- startLoc = dmp.MatchMain(text, text1, expectedLoc)
- }
- if startLoc == -1 {
-
- results[x] = false
-
- delta -= aPatch.Length2 - aPatch.Length1
- } else {
-
- 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 {
-
- text = text[:startLoc] + dmp.DiffText2(aPatch.diffs) + text[startLoc+len(text1):]
- } else {
-
- diffs := dmp.DiffMain(text1, text2, false)
- if len(text1) > dmp.MatchMaxBits && float64(dmp.DiffLevenshtein(diffs))/float64(len(text1)) > dmp.PatchDeleteThreshold {
-
- 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 {
-
- text = text[:startLoc+index2] + aDiff.Text + text[startLoc+index2:]
- } else if aDiff.Type == DiffDelete {
-
- 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++
- }
-
- text = text[len(nullPadding) : len(nullPadding)+(len(text)-2*len(nullPadding))]
- return text, results
- }
- func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string {
- paddingLength := dmp.PatchMargin
- nullPadding := ""
- for x := 1; x <= paddingLength; x++ {
- nullPadding += string(x)
- }
-
- for i := range patches {
- patches[i].Start1 += paddingLength
- patches[i].Start2 += paddingLength
- }
-
- if len(patches[0].diffs) == 0 || patches[0].diffs[0].Type != DiffEqual {
-
- patches[0].diffs = append([]Diff{Diff{DiffEqual, nullPadding}}, patches[0].diffs...)
- patches[0].Start1 -= paddingLength
- patches[0].Start2 -= paddingLength
- patches[0].Length1 += paddingLength
- patches[0].Length2 += paddingLength
- } else if paddingLength > len(patches[0].diffs[0].Text) {
-
- 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
- }
-
- last := len(patches) - 1
- if len(patches[last].diffs) == 0 || patches[last].diffs[len(patches[last].diffs)-1].Type != DiffEqual {
-
- 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) {
-
- 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
- }
- 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]
-
- patches = append(patches[:x], patches[x+1:]...)
- x--
- Start1 := bigpatch.Start1
- Start2 := bigpatch.Start2
- precontext := ""
- for len(bigpatch.diffs) != 0 {
-
- 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 {
-
- 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 {
-
- patch.Length1 += len(diffText)
- Start1 += len(diffText)
- empty = false
- patch.diffs = append(patch.diffs, Diff{diffType, diffText})
- bigpatch.diffs = bigpatch.diffs[1:]
- } else {
-
- 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):]
- }
- }
- }
-
- precontext = dmp.DiffText2(patch.diffs)
- precontext = precontext[max(0, len(precontext)-dmp.PatchMargin):]
- postcontext := ""
-
- 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
- }
- func (dmp *DiffMatchPatch) PatchToText(patches []Patch) string {
- var text bytes.Buffer
- for _, aPatch := range patches {
- _, _ = text.WriteString(aPatch.String())
- }
- return text.String()
- }
- 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 == '-' {
-
- patch.diffs = append(patch.diffs, Diff{DiffDelete, line})
- } else if sign == '+' {
-
- patch.diffs = append(patch.diffs, Diff{DiffInsert, line})
- } else if sign == ' ' {
-
- patch.diffs = append(patch.diffs, Diff{DiffEqual, line})
- } else if sign == '@' {
-
- break
- } else {
-
- return patches, errors.New("Invalid patch mode '" + string(sign) + "' in: " + string(line))
- }
- textPointer++
- }
- patches = append(patches, patch)
- }
- return patches, nil
- }
|