Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 31 additions & 30 deletions diffmatchpatch/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
DiffInsert Operation = 1
// DiffEqual item represents an equal diff.
DiffEqual Operation = 0
// IndexSeparator is used to seperate the array indexes in an index string
IndexSeparator = ","
)

// Diff represents one diff operation
Expand Down Expand Up @@ -420,14 +418,11 @@
func (dmp *DiffMatchPatch) DiffCharsToLines(diffs []Diff, lineArray []string) []Diff {
hydrated := make([]Diff, 0, len(diffs))
for _, aDiff := range diffs {
chars := strings.Split(aDiff.Text, IndexSeparator)
text := make([]string, len(chars))
runes := []rune(aDiff.Text)
text := make([]string, len(runes))

for i, r := range chars {
i1, err := strconv.Atoi(r)
if err == nil {
text[i] = lineArray[i1]
}
for i, r := range runes {
text[i] = lineArray[runeToInt(r)]
}

aDiff.Text = strings.Join(text, "")
Expand Down Expand Up @@ -1168,13 +1163,28 @@

switch diff.Type {
case DiffInsert:
_, _ = buff.WriteString("\x1b[32m")
_, _ = buff.WriteString(text)
_, _ = buff.WriteString("\x1b[0m")
lines := strings.Split(text, "\n")
for i, line := range lines {
_, _ = buff.WriteString("\x1b[32m")
_, _ = buff.WriteString(line)
if i < len(lines)-1 {
_, _ = buff.WriteString("\x1b[0m\n")
} else {
_, _ = buff.WriteString("\x1b[0m")
}
}

case DiffDelete:
_, _ = buff.WriteString("\x1b[31m")
_, _ = buff.WriteString(text)
_, _ = buff.WriteString("\x1b[0m")
lines := strings.Split(text, "\n")
for i, line := range lines {
_, _ = buff.WriteString("\x1b[31m")
_, _ = buff.WriteString(line)
if i < len(lines)-1 {
_, _ = buff.WriteString("\x1b[0m\n")
} else {
_, _ = buff.WriteString("\x1b[0m")
}
}
case DiffEqual:
_, _ = buff.WriteString(text)
}
Expand Down Expand Up @@ -1334,30 +1344,21 @@

// diffLinesToStrings splits two texts into a list of strings. Each string represents one line.
func (dmp *DiffMatchPatch) diffLinesToStrings(text1, text2 string) (string, string, []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.
lineArray := []string{""} // e.g. lineArray[4] == 'Hello\n'

lineHash := make(map[string]int)
// Each string has the index of lineArray which it points to
//Each string has the index of lineArray which it points to

Check failure on line 1350 in diffmatchpatch/diff.go

View workflow job for this annotation

GitHub Actions / Run linting

File is not properly formatted (gofumpt)
strIndexArray1 := dmp.diffLinesToStringsMunge(text1, &lineArray, lineHash)
strIndexArray2 := dmp.diffLinesToStringsMunge(text2, &lineArray, lineHash)

return intArrayToString(strIndexArray1), intArrayToString(strIndexArray2), lineArray
}

// diffLinesToStringsMunge splits a text into an array of strings, and reduces the texts to a []string.
func (dmp *DiffMatchPatch) diffLinesToStringsMunge(
text string,
lineArray *[]string,
lineHash map[string]int,
) []uint32 {
// 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.
// diffLinesToStringsMunge splits a text into an array of strings, and reduces the texts to a []index.
func (dmp *DiffMatchPatch) diffLinesToStringsMunge(text string, lineArray *[]string, lineHash map[string]int) []index {
lineStart := 0
lineEnd := -1
strs := []uint32{}
strs := []index{}

for lineEnd < len(text)-1 {
lineEnd = indexOf(text, "\n", lineStart)
Expand All @@ -1371,11 +1372,11 @@
lineValue, ok := lineHash[line]

if ok {
strs = append(strs, uint32(lineValue))
strs = append(strs, index(lineValue))
} else {
*lineArray = append(*lineArray, line)
lineHash[line] = len(*lineArray) - 1
strs = append(strs, uint32(len(*lineArray)-1))
strs = append(strs, index(len(*lineArray)-1))
}
}

Expand Down
69 changes: 56 additions & 13 deletions diffmatchpatch/diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,12 @@
dmp := New()

for i, tc := range []TestCase{
{"", "alpha\r\nbeta\r\n\r\n\r\n", "", "1,2,3,3", []string{"", "alpha\r\n", "beta\r\n", "\r\n"}},
{"a", "b", "1", "2", []string{"", "a", "b"}},
{"", "alpha\r\nbeta\r\n\r\n\r\n", "", "\x01\x02\x03\x03", []string{"", "alpha\r\n", "beta\r\n", "\r\n"}},
{"a", "b", "\x01", "\x02", []string{"", "a", "b"}},
// Omit final newline.
{"alpha\nbeta\nalpha", "", "1,2,3", "", []string{"", "alpha\n", "beta\n", "alpha"}},
{"alpha\nbeta\nalpha", "", "\x01\x02\x03", "", []string{"", "alpha\n", "beta\n", "alpha"}},
// Same lines in Text1 and Text2
{"abc\ndefg\n12345\n", "abc\ndef\n12345\n678", "1,2,3", "1,4,3,5", []string{"", "abc\n", "defg\n", "12345\n", "def\n", "678"}},
{"abc\ndefg\n12345\n", "abc\ndef\n12345\n678", "\x01\x02\x03", "\x01\x04\x03\x05", []string{"", "abc\n", "defg\n", "12345\n", "def\n", "678"}},

Check failure on line 310 in diffmatchpatch/diff_test.go

View workflow job for this annotation

GitHub Actions / Run linting

The line is 145 characters long, which exceeds the maximum of 130 characters. (lll)
} {
actualChars1, actualChars2, actualLines := dmp.DiffLinesToChars(tc.Text1, tc.Text2)
assertEqual(t, tc.ExpectedChars1, actualChars1, fmt.Sprintf("Test case #%d, %#v", i, tc))
Expand All @@ -320,14 +320,14 @@
lineList := []string{
"", // Account for the initial empty element of the lines array.
}
var charList []string
var charList []index
for x := 1; x < n+1; x++ {
lineList = append(lineList, strconv.Itoa(x)+"\n")
charList = append(charList, strconv.Itoa(x))
charList = append(charList, index(x))
}
lines := strings.Join(lineList, "")
chars := strings.Join(charList[:], ",")
assertEqual(t, n, len(strings.Split(chars, ",")))
chars := indexesToString(charList)
assertEqual(t, n, len(charList))

actualChars1, actualChars2, actualLines := dmp.DiffLinesToChars(lines, "")
assertEqual(t, chars, actualChars1)
Expand All @@ -347,8 +347,8 @@
for i, tc := range []TestCase{
{
Diffs: []Diff{
{DiffEqual, "1,2,1"},
{DiffInsert, "2,1,2"},
{DiffEqual, "\x01\x02\x01"},
{DiffInsert, "\x02\x01\x02"},
},
Lines: []string{"", "alpha\n", "beta\n"},

Expand All @@ -367,13 +367,13 @@
lineList := []string{
"", // Account for the initial empty element of the lines array.
}
charList := []string{}
charList := []index{}
for x := 1; x <= n; x++ {
lineList = append(lineList, strconv.Itoa(x)+"\n")
charList = append(charList, strconv.Itoa(x))
charList = append(charList, index(x))
}
assertEqual(t, n, len(charList))
chars := strings.Join(charList[:], ",")
chars := indexesToString(charList)

actual := dmp.DiffCharsToLines([]Diff{{DiffDelete, chars}}, lineList)
assertEqual(t, []Diff{{DiffDelete, strings.Join(lineList, "")}}, actual)
Expand Down Expand Up @@ -1018,6 +1018,17 @@

Expected: "a\n\x1b[31m<B>b</B>\x1b[0m\x1b[32mc&d\x1b[0m",
},
{
Diffs: []Diff{
{Type: DiffEqual, Text: "a\n"},
{Type: DiffDelete, Text: "b\nc\n"},
{Type: DiffEqual, Text: "def"},
{Type: DiffInsert, Text: "\ng\nh"},
{Type: DiffEqual, Text: "\ni"},
},

Expected: "a\n\x1b[31mb\x1b[0m\n\x1b[31mc\x1b[0m\n\x1b[31m\x1b[0mdef\x1b[32m\x1b[0m\n\x1b[32mg\x1b[0m\n\x1b[32mh\x1b[0m\ni",
},
} {
actual := dmp.DiffPrettyText(tc.Diffs)
assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
Expand Down Expand Up @@ -1522,6 +1533,38 @@
assertEqual(t, true, len(diffs) > 0)
}

func TestDiffPartialLineIndex(t *testing.T) {
dmp := New()
t1, t2, tt := dmp.DiffLinesToChars(
`line 1
line 2
line 3
line 4
line 5
line 6
line 7
line 8
line 9
line 10 text1`,
`line 1
line 2
line 3
line 4
line 5
line 6
line 7
line 8
line 9
line 10 text2`)
diffs := dmp.DiffMain(t1, t2, false)
diffs = dmp.DiffCharsToLines(diffs, tt)
assertEqual(t, []Diff{
Diff{DiffEqual, "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\n"},
Diff{DiffDelete, "line 10 text1"},
Diff{DiffInsert, "line 10 text2"},
}, diffs)
}

func BenchmarkDiffMain(bench *testing.B) {
var r []Diff

Expand Down
32 changes: 32 additions & 0 deletions diffmatchpatch/index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package diffmatchpatch

type index uint32

const runeSkipStart = 0xd800

Check failure on line 5 in diffmatchpatch/index.go

View workflow job for this annotation

GitHub Actions / Run linting

File is not properly formatted (gofumpt)
const runeSkipEnd = 0xdfff + 1
const runeMax = 0x110000 // next invalid code point

func stringToIndex(text string) []index {
runes := []rune(text)
indexes := make([]index, len(runes))
for i, r := range runes {
if r < runeSkipEnd {
indexes[i] = index(r)
} else {
indexes[i] = index(r) - (runeSkipEnd - runeSkipStart)
}
}
return indexes
}

func indexesToString(indexes []index) string {
runes := make([]rune, len(indexes))
for i, index := range indexes {
if index < runeSkipStart {
runes[i] = rune(index)
} else {
runes[i] = rune(index + (runeSkipEnd - runeSkipStart))
}
}
return string(runes)
}
15 changes: 15 additions & 0 deletions diffmatchpatch/index_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package diffmatchpatch

import (
"testing"
)

func TestIndexConversion(t *testing.T) {
n := runeMax - (runeSkipEnd - runeSkipStart)
indexes := make([]index, n)
for i := 0; i < n; i++ {
indexes[i] = index(i)
}
indexes2 := stringToIndex(indexesToString(indexes))
assertEqual(t, indexes, indexes2)
}
25 changes: 25 additions & 0 deletions diffmatchpatch/patch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,3 +567,28 @@ func TestPatchApply(t *testing.T) {
)
}
}

func TestPatchMakeOutOfRangePanic(t *testing.T) {
text1 := `
1111111111111 000000
------------- ------
xxxxxxxxxxxxx ------
xxxxxxxxxxxxx ------
xxxxxxxxxxxxx xxxxxx
xxxxxxxxxxxxx ......
xxxxxxxxxxxxx 111111
xxxxxxxxxxxxx ??????
xxxxxxxxxxxxx 333333
xxxxxxxxxxxxx 555555
xxxxxxxxxx xxxxx
xxxxxxxxxx xxxxx
xxxxxxxxxx xxxxx
xxxxxxxxxx xxxxx
`
text2 := `
2222222222222 000000
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
dmp := New()
patches := dmp.PatchMake(text1, text2)
assertEqual(t, 6, len(patches), "TestPatchMakeOutOfRangePanic")
}
Loading
Loading