summaryrefslogtreecommitdiffstats
path: root/services/gitdiff/gitdiff.go
diff options
context:
space:
mode:
authorzeripath <art27@cantab.net>2020-10-31 17:24:32 +0000
committerGitHub <noreply@github.com>2020-10-31 13:24:32 -0400
commit6b7c199f5fa4b4b225a08bd72b98ad4ef170c7c6 (patch)
tree775d6be8d397dadbbe72af407f085998117ab536 /services/gitdiff/gitdiff.go
parenta420beda2a12f25f5f5503c20d0d6febee892132 (diff)
downloadgitea-6b7c199f5fa4b4b225a08bd72b98ad4ef170c7c6.tar.gz
gitea-6b7c199f5fa4b4b225a08bd72b98ad4ef170c7c6.zip
When creating line diffs do not split within an html entity (#13357)
* When creating line diffs do not split within an html entity Fix #13342 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add test case Signed-off-by: Andrew Thornton <art27@cantab.net> * improve test Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Diffstat (limited to 'services/gitdiff/gitdiff.go')
-rw-r--r--services/gitdiff/gitdiff.go84
1 files changed, 84 insertions, 0 deletions
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index 7acf8324c3..50e2f8cf1e 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -290,6 +290,9 @@ func init() {
diffMatchPatch.DiffEditCost = 100
}
+var unterminatedEntityRE = regexp.MustCompile(`&[^ ;]*$`)
+var unstartedEntiyRE = regexp.MustCompile(`^[^ ;]*;`)
+
// GetComputedInlineDiffFor computes inline diff for the given line.
func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) template.HTML {
if setting.Git.DisableDiffHighlight {
@@ -329,9 +332,90 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) tem
diffRecord := diffMatchPatch.DiffMain(highlight.Code(diffSection.FileName, diff1[1:]), highlight.Code(diffSection.FileName, diff2[1:]), true)
diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord)
+
+ // Now we need to clean up the split entities
+ diffRecord = unsplitEntities(diffRecord)
+ diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord)
+
return diffToHTML(diffSection.FileName, diffRecord, diffLine.Type)
}
+// unsplitEntities looks for broken up html entities. It relies on records being presimplified and the data being passed in being valid html
+func unsplitEntities(records []diffmatchpatch.Diff) []diffmatchpatch.Diff {
+ // Unsplitting entities is simple...
+ //
+ // Iterate through all be the last records because if we're the last record then there's nothing we can do
+ for i := 0; i+1 < len(records); i++ {
+ record := &records[i]
+
+ // Look for an unterminated entity at the end of the line
+ unterminated := unterminatedEntityRE.FindString(record.Text)
+ if len(unterminated) == 0 {
+ continue
+ }
+
+ switch record.Type {
+ case diffmatchpatch.DiffEqual:
+ // If we're an diff equal we want to give this unterminated entity to our next delete and insert
+ record.Text = record.Text[0 : len(record.Text)-len(unterminated)]
+ records[i+1].Text = unterminated + records[i+1].Text
+
+ nextType := records[i+1].Type
+
+ if nextType == diffmatchpatch.DiffEqual {
+ continue
+ }
+
+ // if the next in line is a delete then we will want the thing after that to be an insert and so on.
+ oneAfterType := diffmatchpatch.DiffInsert
+ if nextType == diffmatchpatch.DiffInsert {
+ oneAfterType = diffmatchpatch.DiffDelete
+ }
+
+ if i+2 < len(records) && records[i+2].Type == oneAfterType {
+ records[i+2].Text = unterminated + records[i+2].Text
+ } else {
+ records = append(records[:i+2], append([]diffmatchpatch.Diff{
+ {
+ Type: oneAfterType,
+ Text: unterminated,
+ }}, records[i+2:]...)...)
+ }
+ case diffmatchpatch.DiffDelete:
+ fallthrough
+ case diffmatchpatch.DiffInsert:
+ // if we're an insert or delete we want to claim the terminal bit of the entity from the next equal in line
+ targetType := diffmatchpatch.DiffInsert
+ if record.Type == diffmatchpatch.DiffInsert {
+ targetType = diffmatchpatch.DiffDelete
+ }
+ next := &records[i+1]
+ if next.Type == diffmatchpatch.DiffEqual {
+ // if the next is an equal we need to snaffle the entity end off the start and add an delete/insert
+ if terminal := unstartedEntiyRE.FindString(next.Text); len(terminal) > 0 {
+ record.Text += terminal
+ next.Text = next.Text[len(terminal):]
+ records = append(records[:i+2], append([]diffmatchpatch.Diff{
+ {
+ Type: targetType,
+ Text: unterminated,
+ }}, records[i+2:]...)...)
+ }
+ } else if next.Type == targetType {
+ // if the next is an insert we need to snaffle the entity end off the one after that and add it to both.
+ if i+2 < len(records) && records[i+2].Type == diffmatchpatch.DiffEqual {
+ if terminal := unstartedEntiyRE.FindString(records[i+2].Text); len(terminal) > 0 {
+ record.Text += terminal
+ next.Text += terminal
+ records[i+2].Text = records[i+2].Text[len(terminal):]
+ }
+ }
+ }
+ }
+ }
+ return records
+}
+
// DiffFile represents a file diff.
type DiffFile struct {
Name string