mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Add better error checking for inline html diff code (#13239)
* Add better error checking for inline html diff code A better fix for #13191 which cleans up this code a bit and adds basic checking which should avoid writing broken HTML in future situations. * Update gitdiff_test.go * better regex Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
		@@ -181,64 +181,60 @@ var (
 | 
			
		||||
	removedCodePrefix = []byte(`<span class="removed-code">`)
 | 
			
		||||
	codeTagSuffix     = []byte(`</span>`)
 | 
			
		||||
)
 | 
			
		||||
var addSpanRegex = regexp.MustCompile(`<span\s*[a-z="]*$`)
 | 
			
		||||
var trailingSpanRegex = regexp.MustCompile(`<span\s*[[:alpha:]="]*?[>]?$`)
 | 
			
		||||
 | 
			
		||||
// shouldWriteInline represents combinations where we manually write inline changes
 | 
			
		||||
func shouldWriteInline(diff diffmatchpatch.Diff, lineType DiffLineType) bool {
 | 
			
		||||
	if true &&
 | 
			
		||||
		diff.Type == diffmatchpatch.DiffEqual ||
 | 
			
		||||
		diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd ||
 | 
			
		||||
		diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func diffToHTML(fileName string, diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML {
 | 
			
		||||
	buf := bytes.NewBuffer(nil)
 | 
			
		||||
	var addSpan string
 | 
			
		||||
	for i := range diffs {
 | 
			
		||||
	match := ""
 | 
			
		||||
 | 
			
		||||
	for _, diff := range diffs {
 | 
			
		||||
		if shouldWriteInline(diff, lineType) {
 | 
			
		||||
			if len(match) > 0 {
 | 
			
		||||
				diff.Text = match + diff.Text
 | 
			
		||||
				match = ""
 | 
			
		||||
			}
 | 
			
		||||
			// Chroma HTML syntax highlighting is done before diffing individual lines in order to maintain consistency.
 | 
			
		||||
			// Since inline changes might split in the middle of a chroma span tag, make we manually put it back together
 | 
			
		||||
			// before writing so we don't try insert added/removed code spans in the middle of an existing chroma span
 | 
			
		||||
			// and create broken HTML.
 | 
			
		||||
			m := trailingSpanRegex.FindStringSubmatchIndex(diff.Text)
 | 
			
		||||
			if m != nil {
 | 
			
		||||
				match = diff.Text[m[0]:m[1]]
 | 
			
		||||
				diff.Text = strings.TrimSuffix(diff.Text, match)
 | 
			
		||||
			}
 | 
			
		||||
			// Print an existing closing span first before opening added/remove-code span so it doesn't unintentionally close it
 | 
			
		||||
			if strings.HasPrefix(diff.Text, "</span>") {
 | 
			
		||||
				buf.WriteString("</span>")
 | 
			
		||||
				diff.Text = strings.TrimPrefix(diff.Text, "</span>")
 | 
			
		||||
			}
 | 
			
		||||
			// If we weren't able to fix it then this should avoid broken HTML by not inserting more spans below
 | 
			
		||||
			// The previous/next diff section will contain the rest of the tag that is missing here
 | 
			
		||||
			if strings.Count(diff.Text, "<") != strings.Count(diff.Text, ">") {
 | 
			
		||||
				buf.WriteString(diff.Text)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		switch {
 | 
			
		||||
		case diffs[i].Type == diffmatchpatch.DiffEqual:
 | 
			
		||||
			// 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
 | 
			
		||||
			// also changes the chroma class name, either partially or fully. If found, just move the openining span code forward into the next section
 | 
			
		||||
			// see TestDiffToHTML for examples
 | 
			
		||||
			if len(addSpan) > 0 {
 | 
			
		||||
				diffs[i].Text = addSpan + diffs[i].Text
 | 
			
		||||
				addSpan = ""
 | 
			
		||||
			}
 | 
			
		||||
			m := addSpanRegex.FindStringSubmatchIndex(diffs[i].Text)
 | 
			
		||||
			if m != nil {
 | 
			
		||||
				addSpan = diffs[i].Text[m[0]:m[1]]
 | 
			
		||||
				buf.WriteString(strings.TrimSuffix(diffs[i].Text, addSpan))
 | 
			
		||||
			} else {
 | 
			
		||||
				addSpan = ""
 | 
			
		||||
				buf.WriteString(getLineContent(diffs[i].Text))
 | 
			
		||||
			}
 | 
			
		||||
		case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd:
 | 
			
		||||
			if len(addSpan) > 0 {
 | 
			
		||||
				diffs[i].Text = addSpan + diffs[i].Text
 | 
			
		||||
				addSpan = ""
 | 
			
		||||
			}
 | 
			
		||||
			// Print existing closing span first before opening added-code span so it doesn't unintentionally close it
 | 
			
		||||
			if strings.HasPrefix(diffs[i].Text, "</span>") {
 | 
			
		||||
				buf.WriteString("</span>")
 | 
			
		||||
				diffs[i].Text = strings.TrimPrefix(diffs[i].Text, "</span>")
 | 
			
		||||
			}
 | 
			
		||||
			m := addSpanRegex.FindStringSubmatchIndex(diffs[i].Text)
 | 
			
		||||
			if m != nil {
 | 
			
		||||
				addSpan = diffs[i].Text[m[0]:m[1]]
 | 
			
		||||
				diffs[i].Text = strings.TrimSuffix(diffs[i].Text, addSpan)
 | 
			
		||||
			}
 | 
			
		||||
		case diff.Type == diffmatchpatch.DiffEqual:
 | 
			
		||||
			buf.WriteString(diff.Text)
 | 
			
		||||
		case diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd:
 | 
			
		||||
			buf.Write(addedCodePrefix)
 | 
			
		||||
			buf.WriteString(diffs[i].Text)
 | 
			
		||||
			buf.WriteString(diff.Text)
 | 
			
		||||
			buf.Write(codeTagSuffix)
 | 
			
		||||
		case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel:
 | 
			
		||||
			if len(addSpan) > 0 {
 | 
			
		||||
				diffs[i].Text = addSpan + diffs[i].Text
 | 
			
		||||
				addSpan = ""
 | 
			
		||||
			}
 | 
			
		||||
			if strings.HasPrefix(diffs[i].Text, "</span>") {
 | 
			
		||||
				buf.WriteString("</span>")
 | 
			
		||||
				diffs[i].Text = strings.TrimPrefix(diffs[i].Text, "</span>")
 | 
			
		||||
			}
 | 
			
		||||
			m := addSpanRegex.FindStringSubmatchIndex(diffs[i].Text)
 | 
			
		||||
			if m != nil {
 | 
			
		||||
				addSpan = diffs[i].Text[m[0]:m[1]]
 | 
			
		||||
				diffs[i].Text = strings.TrimSuffix(diffs[i].Text, addSpan)
 | 
			
		||||
			}
 | 
			
		||||
		case diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel:
 | 
			
		||||
			buf.Write(removedCodePrefix)
 | 
			
		||||
			buf.WriteString(diffs[i].Text)
 | 
			
		||||
			buf.WriteString(diff.Text)
 | 
			
		||||
			buf.Write(codeTagSuffix)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user