mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-01 05:08:58 +00:00
perf(gitdiff): optimize inline diff highlighting using cache (#38706)
This pull request optimizes code diff highlighting in the PR Files Changed view, which is a major CPU bottleneck and hot path in production. Previously, Gitea executed the expensive `DiffMatchPatch` algorithm twice for every matching deleted/added line pair (once for the deleted line, and once for the added line). We now run the diff algorithm once and cache the resulting `DiffInline` on the `DiffLine` struct itself, allowing the second line to render via a 0-cost cache lookup. --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
@@ -150,7 +150,7 @@ func (hcd *highlightCodeDiff) diffEqualPartIsSpaceOnly(s string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (hcd *highlightCodeDiff) diffLineWithHighlight(lineType DiffLineType, codeA, codeB template.HTML) template.HTML {
|
||||
func (hcd *highlightCodeDiff) diffLineWithHighlight(codeA, codeB template.HTML) (del, add template.HTML) {
|
||||
hcd.collectUsedRunes(codeA)
|
||||
hcd.collectUsedRunes(codeB)
|
||||
|
||||
@@ -161,8 +161,6 @@ func (hcd *highlightCodeDiff) diffLineWithHighlight(lineType DiffLineType, codeA
|
||||
diffs := dmp.DiffMain(convertedCodeA, convertedCodeB, true)
|
||||
diffs = dmp.DiffCleanupSemantic(diffs)
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
if hcd.diffCodeClose == 0 {
|
||||
// tests can pre-set the placeholders
|
||||
hcd.diffCodeAddedOpen = hcd.registerTokenAsPlaceholder(`<span class="added-code">`)
|
||||
@@ -183,32 +181,37 @@ func (hcd *highlightCodeDiff) diffLineWithHighlight(lineType DiffLineType, codeA
|
||||
// only add "added"/"removed" tags when needed:
|
||||
// * non-space contents appear in the DiffEqual parts (not a full-line add/del)
|
||||
// * placeholder map still works (not exhausted, can get the closing tag placeholder)
|
||||
bufDel := bytes.NewBuffer(nil)
|
||||
bufAdd := bytes.NewBuffer(nil)
|
||||
addDiffTags := !equalPartSpaceOnly && hcd.diffCodeClose != 0
|
||||
if addDiffTags {
|
||||
for _, diff := range diffs {
|
||||
switch {
|
||||
case diff.Type == diffmatchpatch.DiffEqual:
|
||||
buf.WriteString(diff.Text)
|
||||
case diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd:
|
||||
buf.WriteRune(hcd.diffCodeAddedOpen)
|
||||
buf.WriteString(diff.Text)
|
||||
buf.WriteRune(hcd.diffCodeClose)
|
||||
case diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel:
|
||||
buf.WriteRune(hcd.diffCodeRemovedOpen)
|
||||
buf.WriteString(diff.Text)
|
||||
buf.WriteRune(hcd.diffCodeClose)
|
||||
switch diff.Type {
|
||||
case diffmatchpatch.DiffEqual:
|
||||
bufDel.WriteString(diff.Text)
|
||||
bufAdd.WriteString(diff.Text)
|
||||
case diffmatchpatch.DiffInsert:
|
||||
bufAdd.WriteRune(hcd.diffCodeAddedOpen)
|
||||
bufAdd.WriteString(diff.Text)
|
||||
bufAdd.WriteRune(hcd.diffCodeClose)
|
||||
case diffmatchpatch.DiffDelete:
|
||||
bufDel.WriteRune(hcd.diffCodeRemovedOpen)
|
||||
bufDel.WriteString(diff.Text)
|
||||
bufDel.WriteRune(hcd.diffCodeClose)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// the caller will still add added/removed backgrounds for the whole line
|
||||
for _, diff := range diffs {
|
||||
take := diff.Type == diffmatchpatch.DiffEqual || (diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd) || (diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel)
|
||||
if take {
|
||||
buf.WriteString(diff.Text)
|
||||
if diff.Type == diffmatchpatch.DiffEqual || diff.Type == diffmatchpatch.DiffDelete {
|
||||
bufDel.WriteString(diff.Text)
|
||||
}
|
||||
if diff.Type == diffmatchpatch.DiffEqual || diff.Type == diffmatchpatch.DiffInsert {
|
||||
bufAdd.WriteString(diff.Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
return hcd.recoverOneDiff(buf.String())
|
||||
return hcd.recoverOneDiff(util.UnsafeBytesToString(bufDel.Bytes())), hcd.recoverOneDiff(util.UnsafeBytesToString(bufAdd.Bytes()))
|
||||
}
|
||||
|
||||
func (hcd *highlightCodeDiff) registerTokenAsPlaceholder(token string) rune {
|
||||
|
||||
Reference in New Issue
Block a user