Skip to content

Commit 0171dda

Browse files
mrsdizzielafriks
andauthored
Improved diff syntax highlighting fix (#12455)
Make previous fix from #12238 more robust since I saw a case where a diff changes only a single character in a chroma class instead of the entire thing. Add another more complicated test to match. Co-authored-by: Lauris BH <lauris@nix.lv>
1 parent 96add8c commit 0171dda

File tree

2 files changed

+37
-20
lines changed

2 files changed

+37
-20
lines changed

services/gitdiff/gitdiff.go

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"net/url"
1717
"os"
1818
"os/exec"
19+
"regexp"
1920
"sort"
2021
"strconv"
2122
"strings"
@@ -180,55 +181,61 @@ var (
180181
removedCodePrefix = []byte(`<span class="removed-code">`)
181182
codeTagSuffix = []byte(`</span>`)
182183
)
184+
var addSpanRegex = regexp.MustCompile(`<span class="[a-z]*$`)
183185

184186
func diffToHTML(fileName string, diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML {
185187
buf := bytes.NewBuffer(nil)
186-
var addSpan bool
188+
var addSpan string
187189
for i := range diffs {
188190
switch {
189191
case diffs[i].Type == diffmatchpatch.DiffEqual:
190192
// Looking for the case where our 3rd party diff library previously detected a string difference
191193
// in the middle of a span class because we highlight them first. This happens when added/deleted code
192-
// also changes the chroma class name. If found, just move the openining span code forward into the next section
193-
if addSpan {
194-
diffs[i].Text = "<span class=\"" + diffs[i].Text
194+
// also changes the chroma class name, either partially or fully. If found, just move the openining span code forward into the next section
195+
// see TestDiffToHTML for examples
196+
if len(addSpan) > 0 {
197+
diffs[i].Text = addSpan + diffs[i].Text
198+
addSpan = ""
195199
}
196-
if strings.HasSuffix(diffs[i].Text, "<span class=\"") {
197-
addSpan = true
198-
buf.WriteString(strings.TrimSuffix(diffs[i].Text, "<span class=\""))
200+
m := addSpanRegex.FindStringSubmatchIndex(diffs[i].Text)
201+
if m != nil {
202+
addSpan = diffs[i].Text[m[0]:m[1]]
203+
buf.WriteString(strings.TrimSuffix(diffs[i].Text, addSpan))
199204
} else {
200-
addSpan = false
205+
addSpan = ""
201206
buf.WriteString(getLineContent(diffs[i].Text))
202207
}
203208
case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd:
204-
if addSpan {
205-
addSpan = false
206-
diffs[i].Text = "<span class=\"" + diffs[i].Text
209+
if len(addSpan) > 0 {
210+
diffs[i].Text = addSpan + diffs[i].Text
211+
addSpan = ""
207212
}
208213
// Print existing closing span first before opening added-code span so it doesn't unintentionally close it
209214
if strings.HasPrefix(diffs[i].Text, "</span>") {
210215
buf.WriteString("</span>")
211216
diffs[i].Text = strings.TrimPrefix(diffs[i].Text, "</span>")
212217
}
213-
if strings.HasSuffix(diffs[i].Text, "<span class=\"") {
214-
addSpan = true
215-
diffs[i].Text = strings.TrimSuffix(diffs[i].Text, "<span class=\"")
218+
m := addSpanRegex.FindStringSubmatchIndex(diffs[i].Text)
219+
if m != nil {
220+
addSpan = diffs[i].Text[m[0]:m[1]]
221+
diffs[i].Text = strings.TrimSuffix(diffs[i].Text, addSpan)
216222
}
217223
buf.Write(addedCodePrefix)
218224
buf.WriteString(getLineContent(diffs[i].Text))
219225
buf.Write(codeTagSuffix)
220226
case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel:
221-
if addSpan {
222-
addSpan = false
223-
diffs[i].Text = "<span class=\"" + diffs[i].Text
227+
if len(addSpan) > 0 {
228+
diffs[i].Text = addSpan + diffs[i].Text
229+
addSpan = ""
224230
}
225231
if strings.HasPrefix(diffs[i].Text, "</span>") {
226232
buf.WriteString("</span>")
227233
diffs[i].Text = strings.TrimPrefix(diffs[i].Text, "</span>")
228234
}
229-
if strings.HasSuffix(diffs[i].Text, "<span class=\"") {
230-
addSpan = true
231-
diffs[i].Text = strings.TrimSuffix(diffs[i].Text, "<span class=\"")
235+
m := addSpanRegex.FindStringSubmatchIndex(diffs[i].Text)
236+
if m != nil {
237+
addSpan = diffs[i].Text[m[0]:m[1]]
238+
diffs[i].Text = strings.TrimSuffix(diffs[i].Text, addSpan)
232239
}
233240
buf.Write(removedCodePrefix)
234241
buf.WriteString(getLineContent(diffs[i].Text))

services/gitdiff/gitdiff_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ func TestDiffToHTML(t *testing.T) {
5050
{Type: dmp.DiffInsert, Text: "</span> <span class=\"o\">||</span> <span class=\"nx\">r</span><span class=\"p\">.</span><span class=\"nx\">GuessLanguage</span><span class=\"p\">)"},
5151
{Type: dmp.DiffEqual, Text: "</span> <span class=\"p\">{</span>"},
5252
}, DiffLineAdd))
53+
54+
assertEqual(t, "<span class=\"nx\">tagURL</span> <span class=\"o\">:=</span> <span class=\"removed-code\"><span class=\"nx\">fmt</span><span class=\"p\">.</span><span class=\"nf\">Sprintf</span><span class=\"p\">(</span><span class=\"s\">&#34;## [%s](%s/%s/%s/%s?q=&amp;type=all&amp;state=closed&amp;milestone=%d) - %s&#34;</span><span class=\"p\">,</span> <span class=\"nx\">ge</span><span class=\"p\">.</span><span class=\"nx\">Milestone\"</span></span><span class=\"p\">,</span> <span class=\"nx\">ge</span><span class=\"p\">.</span><span class=\"nx\">BaseURL</span><span class=\"p\">,</span> <span class=\"nx\">ge</span><span class=\"p\">.</span><span class=\"nx\">Owner</span><span class=\"p\">,</span> <span class=\"nx\">ge</span><span class=\"p\">.</span><span class=\"nx\">Repo</span><span class=\"p\">,</span> <span class=\"nx\"><span class=\"removed-code\">from</span><span class=\"p\">,</span> <span class=\"nx\">milestoneID</span><span class=\"p\">,</span> <span class=\"nx\">time</span><span class=\"p\">.</span><span class=\"nf\">Now</span><span class=\"p\">(</span><span class=\"p\">)</span><span class=\"p\">.</span><span class=\"nf\">Format</span><span class=\"p\">(</span><span class=\"s\">&#34;2006-01-02&#34;</span><span class=\"p\">)</span></span><span class=\"p\">)</span>", diffToHTML("", []dmp.Diff{
55+
{Type: dmp.DiffEqual, Text: "<span class=\"nx\">tagURL</span> <span class=\"o\">:=</span> <span class=\"n"},
56+
{Type: dmp.DiffDelete, Text: "x\">fmt</span><span class=\"p\">.</span><span class=\"nf\">Sprintf</span><span class=\"p\">(</span><span class=\"s\">&#34;## [%s](%s/%s/%s/%s?q=&amp;type=all&amp;state=closed&amp;milestone=%d) - %s&#34;</span><span class=\"p\">,</span> <span class=\"nx\">ge</span><span class=\"p\">.</span><span class=\"nx\">Milestone\""},
57+
{Type: dmp.DiffInsert, Text: "f\">getGiteaTagURL</span><span class=\"p\">(</span><span class=\"nx\">client"},
58+
{Type: dmp.DiffEqual, Text: "</span><span class=\"p\">,</span> <span class=\"nx\">ge</span><span class=\"p\">.</span><span class=\"nx\">BaseURL</span><span class=\"p\">,</span> <span class=\"nx\">ge</span><span class=\"p\">.</span><span class=\"nx\">Owner</span><span class=\"p\">,</span> <span class=\"nx\">ge</span><span class=\"p\">.</span><span class=\"nx\">Repo</span><span class=\"p\">,</span> <span class=\"nx\">"},
59+
{Type: dmp.DiffDelete, Text: "from</span><span class=\"p\">,</span> <span class=\"nx\">milestoneID</span><span class=\"p\">,</span> <span class=\"nx\">time</span><span class=\"p\">.</span><span class=\"nf\">Now</span><span class=\"p\">(</span><span class=\"p\">)</span><span class=\"p\">.</span><span class=\"nf\">Format</span><span class=\"p\">(</span><span class=\"s\">&#34;2006-01-02&#34;</span><span class=\"p\">)"},
60+
{Type: dmp.DiffInsert, Text: "ge</span><span class=\"p\">.</span><span class=\"nx\">Milestone</span><span class=\"p\">,</span> <span class=\"nx\">from</span><span class=\"p\">,</span> <span class=\"nx\">milestoneID"},
61+
{Type: dmp.DiffEqual, Text: "</span><span class=\"p\">)</span>"},
62+
}, DiffLineDel))
5363
}
5464

5565
func TestParsePatch(t *testing.T) {

0 commit comments

Comments
 (0)