Browse Source

Improve diff highlight (#3390)

- Try to reduce memory allocations
- Add possibility to disable diff highlight (can improve performance for large diffs)
- Tweaking with cost for prettier (cleaner) diffs
- Do not calculate diff when the number of removed lines in a block is not equal to the number of added lines (this usually resulted in ugly diffs)
pull/3403/head
Andrey Nering 8 years ago committed by 无闻
parent
commit
2772791fda
  1. 8
      conf/app.ini
  2. 82
      models/git_diff.go
  3. 35
      models/git_diff_test.go
  4. 1
      modules/setting/setting.go

8
conf/app.ini

@ -334,11 +334,13 @@ RUN_AT_START = true
SCHEDULE = @every 24h SCHEDULE = @every 24h
[git] [git]
; Max number of lines allowed of a single file in diff view. ; Disables highlight of added and removed changes
DISABLE_DIFF_HIGHLIGHT = false
; Max number of lines allowed of a single file in diff view
MAX_GIT_DIFF_LINES = 1000 MAX_GIT_DIFF_LINES = 1000
; Max number of characters of a line allowed in diff view. ; Max number of characters of a line allowed in diff view
MAX_GIT_DIFF_LINE_CHARACTERS = 500 MAX_GIT_DIFF_LINE_CHARACTERS = 500
; Max number of files shown in diff view. ; Max number of files shown in diff view
MAX_GIT_DIFF_FILES = 100 MAX_GIT_DIFF_FILES = 100
; Arguments for command 'git gc', e.g. "--aggressive --auto" ; Arguments for command 'git gc', e.g. "--aggressive --auto"
; see more on http://git-scm.com/docs/git-gc/1.7.5 ; see more on http://git-scm.com/docs/git-gc/1.7.5

82
models/git_diff.go

@ -26,6 +26,7 @@ import (
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process" "github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/template/highlight" "github.com/gogits/gogs/modules/template/highlight"
) )
@ -70,17 +71,18 @@ var (
) )
func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML { func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML {
var buf bytes.Buffer buf := bytes.NewBuffer(nil)
for i := range diffs { for i := range diffs {
if diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DIFF_LINE_ADD { switch {
case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DIFF_LINE_ADD:
buf.Write(addedCodePrefix) buf.Write(addedCodePrefix)
buf.WriteString(html.EscapeString(diffs[i].Text)) buf.WriteString(html.EscapeString(diffs[i].Text))
buf.Write(codeTagSuffix) buf.Write(codeTagSuffix)
} else if diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DIFF_LINE_DEL { case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DIFF_LINE_DEL:
buf.Write(removedCodePrefix) buf.Write(removedCodePrefix)
buf.WriteString(html.EscapeString(diffs[i].Text)) buf.WriteString(html.EscapeString(diffs[i].Text))
buf.Write(codeTagSuffix) buf.Write(codeTagSuffix)
} else if diffs[i].Type == diffmatchpatch.DiffEqual { case diffs[i].Type == diffmatchpatch.DiffEqual:
buf.WriteString(html.EscapeString(diffs[i].Text)) buf.WriteString(html.EscapeString(diffs[i].Text))
} }
} }
@ -90,62 +92,86 @@ func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTM
// get an specific line by type (add or del) and file line number // get an specific line by type (add or del) and file line number
func (diffSection *DiffSection) GetLine(lineType DiffLineType, idx int) *DiffLine { func (diffSection *DiffSection) GetLine(lineType DiffLineType, idx int) *DiffLine {
difference := 0 var (
difference = 0
addCount = 0
delCount = 0
matchDiffLine *DiffLine
)
LOOP:
for _, diffLine := range diffSection.Lines { for _, diffLine := range diffSection.Lines {
if diffLine.Type == DIFF_LINE_PLAIN { switch diffLine.Type {
// get the difference of line numbers between ADD and DEL versions case DIFF_LINE_ADD:
addCount++
case DIFF_LINE_DEL:
delCount++
default:
if matchDiffLine != nil {
break LOOP
}
difference = diffLine.RightIdx - diffLine.LeftIdx difference = diffLine.RightIdx - diffLine.LeftIdx
continue addCount = 0
delCount = 0
} }
if lineType == DIFF_LINE_DEL { switch lineType {
case DIFF_LINE_DEL:
if diffLine.RightIdx == 0 && diffLine.LeftIdx == idx-difference { if diffLine.RightIdx == 0 && diffLine.LeftIdx == idx-difference {
return diffLine matchDiffLine = diffLine
} }
} else if lineType == DIFF_LINE_ADD { case DIFF_LINE_ADD:
if diffLine.LeftIdx == 0 && diffLine.RightIdx == idx+difference { if diffLine.LeftIdx == 0 && diffLine.RightIdx == idx+difference {
return diffLine matchDiffLine = diffLine
} }
} }
} }
if addCount == delCount {
return matchDiffLine
}
return nil return nil
} }
var diffMatchPatch = diffmatchpatch.New()
func init() {
diffMatchPatch.DiffEditCost = 100
}
// computes inline diff for the given line // computes inline diff for the given line
func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) template.HTML { func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) template.HTML {
var compareDiffLine *DiffLine if setting.Git.DisableDiffHighlight {
var diff1, diff2 string
getDefaultReturn := func() template.HTML {
return template.HTML(html.EscapeString(diffLine.Content[1:])) return template.HTML(html.EscapeString(diffLine.Content[1:]))
} }
var (
// just compute diff for adds and removes compareDiffLine *DiffLine
if diffLine.Type != DIFF_LINE_ADD && diffLine.Type != DIFF_LINE_DEL { diff1 string
return getDefaultReturn() diff2 string
} )
// try to find equivalent diff line. ignore, otherwise // try to find equivalent diff line. ignore, otherwise
if diffLine.Type == DIFF_LINE_ADD { switch diffLine.Type {
case DIFF_LINE_ADD:
compareDiffLine = diffSection.GetLine(DIFF_LINE_DEL, diffLine.RightIdx) compareDiffLine = diffSection.GetLine(DIFF_LINE_DEL, diffLine.RightIdx)
if compareDiffLine == nil { if compareDiffLine == nil {
return getDefaultReturn() return template.HTML(html.EscapeString(diffLine.Content[1:]))
} }
diff1 = compareDiffLine.Content diff1 = compareDiffLine.Content
diff2 = diffLine.Content diff2 = diffLine.Content
} else { case DIFF_LINE_DEL:
compareDiffLine = diffSection.GetLine(DIFF_LINE_ADD, diffLine.LeftIdx) compareDiffLine = diffSection.GetLine(DIFF_LINE_ADD, diffLine.LeftIdx)
if compareDiffLine == nil { if compareDiffLine == nil {
return getDefaultReturn() return template.HTML(html.EscapeString(diffLine.Content[1:]))
} }
diff1 = diffLine.Content diff1 = diffLine.Content
diff2 = compareDiffLine.Content diff2 = compareDiffLine.Content
default:
return template.HTML(html.EscapeString(diffLine.Content[1:]))
} }
dmp := diffmatchpatch.New() diffRecord := diffMatchPatch.DiffMain(diff1[1:], diff2[1:], true)
diffRecord := dmp.DiffMain(diff1[1:], diff2[1:], true) diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord)
diffRecord = dmp.DiffCleanupSemantic(diffRecord)
return diffToHTML(diffRecord, diffLine.Type) return diffToHTML(diffRecord, diffLine.Type)
} }

35
models/git_diff_test.go

@ -33,38 +33,3 @@ func TestDiffToHTML(t *testing.T) {
dmp.Diff{dmp.DiffEqual, " biz"}, dmp.Diff{dmp.DiffEqual, " biz"},
}, DIFF_LINE_DEL)) }, DIFF_LINE_DEL))
} }
// test if GetLine is return the correct lines
func TestGetLine(t *testing.T) {
ds := DiffSection{Lines: []*DiffLine{
&DiffLine{LeftIdx: 28, RightIdx: 28, Type: DIFF_LINE_PLAIN},
&DiffLine{LeftIdx: 29, RightIdx: 29, Type: DIFF_LINE_PLAIN},
&DiffLine{LeftIdx: 30, RightIdx: 30, Type: DIFF_LINE_PLAIN},
&DiffLine{LeftIdx: 31, RightIdx: 0, Type: DIFF_LINE_DEL},
&DiffLine{LeftIdx: 0, RightIdx: 31, Type: DIFF_LINE_ADD},
&DiffLine{LeftIdx: 0, RightIdx: 32, Type: DIFF_LINE_ADD},
&DiffLine{LeftIdx: 32, RightIdx: 33, Type: DIFF_LINE_PLAIN},
&DiffLine{LeftIdx: 33, RightIdx: 0, Type: DIFF_LINE_DEL},
&DiffLine{LeftIdx: 34, RightIdx: 0, Type: DIFF_LINE_DEL},
&DiffLine{LeftIdx: 35, RightIdx: 0, Type: DIFF_LINE_DEL},
&DiffLine{LeftIdx: 36, RightIdx: 0, Type: DIFF_LINE_DEL},
&DiffLine{LeftIdx: 0, RightIdx: 34, Type: DIFF_LINE_ADD},
&DiffLine{LeftIdx: 0, RightIdx: 35, Type: DIFF_LINE_ADD},
&DiffLine{LeftIdx: 0, RightIdx: 36, Type: DIFF_LINE_ADD},
&DiffLine{LeftIdx: 0, RightIdx: 37, Type: DIFF_LINE_ADD},
&DiffLine{LeftIdx: 37, RightIdx: 38, Type: DIFF_LINE_PLAIN},
&DiffLine{LeftIdx: 38, RightIdx: 39, Type: DIFF_LINE_PLAIN},
}}
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 31), ds.Lines[4])
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 31), ds.Lines[3])
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 33), ds.Lines[11])
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 34), ds.Lines[12])
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 35), ds.Lines[13])
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 36), ds.Lines[14])
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 34), ds.Lines[7])
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 35), ds.Lines[8])
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 36), ds.Lines[9])
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 37), ds.Lines[10])
}

1
modules/setting/setting.go

@ -191,6 +191,7 @@ var (
// Git settings // Git settings
Git struct { Git struct {
DisableDiffHighlight bool
MaxGitDiffLines int MaxGitDiffLines int
MaxGitDiffLineCharacters int MaxGitDiffLineCharacters int
MaxGitDiffFiles int MaxGitDiffFiles int

Loading…
Cancel
Save