2017-07-11 05:53:06 +00:00
|
|
|
// Paul's Simple Diff Algorithm v 0.1
|
|
|
|
// (C) Paul Butler 2007 <http://www.paulbutler.org/>
|
|
|
|
// May be used and distributed under the zlib/libpng license.
|
|
|
|
// https://github.com/paulgb/simplediff/
|
2017-07-11 05:14:00 +00:00
|
|
|
package diff
|
|
|
|
|
|
|
|
import (
|
|
|
|
"html"
|
|
|
|
"io"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
var Separator string = "\n"
|
|
|
|
|
|
|
|
type Operation int8
|
|
|
|
|
|
|
|
const (
|
|
|
|
OP_INSERTED Operation = 1
|
|
|
|
OP_UNCHANGED = 2
|
|
|
|
OP_REMOVED = 3
|
|
|
|
)
|
|
|
|
|
|
|
|
type Instruction struct {
|
|
|
|
Op Operation
|
|
|
|
Content []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *Instruction) RenderHTML() string {
|
2017-07-11 05:53:06 +00:00
|
|
|
sc := strings.Join(this.Content, Separator)
|
|
|
|
if len(sc) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2017-07-11 05:14:00 +00:00
|
|
|
switch this.Op {
|
|
|
|
case OP_INSERTED:
|
2017-07-11 05:53:06 +00:00
|
|
|
return `<ins>` + sc + `</ins>`
|
2017-07-11 05:14:00 +00:00
|
|
|
case OP_REMOVED:
|
2017-07-11 05:53:06 +00:00
|
|
|
return `<del>` + sc + `</del>`
|
2017-07-11 05:14:00 +00:00
|
|
|
default: //OP_UNCHANGED:
|
2017-07-11 05:53:06 +00:00
|
|
|
return sc
|
2017-07-11 05:14:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-11 05:53:06 +00:00
|
|
|
type twoints struct {
|
|
|
|
O, N int
|
|
|
|
}
|
|
|
|
|
2017-07-11 05:14:00 +00:00
|
|
|
func Diff(o, n []string) []Instruction {
|
2017-07-11 06:25:49 +00:00
|
|
|
|
2017-07-11 05:53:06 +00:00
|
|
|
if len(o) == 0 && len(n) == 0 {
|
|
|
|
return []Instruction{}
|
|
|
|
}
|
2017-07-11 06:07:55 +00:00
|
|
|
if len(o) == 0 {
|
|
|
|
return []Instruction{{OP_INSERTED, n}}
|
|
|
|
}
|
|
|
|
if len(n) == 0 {
|
|
|
|
return []Instruction{{OP_REMOVED, o}}
|
|
|
|
}
|
2017-07-11 05:53:06 +00:00
|
|
|
|
2017-07-11 05:14:00 +00:00
|
|
|
maxl := 0
|
|
|
|
omax := 0
|
|
|
|
nmax := 0
|
2017-07-11 05:57:05 +00:00
|
|
|
matrix := make(map[twoints]int, len(o)+1)
|
2017-07-11 05:14:00 +00:00
|
|
|
|
|
|
|
for oindex, ovalue := range o {
|
|
|
|
|
|
|
|
// Find keys in new, where value matchines ovalue
|
|
|
|
nkeys := make([]int, 0)
|
|
|
|
for nindex, nvalue := range n {
|
|
|
|
if nvalue == ovalue {
|
|
|
|
nkeys = append(nkeys, nindex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build diff matrix
|
2017-07-11 05:57:05 +00:00
|
|
|
for _, nindex := range nkeys {
|
|
|
|
matrix[twoints{oindex, nindex}] = matrix[twoints{oindex - 1, nindex - 1}] + 1
|
2017-07-11 05:14:00 +00:00
|
|
|
|
2017-07-11 05:57:05 +00:00
|
|
|
if matrix[twoints{oindex, nindex}] > maxl {
|
|
|
|
maxl = matrix[twoints{oindex, nindex}]
|
2017-07-11 05:14:00 +00:00
|
|
|
omax = oindex + 1 - maxl
|
|
|
|
nmax = nindex + 1 - maxl
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if maxl == 0 {
|
|
|
|
return []Instruction{
|
2017-07-11 06:07:55 +00:00
|
|
|
{OP_REMOVED, o},
|
|
|
|
{OP_INSERTED, n},
|
2017-07-11 05:14:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-11 05:57:05 +00:00
|
|
|
ret := Diff(o[:omax], n[:nmax])
|
2017-07-11 06:25:49 +00:00
|
|
|
ret = append(ret, Instruction{OP_UNCHANGED, n[nmax : nmax+maxl]})
|
2017-07-11 05:53:06 +00:00
|
|
|
ret = append(ret, Diff(o[omax+maxl:], n[nmax+maxl:])...)
|
2017-07-11 05:14:00 +00:00
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// HtmlDiff splits o and n by newlines, and writes an HTML-safe version to the dest writer.
|
|
|
|
func HtmlDiff(o, n string, dest io.Writer) {
|
|
|
|
ops := Diff(strings.Split(html.EscapeString(o), Separator), strings.Split(html.EscapeString(n), Separator))
|
|
|
|
|
|
|
|
for _, op := range ops {
|
|
|
|
dest.Write([]byte(op.RenderHTML()))
|
|
|
|
}
|
|
|
|
}
|