@ -16,6 +16,7 @@ import (
"net/url"
"net/url"
"os"
"os"
"os/exec"
"os/exec"
"regexp"
"sort"
"sort"
"strconv"
"strconv"
"strings"
"strings"
@ -180,55 +181,61 @@ var (
removedCodePrefix = [ ] byte ( ` <span class="removed-code"> ` )
removedCodePrefix = [ ] byte ( ` <span class="removed-code"> ` )
codeTagSuffix = [ ] byte ( ` </span> ` )
codeTagSuffix = [ ] byte ( ` </span> ` )
)
)
var addSpanRegex = regexp . MustCompile ( ` <span class="[a-z]*$ ` )
func diffToHTML ( fileName string , diffs [ ] diffmatchpatch . Diff , lineType DiffLineType ) template . HTML {
func diffToHTML ( fileName string , diffs [ ] diffmatchpatch . Diff , lineType DiffLineType ) template . HTML {
buf := bytes . NewBuffer ( nil )
buf := bytes . NewBuffer ( nil )
var addSpan bool
var addSpan string
for i := range diffs {
for i := range diffs {
switch {
switch {
case diffs [ i ] . Type == diffmatchpatch . DiffEqual :
case diffs [ i ] . Type == diffmatchpatch . DiffEqual :
// Looking for the case where our 3rd party diff library previously detected a string difference
// Looking for the case where our 3rd party diff library previously detected a string difference
// in the middle of a span class because we highlight them first. This happens when added/deleted code
// in the middle of a span class because we highlight them first. This happens when added/deleted code
// also changes the chroma class name. If found, just move the openining span code forward into the next section
// also changes the chroma class name, either partially or fully. If found, just move the openining span code forward into the next section
if addSpan {
// see TestDiffToHTML for examples
diffs [ i ] . Text = "<span class=\"" + diffs [ i ] . Text
if len ( addSpan ) > 0 {
diffs [ i ] . Text = addSpan + diffs [ i ] . Text
addSpan = ""
}
}
if strings . HasSuffix ( diffs [ i ] . Text , "<span class=\"" ) {
m := addSpanRegex . FindStringSubmatchIndex ( diffs [ i ] . Text )
addSpan = true
if m != nil {
buf . WriteString ( strings . TrimSuffix ( diffs [ i ] . Text , "<span class=\"" ) )
addSpan = diffs [ i ] . Text [ m [ 0 ] : m [ 1 ] ]
buf . WriteString ( strings . TrimSuffix ( diffs [ i ] . Text , addSpan ) )
} else {
} else {
addSpan = false
addSpan = ""
buf . WriteString ( getLineContent ( diffs [ i ] . Text ) )
buf . WriteString ( getLineContent ( diffs [ i ] . Text ) )
}
}
case diffs [ i ] . Type == diffmatchpatch . DiffInsert && lineType == DiffLineAdd :
case diffs [ i ] . Type == diffmatchpatch . DiffInsert && lineType == DiffLineAdd :
if addSpan {
if len ( addSpan ) > 0 {
addSpan = false
diffs [ i ] . Text = addSpan + diffs [ i ] . Text
diffs [ i ] . Text = "<span class=\ "" + diffs [ i ] . Text
addSpan = ""
}
}
// Print existing closing span first before opening added-code span so it doesn't unintentionally close it
// Print existing closing span first before opening added-code span so it doesn't unintentionally close it
if strings . HasPrefix ( diffs [ i ] . Text , "</span>" ) {
if strings . HasPrefix ( diffs [ i ] . Text , "</span>" ) {
buf . WriteString ( "</span>" )
buf . WriteString ( "</span>" )
diffs [ i ] . Text = strings . TrimPrefix ( diffs [ i ] . Text , "</span>" )
diffs [ i ] . Text = strings . TrimPrefix ( diffs [ i ] . Text , "</span>" )
}
}
if strings . HasSuffix ( diffs [ i ] . Text , "<span class=\"" ) {
m := addSpanRegex . FindStringSubmatchIndex ( diffs [ i ] . Text )
addSpan = true
if m != nil {
diffs [ i ] . Text = strings . TrimSuffix ( diffs [ i ] . Text , "<span class=\"" )
addSpan = diffs [ i ] . Text [ m [ 0 ] : m [ 1 ] ]
diffs [ i ] . Text = strings . TrimSuffix ( diffs [ i ] . Text , addSpan )
}
}
buf . Write ( addedCodePrefix )
buf . Write ( addedCodePrefix )
buf . WriteString ( getLineContent ( diffs [ i ] . Text ) )
buf . WriteString ( getLineContent ( diffs [ i ] . Text ) )
buf . Write ( codeTagSuffix )
buf . Write ( codeTagSuffix )
case diffs [ i ] . Type == diffmatchpatch . DiffDelete && lineType == DiffLineDel :
case diffs [ i ] . Type == diffmatchpatch . DiffDelete && lineType == DiffLineDel :
if addSpan {
if len ( addSpan ) > 0 {
addSpan = false
diffs [ i ] . Text = addSpan + diffs [ i ] . Text
diffs [ i ] . Text = "<span class=\ "" + diffs [ i ] . Text
addSpan = ""
}
}
if strings . HasPrefix ( diffs [ i ] . Text , "</span>" ) {
if strings . HasPrefix ( diffs [ i ] . Text , "</span>" ) {
buf . WriteString ( "</span>" )
buf . WriteString ( "</span>" )
diffs [ i ] . Text = strings . TrimPrefix ( diffs [ i ] . Text , "</span>" )
diffs [ i ] . Text = strings . TrimPrefix ( diffs [ i ] . Text , "</span>" )
}
}
if strings . HasSuffix ( diffs [ i ] . Text , "<span class=\"" ) {
m := addSpanRegex . FindStringSubmatchIndex ( diffs [ i ] . Text )
addSpan = true
if m != nil {
diffs [ i ] . Text = strings . TrimSuffix ( diffs [ i ] . Text , "<span class=\"" )
addSpan = diffs [ i ] . Text [ m [ 0 ] : m [ 1 ] ]
diffs [ i ] . Text = strings . TrimSuffix ( diffs [ i ] . Text , addSpan )
}
}
buf . Write ( removedCodePrefix )
buf . Write ( removedCodePrefix )
buf . WriteString ( getLineContent ( diffs [ i ] . Text ) )
buf . WriteString ( getLineContent ( diffs [ i ] . Text ) )