// Paul's Simple Diff Algorithm v 0.1 // (C) Paul Butler 2007 // May be used and distributed under the zlib/libpng license. // https://github.com/paulgb/simplediff/ 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 { sc := strings.Join(this.Content, Separator) if len(sc) == 0 { return "" } switch this.Op { case OP_INSERTED: return `` + sc + `` case OP_REMOVED: return `` + sc + `` default: //OP_UNCHANGED: return sc } } type twoints struct { O, N int } func Diff(o, n []string) []Instruction { if len(o) == 0 && len(n) == 0 { return []Instruction{} } maxl := 0 omax := 0 nmax := 0 matrix := make(map[twoints]int, len(o)+1) 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 for _, nindex := range nkeys { matrix[twoints{oindex, nindex}] = matrix[twoints{oindex - 1, nindex - 1}] + 1 if matrix[twoints{oindex, nindex}] > maxl { maxl = matrix[twoints{oindex, nindex}] omax = oindex + 1 - maxl nmax = nindex + 1 - maxl } } } if maxl == 0 { return []Instruction{ Instruction{OP_REMOVED, o}, Instruction{OP_INSERTED, n}, } } ret := Diff(o[:omax], n[:nmax]) if maxl != nmax { ret = append(ret, Instruction{OP_UNCHANGED, n[nmax : maxl-nmax]}) } ret = append(ret, Diff(o[omax+maxl:], n[nmax+maxl:])...) 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())) } }