History

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

mrsdizzie committed 6 years ago

Commit f6ee7ce1 parent 83106c12 files · +44 −48Browse files

Showing the changes to services/gitdiff/gitdiff.go (1 of 2 files). Show all

Unified · Split

services/gitdiff/gitdiff.go+42−46
@@ -181,64 +181,60 @@ var ( · 180 unmodified lines
181 removedCodePrefix = []byte(`<span class="removed-code">`)181 removedCodePrefix = []byte(`<span class="removed-code">`)
182 codeTagSuffix = []byte(`</span>`)182 codeTagSuffix = []byte(`</span>`)
183)183)
184var addSpanRegex = regexp.MustCompile(`<span\s*[a-z="]*$`)184var trailingSpanRegex = regexp.MustCompile(`<span\s*[[:alpha:]="]*?[>]?$`)
185
186// shouldWriteInline represents combinations where we manually write inline changes
187func shouldWriteInline(diff diffmatchpatch.Diff, lineType DiffLineType) bool {
188 if true &&
189 diff.Type == diffmatchpatch.DiffEqual ||
190 diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd ||
191 diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel {
192 return true
193 }
194 return false
195}
185 196
186func diffToHTML(fileName string, diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML {197func diffToHTML(fileName string, diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML {
187 buf := bytes.NewBuffer(nil)198 buf := bytes.NewBuffer(nil)
188 var addSpan string199 match := ""
189 for i := range diffs {200
190 switch {201 for _, diff := range diffs {
191 case diffs[i].Type == diffmatchpatch.DiffEqual:202 if shouldWriteInline(diff, lineType) {
192 // Looking for the case where our 3rd party diff library previously detected a string difference203 if len(match) > 0 {
193 // in the middle of a span class because we highlight them first. This happens when added/deleted code204 diff.Text = match + diff.Text
194 // also changes the chroma class name, either partially or fully. If found, just move the openining span code forward into the next section205 match = ""
195 // see TestDiffToHTML for examples
196 if len(addSpan) > 0 {
197 diffs[i].Text = addSpan + diffs[i].Text
198 addSpan = ""
199 }206 }
200 m := addSpanRegex.FindStringSubmatchIndex(diffs[i].Text)207 // Chroma HTML syntax highlighting is done before diffing individual lines in order to maintain consistency.
208 // Since inline changes might split in the middle of a chroma span tag, make we manually put it back together
209 // before writing so we don't try insert added/removed code spans in the middle of an existing chroma span
210 // and create broken HTML.
211 m := trailingSpanRegex.FindStringSubmatchIndex(diff.Text)
201 if m != nil {212 if m != nil {
202 addSpan = diffs[i].Text[m[0]:m[1]]213 match = diff.Text[m[0]:m[1]]
203 buf.WriteString(strings.TrimSuffix(diffs[i].Text, addSpan))214 diff.Text = strings.TrimSuffix(diff.Text, match)
204 } else {
205 addSpan = ""
206 buf.WriteString(getLineContent(diffs[i].Text))
207 }
208 case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd:
209 if len(addSpan) > 0 {
210 diffs[i].Text = addSpan + diffs[i].Text
211 addSpan = ""
212 }215 }
213 // Print existing closing span first before opening added-code span so it doesn't unintentionally close it216 // Print an existing closing span first before opening added/remove-code span so it doesn't unintentionally close it
214 if strings.HasPrefix(diffs[i].Text, "</span>") {217 if strings.HasPrefix(diff.Text, "</span>") {
215 buf.WriteString("</span>")218 buf.WriteString("</span>")
216 diffs[i].Text = strings.TrimPrefix(diffs[i].Text, "</span>")219 diff.Text = strings.TrimPrefix(diff.Text, "</span>")
217 }220 }
218 m := addSpanRegex.FindStringSubmatchIndex(diffs[i].Text)221 // If we weren't able to fix it then this should avoid broken HTML by not inserting more spans below
219 if m != nil {222 // The previous/next diff section will contain the rest of the tag that is missing here
220 addSpan = diffs[i].Text[m[0]:m[1]]223 if strings.Count(diff.Text, "<") != strings.Count(diff.Text, ">") {
221 diffs[i].Text = strings.TrimSuffix(diffs[i].Text, addSpan)224 buf.WriteString(diff.Text)
225 continue
222 }226 }
227 }
228 switch {
229 case diff.Type == diffmatchpatch.DiffEqual:
230 buf.WriteString(diff.Text)
231 case diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd:
223 buf.Write(addedCodePrefix)232 buf.Write(addedCodePrefix)
224 buf.WriteString(diffs[i].Text)233 buf.WriteString(diff.Text)
225 buf.Write(codeTagSuffix)234 buf.Write(codeTagSuffix)
226 case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel:235 case diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel:
227 if len(addSpan) > 0 {
228 diffs[i].Text = addSpan + diffs[i].Text
229 addSpan = ""
230 }
231 if strings.HasPrefix(diffs[i].Text, "</span>") {
232 buf.WriteString("</span>")
233 diffs[i].Text = strings.TrimPrefix(diffs[i].Text, "</span>")
234 }
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)
239 }
240 buf.Write(removedCodePrefix)236 buf.Write(removedCodePrefix)
241 buf.WriteString(diffs[i].Text)237 buf.WriteString(diff.Text)
242 buf.Write(codeTagSuffix)238 buf.Write(codeTagSuffix)
243 }239 }
244 }240 }

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t