mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-04 05:18:25 +00:00 
			
		
		
		
	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>
This commit is contained in:
		@@ -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))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,6 +50,16 @@ func TestDiffToHTML(t *testing.T) {
 | 
				
			|||||||
		{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\">)"},
 | 
							{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\">)"},
 | 
				
			||||||
		{Type: dmp.DiffEqual, Text: "</span> <span class=\"p\">{</span>"},
 | 
							{Type: dmp.DiffEqual, Text: "</span> <span class=\"p\">{</span>"},
 | 
				
			||||||
	}, DiffLineAdd))
 | 
						}, DiffLineAdd))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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\">"## [%s](%s/%s/%s/%s?q=&type=all&state=closed&milestone=%d) - %s"</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\">"2006-01-02"</span><span class=\"p\">)</span></span><span class=\"p\">)</span>", diffToHTML("", []dmp.Diff{
 | 
				
			||||||
 | 
							{Type: dmp.DiffEqual, Text: "<span class=\"nx\">tagURL</span> <span class=\"o\">:=</span> <span class=\"n"},
 | 
				
			||||||
 | 
							{Type: dmp.DiffDelete, Text: "x\">fmt</span><span class=\"p\">.</span><span class=\"nf\">Sprintf</span><span class=\"p\">(</span><span class=\"s\">"## [%s](%s/%s/%s/%s?q=&type=all&state=closed&milestone=%d) - %s"</span><span class=\"p\">,</span> <span class=\"nx\">ge</span><span class=\"p\">.</span><span class=\"nx\">Milestone\""},
 | 
				
			||||||
 | 
							{Type: dmp.DiffInsert, Text: "f\">getGiteaTagURL</span><span class=\"p\">(</span><span class=\"nx\">client"},
 | 
				
			||||||
 | 
							{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\">"},
 | 
				
			||||||
 | 
							{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\">"2006-01-02"</span><span class=\"p\">)"},
 | 
				
			||||||
 | 
							{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"},
 | 
				
			||||||
 | 
							{Type: dmp.DiffEqual, Text: "</span><span class=\"p\">)</span>"},
 | 
				
			||||||
 | 
						}, DiffLineDel))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestParsePatch(t *testing.T) {
 | 
					func TestParsePatch(t *testing.T) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user