1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345 |
- package diffmatchpatch
- import (
- "bytes"
- "errors"
- "fmt"
- "html"
- "math"
- "net/url"
- "regexp"
- "strconv"
- "strings"
- "time"
- "unicode/utf8"
- )
- type Operation int8
- const (
-
- DiffDelete Operation = -1
-
- DiffInsert Operation = 1
-
- DiffEqual Operation = 0
- )
- type Diff struct {
- Type Operation
- Text string
- }
- func splice(slice []Diff, index int, amount int, elements ...Diff) []Diff {
- if len(elements) == amount {
-
- copy(slice[index:], elements)
- return slice
- }
- if len(elements) < amount {
-
-
- copy(slice[index:], elements)
-
- copy(slice[index+len(elements):], slice[index+amount:])
-
- end := len(slice) - amount + len(elements)
-
- tail := slice[end:]
- for i := range tail {
- tail[i] = Diff{}
- }
- return slice[:end]
- }
-
-
-
-
- need := len(slice) - amount + len(elements)
- for len(slice) < need {
- slice = append(slice, Diff{})
- }
-
- copy(slice[index+len(elements):], slice[index+amount:])
-
- copy(slice[index:], elements)
- return slice
- }
- func (dmp *DiffMatchPatch) DiffMain(text1, text2 string, checklines bool) []Diff {
- return dmp.DiffMainRunes([]rune(text1), []rune(text2), checklines)
- }
- func (dmp *DiffMatchPatch) DiffMainRunes(text1, text2 []rune, checklines bool) []Diff {
- var deadline time.Time
- if dmp.DiffTimeout > 0 {
- deadline = time.Now().Add(dmp.DiffTimeout)
- }
- return dmp.diffMainRunes(text1, text2, checklines, deadline)
- }
- func (dmp *DiffMatchPatch) diffMainRunes(text1, text2 []rune, checklines bool, deadline time.Time) []Diff {
- if runesEqual(text1, text2) {
- var diffs []Diff
- if len(text1) > 0 {
- diffs = append(diffs, Diff{DiffEqual, string(text1)})
- }
- return diffs
- }
-
- commonlength := commonPrefixLength(text1, text2)
- commonprefix := text1[:commonlength]
- text1 = text1[commonlength:]
- text2 = text2[commonlength:]
-
- commonlength = commonSuffixLength(text1, text2)
- commonsuffix := text1[len(text1)-commonlength:]
- text1 = text1[:len(text1)-commonlength]
- text2 = text2[:len(text2)-commonlength]
-
- diffs := dmp.diffCompute(text1, text2, checklines, deadline)
-
- if len(commonprefix) != 0 {
- diffs = append([]Diff{Diff{DiffEqual, string(commonprefix)}}, diffs...)
- }
- if len(commonsuffix) != 0 {
- diffs = append(diffs, Diff{DiffEqual, string(commonsuffix)})
- }
- return dmp.DiffCleanupMerge(diffs)
- }
- func (dmp *DiffMatchPatch) diffCompute(text1, text2 []rune, checklines bool, deadline time.Time) []Diff {
- diffs := []Diff{}
- if len(text1) == 0 {
-
- return append(diffs, Diff{DiffInsert, string(text2)})
- } else if len(text2) == 0 {
-
- return append(diffs, Diff{DiffDelete, string(text1)})
- }
- var longtext, shorttext []rune
- if len(text1) > len(text2) {
- longtext = text1
- shorttext = text2
- } else {
- longtext = text2
- shorttext = text1
- }
- if i := runesIndex(longtext, shorttext); i != -1 {
- op := DiffInsert
-
- if len(text1) > len(text2) {
- op = DiffDelete
- }
-
- return []Diff{
- Diff{op, string(longtext[:i])},
- Diff{DiffEqual, string(shorttext)},
- Diff{op, string(longtext[i+len(shorttext):])},
- }
- } else if len(shorttext) == 1 {
-
-
- return []Diff{
- Diff{DiffDelete, string(text1)},
- Diff{DiffInsert, string(text2)},
- }
-
- } else if hm := dmp.diffHalfMatch(text1, text2); hm != nil {
-
- text1A := hm[0]
- text1B := hm[1]
- text2A := hm[2]
- text2B := hm[3]
- midCommon := hm[4]
-
- diffsA := dmp.diffMainRunes(text1A, text2A, checklines, deadline)
- diffsB := dmp.diffMainRunes(text1B, text2B, checklines, deadline)
-
- diffs := diffsA
- diffs = append(diffs, Diff{DiffEqual, string(midCommon)})
- diffs = append(diffs, diffsB...)
- return diffs
- } else if checklines && len(text1) > 100 && len(text2) > 100 {
- return dmp.diffLineMode(text1, text2, deadline)
- }
- return dmp.diffBisect(text1, text2, deadline)
- }
- func (dmp *DiffMatchPatch) diffLineMode(text1, text2 []rune, deadline time.Time) []Diff {
-
- text1, text2, linearray := dmp.diffLinesToRunes(text1, text2)
- diffs := dmp.diffMainRunes(text1, text2, false, deadline)
-
- diffs = dmp.DiffCharsToLines(diffs, linearray)
-
- diffs = dmp.DiffCleanupSemantic(diffs)
-
-
- diffs = append(diffs, Diff{DiffEqual, ""})
- pointer := 0
- countDelete := 0
- countInsert := 0
-
- textDelete := ""
- textInsert := ""
- for pointer < len(diffs) {
- switch diffs[pointer].Type {
- case DiffInsert:
- countInsert++
- textInsert += diffs[pointer].Text
- case DiffDelete:
- countDelete++
- textDelete += diffs[pointer].Text
- case DiffEqual:
-
- if countDelete >= 1 && countInsert >= 1 {
-
- diffs = splice(diffs, pointer-countDelete-countInsert,
- countDelete+countInsert)
- pointer = pointer - countDelete - countInsert
- a := dmp.diffMainRunes([]rune(textDelete), []rune(textInsert), false, deadline)
- for j := len(a) - 1; j >= 0; j-- {
- diffs = splice(diffs, pointer, 0, a[j])
- }
- pointer = pointer + len(a)
- }
- countInsert = 0
- countDelete = 0
- textDelete = ""
- textInsert = ""
- }
- pointer++
- }
- return diffs[:len(diffs)-1]
- }
- func (dmp *DiffMatchPatch) DiffBisect(text1, text2 string, deadline time.Time) []Diff {
-
- return dmp.diffBisect([]rune(text1), []rune(text2), deadline)
- }
- func (dmp *DiffMatchPatch) diffBisect(runes1, runes2 []rune, deadline time.Time) []Diff {
-
- runes1Len, runes2Len := len(runes1), len(runes2)
- maxD := (runes1Len + runes2Len + 1) / 2
- vOffset := maxD
- vLength := 2 * maxD
- v1 := make([]int, vLength)
- v2 := make([]int, vLength)
- for i := range v1 {
- v1[i] = -1
- v2[i] = -1
- }
- v1[vOffset+1] = 0
- v2[vOffset+1] = 0
- delta := runes1Len - runes2Len
-
- front := (delta%2 != 0)
-
- k1start := 0
- k1end := 0
- k2start := 0
- k2end := 0
- for d := 0; d < maxD; d++ {
-
- if !deadline.IsZero() && d%16 == 0 && time.Now().After(deadline) {
- break
- }
-
- for k1 := -d + k1start; k1 <= d-k1end; k1 += 2 {
- k1Offset := vOffset + k1
- var x1 int
- if k1 == -d || (k1 != d && v1[k1Offset-1] < v1[k1Offset+1]) {
- x1 = v1[k1Offset+1]
- } else {
- x1 = v1[k1Offset-1] + 1
- }
- y1 := x1 - k1
- for x1 < runes1Len && y1 < runes2Len {
- if runes1[x1] != runes2[y1] {
- break
- }
- x1++
- y1++
- }
- v1[k1Offset] = x1
- if x1 > runes1Len {
-
- k1end += 2
- } else if y1 > runes2Len {
-
- k1start += 2
- } else if front {
- k2Offset := vOffset + delta - k1
- if k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] != -1 {
-
- x2 := runes1Len - v2[k2Offset]
- if x1 >= x2 {
-
- return dmp.diffBisectSplit(runes1, runes2, x1, y1, deadline)
- }
- }
- }
- }
-
- for k2 := -d + k2start; k2 <= d-k2end; k2 += 2 {
- k2Offset := vOffset + k2
- var x2 int
- if k2 == -d || (k2 != d && v2[k2Offset-1] < v2[k2Offset+1]) {
- x2 = v2[k2Offset+1]
- } else {
- x2 = v2[k2Offset-1] + 1
- }
- var y2 = x2 - k2
- for x2 < runes1Len && y2 < runes2Len {
- if runes1[runes1Len-x2-1] != runes2[runes2Len-y2-1] {
- break
- }
- x2++
- y2++
- }
- v2[k2Offset] = x2
- if x2 > runes1Len {
-
- k2end += 2
- } else if y2 > runes2Len {
-
- k2start += 2
- } else if !front {
- k1Offset := vOffset + delta - k2
- if k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] != -1 {
- x1 := v1[k1Offset]
- y1 := vOffset + x1 - k1Offset
-
- x2 = runes1Len - x2
- if x1 >= x2 {
-
- return dmp.diffBisectSplit(runes1, runes2, x1, y1, deadline)
- }
- }
- }
- }
- }
-
- return []Diff{
- Diff{DiffDelete, string(runes1)},
- Diff{DiffInsert, string(runes2)},
- }
- }
- func (dmp *DiffMatchPatch) diffBisectSplit(runes1, runes2 []rune, x, y int,
- deadline time.Time) []Diff {
- runes1a := runes1[:x]
- runes2a := runes2[:y]
- runes1b := runes1[x:]
- runes2b := runes2[y:]
-
- diffs := dmp.diffMainRunes(runes1a, runes2a, false, deadline)
- diffsb := dmp.diffMainRunes(runes1b, runes2b, false, deadline)
- return append(diffs, diffsb...)
- }
- func (dmp *DiffMatchPatch) DiffLinesToChars(text1, text2 string) (string, string, []string) {
- chars1, chars2, lineArray := dmp.DiffLinesToRunes(text1, text2)
- return string(chars1), string(chars2), lineArray
- }
- func (dmp *DiffMatchPatch) DiffLinesToRunes(text1, text2 string) ([]rune, []rune, []string) {
-
- lineArray := []string{""}
- lineHash := map[string]int{}
- chars1 := dmp.diffLinesToRunesMunge(text1, &lineArray, lineHash)
- chars2 := dmp.diffLinesToRunesMunge(text2, &lineArray, lineHash)
- return chars1, chars2, lineArray
- }
- func (dmp *DiffMatchPatch) diffLinesToRunes(text1, text2 []rune) ([]rune, []rune, []string) {
- return dmp.DiffLinesToRunes(string(text1), string(text2))
- }
- func (dmp *DiffMatchPatch) diffLinesToRunesMunge(text string, lineArray *[]string, lineHash map[string]int) []rune {
-
- lineStart := 0
- lineEnd := -1
- runes := []rune{}
- for lineEnd < len(text)-1 {
- lineEnd = indexOf(text, "\n", lineStart)
- if lineEnd == -1 {
- lineEnd = len(text) - 1
- }
- line := text[lineStart : lineEnd+1]
- lineStart = lineEnd + 1
- lineValue, ok := lineHash[line]
- if ok {
- runes = append(runes, rune(lineValue))
- } else {
- *lineArray = append(*lineArray, line)
- lineHash[line] = len(*lineArray) - 1
- runes = append(runes, rune(len(*lineArray)-1))
- }
- }
- return runes
- }
- func (dmp *DiffMatchPatch) DiffCharsToLines(diffs []Diff, lineArray []string) []Diff {
- hydrated := make([]Diff, 0, len(diffs))
- for _, aDiff := range diffs {
- chars := aDiff.Text
- text := make([]string, len(chars))
- for i, r := range chars {
- text[i] = lineArray[r]
- }
- aDiff.Text = strings.Join(text, "")
- hydrated = append(hydrated, aDiff)
- }
- return hydrated
- }
- func (dmp *DiffMatchPatch) DiffCommonPrefix(text1, text2 string) int {
-
- return commonPrefixLength([]rune(text1), []rune(text2))
- }
- func (dmp *DiffMatchPatch) DiffCommonSuffix(text1, text2 string) int {
-
- return commonSuffixLength([]rune(text1), []rune(text2))
- }
- func commonPrefixLength(text1, text2 []rune) int {
-
- n := 0
- for ; n < len(text1) && n < len(text2); n++ {
- if text1[n] != text2[n] {
- return n
- }
- }
- return n
- }
- func commonSuffixLength(text1, text2 []rune) int {
-
-
- i1 := len(text1)
- i2 := len(text2)
- for n := 0; ; n++ {
- i1--
- i2--
- if i1 < 0 || i2 < 0 || text1[i1] != text2[i2] {
- return n
- }
- }
- }
- func (dmp *DiffMatchPatch) DiffCommonOverlap(text1 string, text2 string) int {
-
- text1Length := len(text1)
- text2Length := len(text2)
-
- if text1Length == 0 || text2Length == 0 {
- return 0
- }
-
- if text1Length > text2Length {
- text1 = text1[text1Length-text2Length:]
- } else if text1Length < text2Length {
- text2 = text2[0:text1Length]
- }
- textLength := int(math.Min(float64(text1Length), float64(text2Length)))
-
- if text1 == text2 {
- return textLength
- }
-
- best := 0
- length := 1
- for {
- pattern := text1[textLength-length:]
- found := strings.Index(text2, pattern)
- if found == -1 {
- break
- }
- length += found
- if found == 0 || text1[textLength-length:] == text2[0:length] {
- best = length
- length++
- }
- }
- return best
- }
- func (dmp *DiffMatchPatch) DiffHalfMatch(text1, text2 string) []string {
-
- runeSlices := dmp.diffHalfMatch([]rune(text1), []rune(text2))
- if runeSlices == nil {
- return nil
- }
- result := make([]string, len(runeSlices))
- for i, r := range runeSlices {
- result[i] = string(r)
- }
- return result
- }
- func (dmp *DiffMatchPatch) diffHalfMatch(text1, text2 []rune) [][]rune {
- if dmp.DiffTimeout <= 0 {
-
- return nil
- }
- var longtext, shorttext []rune
- if len(text1) > len(text2) {
- longtext = text1
- shorttext = text2
- } else {
- longtext = text2
- shorttext = text1
- }
- if len(longtext) < 4 || len(shorttext)*2 < len(longtext) {
- return nil
- }
-
- hm1 := dmp.diffHalfMatchI(longtext, shorttext, int(float64(len(longtext)+3)/4))
-
- hm2 := dmp.diffHalfMatchI(longtext, shorttext, int(float64(len(longtext)+1)/2))
- hm := [][]rune{}
- if hm1 == nil && hm2 == nil {
- return nil
- } else if hm2 == nil {
- hm = hm1
- } else if hm1 == nil {
- hm = hm2
- } else {
-
- if len(hm1[4]) > len(hm2[4]) {
- hm = hm1
- } else {
- hm = hm2
- }
- }
-
- if len(text1) > len(text2) {
- return hm
- }
- return [][]rune{hm[2], hm[3], hm[0], hm[1], hm[4]}
- }
- func (dmp *DiffMatchPatch) diffHalfMatchI(l, s []rune, i int) [][]rune {
- var bestCommonA []rune
- var bestCommonB []rune
- var bestCommonLen int
- var bestLongtextA []rune
- var bestLongtextB []rune
- var bestShorttextA []rune
- var bestShorttextB []rune
-
- seed := l[i : i+len(l)/4]
- for j := runesIndexOf(s, seed, 0); j != -1; j = runesIndexOf(s, seed, j+1) {
- prefixLength := commonPrefixLength(l[i:], s[j:])
- suffixLength := commonSuffixLength(l[:i], s[:j])
- if bestCommonLen < suffixLength+prefixLength {
- bestCommonA = s[j-suffixLength : j]
- bestCommonB = s[j : j+prefixLength]
- bestCommonLen = len(bestCommonA) + len(bestCommonB)
- bestLongtextA = l[:i-suffixLength]
- bestLongtextB = l[i+prefixLength:]
- bestShorttextA = s[:j-suffixLength]
- bestShorttextB = s[j+prefixLength:]
- }
- }
- if bestCommonLen*2 < len(l) {
- return nil
- }
- return [][]rune{
- bestLongtextA,
- bestLongtextB,
- bestShorttextA,
- bestShorttextB,
- append(bestCommonA, bestCommonB...),
- }
- }
- func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff {
- changes := false
-
- equalities := make([]int, 0, len(diffs))
- var lastequality string
-
- var pointer int
-
- var lengthInsertions1, lengthDeletions1 int
-
- var lengthInsertions2, lengthDeletions2 int
- for pointer < len(diffs) {
- if diffs[pointer].Type == DiffEqual {
-
- equalities = append(equalities, pointer)
- lengthInsertions1 = lengthInsertions2
- lengthDeletions1 = lengthDeletions2
- lengthInsertions2 = 0
- lengthDeletions2 = 0
- lastequality = diffs[pointer].Text
- } else {
-
- if diffs[pointer].Type == DiffInsert {
- lengthInsertions2 += len(diffs[pointer].Text)
- } else {
- lengthDeletions2 += len(diffs[pointer].Text)
- }
-
- difference1 := int(math.Max(float64(lengthInsertions1), float64(lengthDeletions1)))
- difference2 := int(math.Max(float64(lengthInsertions2), float64(lengthDeletions2)))
- if len(lastequality) > 0 &&
- (len(lastequality) <= difference1) &&
- (len(lastequality) <= difference2) {
-
- insPoint := equalities[len(equalities)-1]
- diffs = splice(diffs, insPoint, 0, Diff{DiffDelete, lastequality})
-
- diffs[insPoint+1].Type = DiffInsert
-
- equalities = equalities[:len(equalities)-1]
- if len(equalities) > 0 {
- equalities = equalities[:len(equalities)-1]
- }
- pointer = -1
- if len(equalities) > 0 {
- pointer = equalities[len(equalities)-1]
- }
- lengthInsertions1 = 0
- lengthDeletions1 = 0
- lengthInsertions2 = 0
- lengthDeletions2 = 0
- lastequality = ""
- changes = true
- }
- }
- pointer++
- }
-
- if changes {
- diffs = dmp.DiffCleanupMerge(diffs)
- }
- diffs = dmp.DiffCleanupSemanticLossless(diffs)
-
-
-
-
-
-
- pointer = 1
- for pointer < len(diffs) {
- if diffs[pointer-1].Type == DiffDelete &&
- diffs[pointer].Type == DiffInsert {
- deletion := diffs[pointer-1].Text
- insertion := diffs[pointer].Text
- overlapLength1 := dmp.DiffCommonOverlap(deletion, insertion)
- overlapLength2 := dmp.DiffCommonOverlap(insertion, deletion)
- if overlapLength1 >= overlapLength2 {
- if float64(overlapLength1) >= float64(len(deletion))/2 ||
- float64(overlapLength1) >= float64(len(insertion))/2 {
-
- diffs = splice(diffs, pointer, 0, Diff{DiffEqual, insertion[:overlapLength1]})
- diffs[pointer-1].Text =
- deletion[0 : len(deletion)-overlapLength1]
- diffs[pointer+1].Text = insertion[overlapLength1:]
- pointer++
- }
- } else {
- if float64(overlapLength2) >= float64(len(deletion))/2 ||
- float64(overlapLength2) >= float64(len(insertion))/2 {
-
- overlap := Diff{DiffEqual, deletion[:overlapLength2]}
- diffs = splice(diffs, pointer, 0, overlap)
- diffs[pointer-1].Type = DiffInsert
- diffs[pointer-1].Text = insertion[0 : len(insertion)-overlapLength2]
- diffs[pointer+1].Type = DiffDelete
- diffs[pointer+1].Text = deletion[overlapLength2:]
- pointer++
- }
- }
- pointer++
- }
- pointer++
- }
- return diffs
- }
- 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 diffCleanupSemanticScore(one, two string) int {
- if len(one) == 0 || len(two) == 0 {
-
- return 6
- }
-
- 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 {
-
- return 5
- } else if lineBreak1 || lineBreak2 {
-
- return 4
- } else if nonAlphaNumeric1 && !whitespace1 && whitespace2 {
-
- return 3
- } else if whitespace1 || whitespace2 {
-
- return 2
- } else if nonAlphaNumeric1 || nonAlphaNumeric2 {
-
- return 1
- }
- return 0
- }
- func (dmp *DiffMatchPatch) DiffCleanupSemanticLossless(diffs []Diff) []Diff {
- pointer := 1
-
- for pointer < len(diffs)-1 {
- if diffs[pointer-1].Type == DiffEqual &&
- diffs[pointer+1].Type == DiffEqual {
-
- equality1 := diffs[pointer-1].Text
- edit := diffs[pointer].Text
- equality2 := diffs[pointer+1].Text
-
- commonOffset := dmp.DiffCommonSuffix(equality1, edit)
- if commonOffset > 0 {
- commonString := edit[len(edit)-commonOffset:]
- equality1 = equality1[0 : len(equality1)-commonOffset]
- edit = commonString + edit[:len(edit)-commonOffset]
- equality2 = commonString + equality2
- }
-
- bestEquality1 := equality1
- bestEdit := edit
- bestEquality2 := equality2
- bestScore := diffCleanupSemanticScore(equality1, edit) +
- diffCleanupSemanticScore(edit, equality2)
- for len(edit) != 0 && len(equality2) != 0 {
- _, sz := utf8.DecodeRuneInString(edit)
- if len(equality2) < sz || edit[:sz] != equality2[:sz] {
- break
- }
- equality1 += edit[:sz]
- edit = edit[sz:] + equality2[:sz]
- equality2 = equality2[sz:]
- score := diffCleanupSemanticScore(equality1, edit) +
- diffCleanupSemanticScore(edit, equality2)
-
- if score >= bestScore {
- bestScore = score
- bestEquality1 = equality1
- bestEdit = edit
- bestEquality2 = equality2
- }
- }
- if diffs[pointer-1].Text != bestEquality1 {
-
- if len(bestEquality1) != 0 {
- diffs[pointer-1].Text = bestEquality1
- } else {
- diffs = splice(diffs, pointer-1, 1)
- pointer--
- }
- diffs[pointer].Text = bestEdit
- if len(bestEquality2) != 0 {
- diffs[pointer+1].Text = bestEquality2
- } else {
- diffs = append(diffs[:pointer+1], diffs[pointer+2:]...)
- pointer--
- }
- }
- }
- pointer++
- }
- return diffs
- }
- func (dmp *DiffMatchPatch) DiffCleanupEfficiency(diffs []Diff) []Diff {
- changes := false
-
- type equality struct {
- data int
- next *equality
- }
- var equalities *equality
-
- lastequality := ""
- pointer := 0
-
- preIns := false
-
- preDel := false
-
- postIns := false
-
- postDel := false
- for pointer < len(diffs) {
- if diffs[pointer].Type == DiffEqual {
- if len(diffs[pointer].Text) < dmp.DiffEditCost &&
- (postIns || postDel) {
-
- equalities = &equality{
- data: pointer,
- next: equalities,
- }
- preIns = postIns
- preDel = postDel
- lastequality = diffs[pointer].Text
- } else {
-
- equalities = nil
- lastequality = ""
- }
- postIns = false
- postDel = false
- } else {
- if diffs[pointer].Type == DiffDelete {
- postDel = true
- } else {
- postIns = true
- }
-
-
-
-
-
-
- var sumPres int
- if preIns {
- sumPres++
- }
- if preDel {
- sumPres++
- }
- if postIns {
- sumPres++
- }
- if postDel {
- sumPres++
- }
- if len(lastequality) > 0 &&
- ((preIns && preDel && postIns && postDel) ||
- ((len(lastequality) < dmp.DiffEditCost/2) && sumPres == 3)) {
- insPoint := equalities.data
-
- diffs = splice(diffs, insPoint, 0, Diff{DiffDelete, lastequality})
-
- diffs[insPoint+1].Type = DiffInsert
-
- equalities = equalities.next
- lastequality = ""
- if preIns && preDel {
-
- postIns = true
- postDel = true
- equalities = nil
- } else {
- if equalities != nil {
- equalities = equalities.next
- }
- if equalities != nil {
- pointer = equalities.data
- } else {
- pointer = -1
- }
- postIns = false
- postDel = false
- }
- changes = true
- }
- }
- pointer++
- }
- if changes {
- diffs = dmp.DiffCleanupMerge(diffs)
- }
- return diffs
- }
- func (dmp *DiffMatchPatch) DiffCleanupMerge(diffs []Diff) []Diff {
-
- diffs = append(diffs, Diff{DiffEqual, ""})
- pointer := 0
- countDelete := 0
- countInsert := 0
- commonlength := 0
- textDelete := []rune(nil)
- textInsert := []rune(nil)
- for pointer < len(diffs) {
- switch diffs[pointer].Type {
- case DiffInsert:
- countInsert++
- textInsert = append(textInsert, []rune(diffs[pointer].Text)...)
- pointer++
- break
- case DiffDelete:
- countDelete++
- textDelete = append(textDelete, []rune(diffs[pointer].Text)...)
- pointer++
- break
- case DiffEqual:
-
- if countDelete+countInsert > 1 {
- if countDelete != 0 && countInsert != 0 {
-
- commonlength = commonPrefixLength(textInsert, textDelete)
- if commonlength != 0 {
- x := pointer - countDelete - countInsert
- if x > 0 && diffs[x-1].Type == DiffEqual {
- diffs[x-1].Text += string(textInsert[:commonlength])
- } else {
- diffs = append([]Diff{Diff{DiffEqual, string(textInsert[:commonlength])}}, diffs...)
- pointer++
- }
- textInsert = textInsert[commonlength:]
- textDelete = textDelete[commonlength:]
- }
-
- commonlength = commonSuffixLength(textInsert, textDelete)
- if commonlength != 0 {
- insertIndex := len(textInsert) - commonlength
- deleteIndex := len(textDelete) - commonlength
- diffs[pointer].Text = string(textInsert[insertIndex:]) + diffs[pointer].Text
- textInsert = textInsert[:insertIndex]
- textDelete = textDelete[:deleteIndex]
- }
- }
-
- if countDelete == 0 {
- diffs = splice(diffs, pointer-countInsert,
- countDelete+countInsert,
- Diff{DiffInsert, string(textInsert)})
- } else if countInsert == 0 {
- diffs = splice(diffs, pointer-countDelete,
- countDelete+countInsert,
- Diff{DiffDelete, string(textDelete)})
- } else {
- diffs = splice(diffs, pointer-countDelete-countInsert,
- countDelete+countInsert,
- Diff{DiffDelete, string(textDelete)},
- Diff{DiffInsert, string(textInsert)})
- }
- pointer = pointer - countDelete - countInsert + 1
- if countDelete != 0 {
- pointer++
- }
- if countInsert != 0 {
- pointer++
- }
- } else if pointer != 0 && diffs[pointer-1].Type == DiffEqual {
-
- diffs[pointer-1].Text += diffs[pointer].Text
- diffs = append(diffs[:pointer], diffs[pointer+1:]...)
- } else {
- pointer++
- }
- countInsert = 0
- countDelete = 0
- textDelete = nil
- textInsert = nil
- break
- }
- }
- if len(diffs[len(diffs)-1].Text) == 0 {
- diffs = diffs[0 : len(diffs)-1]
- }
-
- changes := false
- pointer = 1
-
- for pointer < (len(diffs) - 1) {
- if diffs[pointer-1].Type == DiffEqual &&
- diffs[pointer+1].Type == DiffEqual {
-
- if strings.HasSuffix(diffs[pointer].Text, diffs[pointer-1].Text) {
-
- diffs[pointer].Text = diffs[pointer-1].Text +
- diffs[pointer].Text[:len(diffs[pointer].Text)-len(diffs[pointer-1].Text)]
- diffs[pointer+1].Text = diffs[pointer-1].Text + diffs[pointer+1].Text
- diffs = splice(diffs, pointer-1, 1)
- changes = true
- } else if strings.HasPrefix(diffs[pointer].Text, diffs[pointer+1].Text) {
-
- diffs[pointer-1].Text += diffs[pointer+1].Text
- diffs[pointer].Text =
- diffs[pointer].Text[len(diffs[pointer+1].Text):] + diffs[pointer+1].Text
- diffs = splice(diffs, pointer+1, 1)
- changes = true
- }
- }
- pointer++
- }
-
- if changes {
- diffs = dmp.DiffCleanupMerge(diffs)
- }
- return diffs
- }
- func (dmp *DiffMatchPatch) DiffXIndex(diffs []Diff, loc int) int {
- chars1 := 0
- chars2 := 0
- lastChars1 := 0
- lastChars2 := 0
- lastDiff := Diff{}
- for i := 0; i < len(diffs); i++ {
- aDiff := diffs[i]
- if aDiff.Type != DiffInsert {
-
- chars1 += len(aDiff.Text)
- }
- if aDiff.Type != DiffDelete {
-
- chars2 += len(aDiff.Text)
- }
- if chars1 > loc {
-
- lastDiff = aDiff
- break
- }
- lastChars1 = chars1
- lastChars2 = chars2
- }
- if lastDiff.Type == DiffDelete {
-
- return lastChars2
- }
-
- return lastChars2 + (loc - lastChars1)
- }
- func (dmp *DiffMatchPatch) DiffPrettyHtml(diffs []Diff) string {
- var buff bytes.Buffer
- for _, diff := range diffs {
- text := strings.Replace(html.EscapeString(diff.Text), "\n", "¶<br>", -1)
- switch diff.Type {
- case DiffInsert:
- _, _ = buff.WriteString("<ins style=\"background:#e6ffe6;\">")
- _, _ = buff.WriteString(text)
- _, _ = buff.WriteString("</ins>")
- case DiffDelete:
- _, _ = buff.WriteString("<del style=\"background:#ffe6e6;\">")
- _, _ = buff.WriteString(text)
- _, _ = buff.WriteString("</del>")
- case DiffEqual:
- _, _ = buff.WriteString("<span>")
- _, _ = buff.WriteString(text)
- _, _ = buff.WriteString("</span>")
- }
- }
- return buff.String()
- }
- func (dmp *DiffMatchPatch) DiffPrettyText(diffs []Diff) string {
- var buff bytes.Buffer
- for _, diff := range diffs {
- text := diff.Text
- switch diff.Type {
- case DiffInsert:
- _, _ = buff.WriteString("\x1b[32m")
- _, _ = buff.WriteString(text)
- _, _ = buff.WriteString("\x1b[0m")
- case DiffDelete:
- _, _ = buff.WriteString("\x1b[31m")
- _, _ = buff.WriteString(text)
- _, _ = buff.WriteString("\x1b[0m")
- case DiffEqual:
- _, _ = buff.WriteString(text)
- }
- }
- return buff.String()
- }
- func (dmp *DiffMatchPatch) DiffText1(diffs []Diff) string {
-
- var text bytes.Buffer
- for _, aDiff := range diffs {
- if aDiff.Type != DiffInsert {
- _, _ = text.WriteString(aDiff.Text)
- }
- }
- return text.String()
- }
- func (dmp *DiffMatchPatch) DiffText2(diffs []Diff) string {
- var text bytes.Buffer
- for _, aDiff := range diffs {
- if aDiff.Type != DiffDelete {
- _, _ = text.WriteString(aDiff.Text)
- }
- }
- return text.String()
- }
- func (dmp *DiffMatchPatch) DiffLevenshtein(diffs []Diff) int {
- levenshtein := 0
- insertions := 0
- deletions := 0
- for _, aDiff := range diffs {
- switch aDiff.Type {
- case DiffInsert:
- insertions += utf8.RuneCountInString(aDiff.Text)
- case DiffDelete:
- deletions += utf8.RuneCountInString(aDiff.Text)
- case DiffEqual:
-
- levenshtein += max(insertions, deletions)
- insertions = 0
- deletions = 0
- }
- }
- levenshtein += max(insertions, deletions)
- return levenshtein
- }
- func (dmp *DiffMatchPatch) DiffToDelta(diffs []Diff) string {
- var text bytes.Buffer
- for _, aDiff := range diffs {
- switch aDiff.Type {
- case DiffInsert:
- _, _ = text.WriteString("+")
- _, _ = text.WriteString(strings.Replace(url.QueryEscape(aDiff.Text), "+", " ", -1))
- _, _ = text.WriteString("\t")
- break
- case DiffDelete:
- _, _ = text.WriteString("-")
- _, _ = text.WriteString(strconv.Itoa(utf8.RuneCountInString(aDiff.Text)))
- _, _ = text.WriteString("\t")
- break
- case DiffEqual:
- _, _ = text.WriteString("=")
- _, _ = text.WriteString(strconv.Itoa(utf8.RuneCountInString(aDiff.Text)))
- _, _ = text.WriteString("\t")
- break
- }
- }
- delta := text.String()
- if len(delta) != 0 {
-
- delta = delta[0 : utf8.RuneCountInString(delta)-1]
- delta = unescaper.Replace(delta)
- }
- return delta
- }
- func (dmp *DiffMatchPatch) DiffFromDelta(text1 string, delta string) (diffs []Diff, err error) {
- i := 0
- runes := []rune(text1)
- for _, token := range strings.Split(delta, "\t") {
- if len(token) == 0 {
-
- continue
- }
-
- param := token[1:]
- switch op := token[0]; op {
- case '+':
-
- param = strings.Replace(param, "+", "%2b", -1)
- param, err = url.QueryUnescape(param)
- if err != nil {
- return nil, 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 nil, err
- } else if n < 0 {
- return nil, errors.New("Negative number in DiffFromDelta: " + param)
- }
- i += int(n)
-
- if i > len(runes) {
- break
- }
-
- text := string(runes[i-int(n) : i])
- if op == '=' {
- diffs = append(diffs, Diff{DiffEqual, text})
- } else {
- diffs = append(diffs, Diff{DiffDelete, text})
- }
- default:
-
- return nil, errors.New("Invalid diff operation in DiffFromDelta: " + string(token[0]))
- }
- }
- if i != len(runes) {
- return nil, fmt.Errorf("Delta length (%v) is different from source text length (%v)", i, len(text1))
- }
- return diffs, nil
- }
|