mirror of
https://github.com/go-gitea/gitea.git
synced 2026-07-22 14:25:18 +02:00
refactor: clean up fragile diff render templates, use backend typed structs (#38517)
Blob.Size requires a context after the repository context removal
(5b078f72aa).
Actually the template code should just render, it should not depend on
the fragile dynamic calls to backend functions.
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -38,6 +38,7 @@ import (
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/svg"
|
||||
"gitea.dev/modules/translation"
|
||||
"gitea.dev/modules/typesniffer"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
@@ -465,6 +466,14 @@ type DiffFile struct {
|
||||
highlightRender diffVarMutable[chroma.Lexer] // cache render (atm: lexer) for current file, only detect once for line-by-line mode
|
||||
highlightedLeftLines diffVarMutable[map[int]template.HTML]
|
||||
highlightedRightLines diffVarMutable[map[int]template.HTML]
|
||||
|
||||
// image diff and csv diff need some of the following fields
|
||||
LeftBlob, RightBlob *git.Blob
|
||||
LeftBlobSize, RightBlobSize int64
|
||||
LeftBlobMimeType, RightBlobMimeType string
|
||||
|
||||
IsBlobTypeImage bool
|
||||
IsBlobTypeCsv bool
|
||||
}
|
||||
|
||||
// GetType returns type of diff file.
|
||||
@@ -472,31 +481,64 @@ func (diffFile *DiffFile) GetType() int {
|
||||
return int(diffFile.Type)
|
||||
}
|
||||
|
||||
type DiffLimitedContent struct {
|
||||
LeftContent, RightContent *limitByteWriter
|
||||
type DiffRenderDetail struct {
|
||||
needTailSection bool
|
||||
leftLineCount, rightLineCount int
|
||||
leftContent, rightContent *limitByteWriter
|
||||
}
|
||||
|
||||
// GetTailSectionAndLimitedContent creates a fake DiffLineSection if the last section is not the end of the file
|
||||
func (diffFile *DiffFile) GetTailSectionAndLimitedContent(ctx context.Context, gitRepo *git.Repository, leftCommit, rightCommit *git.Commit) (_ *DiffSection, diffLimitedContent DiffLimitedContent) {
|
||||
var leftLineCount, rightLineCount int
|
||||
diffLimitedContent = DiffLimitedContent{}
|
||||
if diffFile.IsBin || diffFile.IsLFSFile {
|
||||
return nil, diffLimitedContent
|
||||
func (diffFile *DiffFile) prepareDiffRenderDetail(ctx context.Context, gitRepo *git.Repository, leftCommit, rightCommit *git.Commit) (ret DiffRenderDetail) {
|
||||
if diffFile.IsLFSFile {
|
||||
return ret
|
||||
}
|
||||
|
||||
// pre-fetch the blob info & content
|
||||
// * for "bin" type: need the pre-fetched buffer to detect content type (e.g.: help to render image diff)
|
||||
// * for "text" type: need to read up to "highlight limit size" to do full-file-highlighting
|
||||
contentLimit := util.Iif(diffFile.IsBin, typesniffer.SniffContentSize, MaxFullFileHighlightSizeLimit)
|
||||
var leftLineCount, rightLineCount int
|
||||
var leftBlobType, rightBlobType typesniffer.SniffedType
|
||||
if (diffFile.Type == DiffFileDel || diffFile.Type == DiffFileChange) && leftCommit != nil {
|
||||
leftLineCount, diffLimitedContent.LeftContent = getCommitFileLineCountAndLimitedContent(ctx, gitRepo, leftCommit, diffFile.OldName)
|
||||
c := getCommitFileBlobAndLimitedContent(ctx, gitRepo, leftCommit, diffFile.OldName, contentLimit)
|
||||
diffFile.LeftBlob, diffFile.LeftBlobSize, leftLineCount, ret.leftContent = c.gitBlob, c.blobSize, c.lineCount, c.limitedContent
|
||||
leftBlobType = typesniffer.DetectContentType(ret.leftContent.buf.Bytes())
|
||||
}
|
||||
if (diffFile.Type == DiffFileAdd || diffFile.Type == DiffFileChange) && rightCommit != nil {
|
||||
rightLineCount, diffLimitedContent.RightContent = getCommitFileLineCountAndLimitedContent(ctx, gitRepo, rightCommit, diffFile.OldName)
|
||||
c := getCommitFileBlobAndLimitedContent(ctx, gitRepo, rightCommit, diffFile.OldName, contentLimit)
|
||||
diffFile.RightBlob, diffFile.RightBlobSize, rightLineCount, ret.rightContent = c.gitBlob, c.blobSize, c.lineCount, c.limitedContent
|
||||
rightBlobType = typesniffer.DetectContentType(ret.rightContent.buf.Bytes())
|
||||
}
|
||||
if len(diffFile.Sections) == 0 || diffFile.Type != DiffFileChange {
|
||||
return nil, diffLimitedContent
|
||||
|
||||
isFileTypeImage := func(st typesniffer.SniffedType) bool {
|
||||
return st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage())
|
||||
}
|
||||
isFileTypeCsv := func(name string) bool {
|
||||
extension := strings.ToLower(path.Ext(name))
|
||||
return extension == ".csv" || extension == ".tsv"
|
||||
}
|
||||
diffFile.LeftBlobMimeType, diffFile.RightBlobMimeType = leftBlobType.GetMimeType(), rightBlobType.GetMimeType()
|
||||
diffFile.IsBlobTypeImage = isFileTypeImage(leftBlobType) || isFileTypeImage(rightBlobType)
|
||||
diffFile.IsBlobTypeCsv = isFileTypeCsv(diffFile.Name)
|
||||
|
||||
if diffFile.IsBin || len(diffFile.Sections) == 0 || diffFile.Type != DiffFileChange {
|
||||
return ret
|
||||
}
|
||||
|
||||
// check whether the text file diff needs a tail section
|
||||
lastSection := diffFile.Sections[len(diffFile.Sections)-1]
|
||||
lastLine := lastSection.Lines[len(lastSection.Lines)-1]
|
||||
if leftLineCount <= lastLine.LeftIdx || rightLineCount <= lastLine.RightIdx {
|
||||
return nil, diffLimitedContent
|
||||
return ret
|
||||
}
|
||||
ret.needTailSection = true
|
||||
return ret
|
||||
}
|
||||
|
||||
func (diffFile *DiffFile) addTailSection(detail DiffRenderDetail) {
|
||||
// if the last diff section still doesn't reach to the end of file, we need to add a "tail section" to
|
||||
// make users can expand the remaining unchanged lines to see the full file content.
|
||||
lastSection := diffFile.Sections[len(diffFile.Sections)-1]
|
||||
lastLine := lastSection.Lines[len(lastSection.Lines)-1]
|
||||
tailDiffLine := &DiffLine{
|
||||
Type: DiffLineSection,
|
||||
Content: " ",
|
||||
@@ -505,12 +547,12 @@ func (diffFile *DiffFile) GetTailSectionAndLimitedContent(ctx context.Context, g
|
||||
Path: diffFile.Name,
|
||||
LastLeftIdx: lastLine.LeftIdx,
|
||||
LastRightIdx: lastLine.RightIdx,
|
||||
LeftIdx: leftLineCount,
|
||||
RightIdx: rightLineCount,
|
||||
LeftIdx: detail.leftLineCount,
|
||||
RightIdx: detail.rightLineCount,
|
||||
},
|
||||
}
|
||||
tailSection := &DiffSection{FileName: diffFile.Name, Lines: []*DiffLine{tailDiffLine}}
|
||||
return tailSection, diffLimitedContent
|
||||
diffFile.Sections = append(diffFile.Sections, tailSection)
|
||||
}
|
||||
|
||||
// GetDiffFileName returns the name of the diff file, or its old name in case it was deleted
|
||||
@@ -577,17 +619,24 @@ func (l *limitByteWriter) Write(p []byte) (n int, err error) {
|
||||
return l.buf.Write(p)
|
||||
}
|
||||
|
||||
func getCommitFileLineCountAndLimitedContent(ctx context.Context, gitRepo *git.Repository, commit *git.Commit, filePath string) (lineCount int, limitWriter *limitByteWriter) {
|
||||
blob, err := commit.GetBlobByPath(ctx, gitRepo, filePath)
|
||||
func getCommitFileBlobAndLimitedContent(ctx context.Context, gitRepo *git.Repository, commit *git.Commit, filePath string, limit int) (ret struct {
|
||||
gitBlob *git.Blob
|
||||
blobSize int64
|
||||
lineCount int
|
||||
limitedContent *limitByteWriter
|
||||
},
|
||||
) {
|
||||
var err error
|
||||
ret.limitedContent = &limitByteWriter{limit: limit}
|
||||
ret.gitBlob, err = commit.GetBlobByPath(ctx, gitRepo, filePath)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
return ret
|
||||
}
|
||||
w := &limitByteWriter{limit: MaxFullFileHighlightSizeLimit + 1}
|
||||
lineCount, err = blob.GetBlobLineCount(ctx, w)
|
||||
ret.blobSize, ret.lineCount, err = ret.gitBlob.GetBlobLineCount(ctx, ret.limitedContent)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
return ret
|
||||
}
|
||||
return lineCount, w
|
||||
return ret
|
||||
}
|
||||
|
||||
// Diff represents a difference between two git trees.
|
||||
@@ -1374,19 +1423,22 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit
|
||||
isGenerated = optional.Some(analyze.IsGenerated(diffFile.Name))
|
||||
}
|
||||
diffFile.IsGenerated = isGenerated.Value()
|
||||
tailSection, limitedContent := diffFile.GetTailSectionAndLimitedContent(ctx, gitRepo, beforeCommit, afterCommit)
|
||||
if tailSection != nil {
|
||||
diffFile.Sections = append(diffFile.Sections, tailSection)
|
||||
|
||||
// prepare more details for the rendering, e.g.: blob (file) size, type, limited content to highlight, etc
|
||||
renderDetail := diffFile.prepareDiffRenderDetail(ctx, gitRepo, beforeCommit, afterCommit)
|
||||
if renderDetail.needTailSection {
|
||||
diffFile.addTailSection(renderDetail)
|
||||
}
|
||||
|
||||
shouldFullFileHighlight := attrDiff.Value() == "" // only do highlight if no custom diff command
|
||||
// only do highlight for text files which have no custom diff command
|
||||
shouldFullFileHighlight := !diffFile.IsBin && !diffFile.IsLFSFile && attrDiff.Value() == ""
|
||||
shouldFullFileHighlight = shouldFullFileHighlight && time.Since(startTime) < MaxFullFileHighlightTimeLimit
|
||||
if shouldFullFileHighlight {
|
||||
if limitedContent.LeftContent != nil {
|
||||
diffFile.highlightedLeftLines.value = highlightCodeLinesForDiffFile(diffFile, true /* left */, limitedContent.LeftContent.buf.Bytes())
|
||||
if renderDetail.leftContent != nil {
|
||||
diffFile.highlightedLeftLines.value = highlightCodeLinesForDiffFile(diffFile, true /* left */, renderDetail.leftContent.buf.Bytes())
|
||||
}
|
||||
if limitedContent.RightContent != nil {
|
||||
diffFile.highlightedRightLines.value = highlightCodeLinesForDiffFile(diffFile, false /* right */, limitedContent.RightContent.buf.Bytes())
|
||||
if renderDetail.rightContent != nil {
|
||||
diffFile.highlightedRightLines.value = highlightCodeLinesForDiffFile(diffFile, false /* right */, renderDetail.rightContent.buf.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1404,7 +1456,7 @@ func highlightCodeLinesForDiffFile(diffFile *DiffFile, isLeft bool, rawContent [
|
||||
}
|
||||
|
||||
func highlightCodeLines(name, lang string, sections []*DiffSection, isLeft bool, rawContent []byte) map[int]template.HTML {
|
||||
if setting.Git.DisableDiffHighlight || len(rawContent) > MaxFullFileHighlightSizeLimit {
|
||||
if setting.Git.DisableDiffHighlight || len(rawContent) >= MaxFullFileHighlightSizeLimit {
|
||||
return nil
|
||||
}
|
||||
content := util.UnsafeBytesToString(charset.ToUTF8(rawContent, charset.ConvertOpts{}))
|
||||
|
||||
Reference in New Issue
Block a user