feat: vendor gitea 1.16.2

This commit is contained in:
2026-06-02 08:36:01 +00:00
parent 247cd60f69
commit 54798b4615
2145 changed files with 162965 additions and 126447 deletions
@@ -7,12 +7,45 @@ import (
"bytes"
"html/template"
"strings"
"unicode/utf8"
"code.gitea.io/gitea/modules/util"
"github.com/sergi/go-diff/diffmatchpatch"
)
// token is a html tag or entity, eg: "<span ...>", "</span>", "&lt;"
func extractHTMLToken(s string) (before, token, after string, valid bool) {
// extractDiffTokenRemainingFullTag tries to extract full tag with content from the remaining string
// e.g. for input: "content</span>the-rest...", it returns "content</span>", "the-rest...", true
func extractDiffTokenRemainingFullTag(s string) (token, after string, valid bool) {
pos := 0
for ; pos < len(s); pos++ {
c := s[pos]
if c == '<' {
break
}
// keep in mind: even if we'd like to relax this check,
// we should never ignore "&" because it is for HTML entity and can't be safely used in the diff algorithm,
// because diff between "&lt;" and "&gt;" will generate broken result.
isSymbolChar := 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' || c == '_' || c == '-' || c == '.'
if !isSymbolChar {
return "", s, false
}
}
if pos+1 >= len(s) || s[pos+1] != '/' {
return "", s, false
}
pos2 := strings.IndexByte(s[pos:], '>')
if pos2 == -1 {
return "", s, false
}
return s[:pos+pos2+1], s[pos+pos2+1:], true
}
// Returned token:
// * full tag with content: "<<span>content</span>>", it is used to optimize diff results to highlight the whole changed symbol
// * opening/closing tag: "<span ...>" or "</span>"
// * HTML entity: "&lt;"
func extractDiffToken(s string) (before, token, after string, valid bool) {
for pos1 := 0; pos1 < len(s); pos1++ {
switch s[pos1] {
case '<':
@@ -20,7 +53,15 @@ func extractHTMLToken(s string) (before, token, after string, valid bool) {
if pos2 == -1 {
return "", "", s, false
}
return s[:pos1], s[pos1 : pos1+pos2+1], s[pos1+pos2+1:], true
before, token, after = s[:pos1], s[pos1:pos1+pos2+1], s[pos1+pos2+1:]
if !strings.HasPrefix(token, "</") {
// try to extract full tag with content, e.g. `<<span>content</span>>`, to optimize diff results
if fullTokenRemaining, fullTokenAfter, ok := extractDiffTokenRemainingFullTag(after); ok {
return before, "<" + token + fullTokenRemaining + ">", fullTokenAfter, true
}
}
return before, token, after, true
case '&':
pos2 := strings.IndexByte(s[pos1:], ';')
if pos2 == -1 {
@@ -47,7 +88,9 @@ type highlightCodeDiff struct {
placeholderOverflowCount int
lineWrapperTags []string
diffCodeAddedOpen rune
diffCodeRemovedOpen rune
diffCodeClose rune
}
func newHighlightCodeDiff() *highlightCodeDiff {
@@ -87,11 +130,26 @@ func (hcd *highlightCodeDiff) collectUsedRunes(code template.HTML) {
}
}
func (hcd *highlightCodeDiff) diffLineWithHighlight(lineType DiffLineType, codeA, codeB template.HTML) template.HTML {
return hcd.diffLineWithHighlightWrapper(nil, lineType, codeA, codeB)
func (hcd *highlightCodeDiff) diffEqualPartIsSpaceOnly(s string) bool {
for _, r := range s {
if r >= hcd.placeholderBegin {
recovered := hcd.placeholderTokenMap[r]
if strings.HasPrefix(recovered, "<<") {
return false // a full tag with content, it can't be space-only
} else if strings.HasPrefix(recovered, "<") {
continue // a single opening/closing tag, skip the tag and continue to check the content
}
return false // otherwise, it must be an HTML entity, it can't be space-only
}
isSpace := r == ' ' || r == '\t' || r == '\n' || r == '\r'
if !isSpace {
return false
}
}
return true
}
func (hcd *highlightCodeDiff) diffLineWithHighlightWrapper(lineWrapperTags []string, lineType DiffLineType, codeA, codeB template.HTML) template.HTML {
func (hcd *highlightCodeDiff) diffLineWithHighlight(lineType DiffLineType, codeA, codeB template.HTML) template.HTML {
hcd.collectUsedRunes(codeA)
hcd.collectUsedRunes(codeB)
@@ -104,32 +162,44 @@ func (hcd *highlightCodeDiff) diffLineWithHighlightWrapper(lineWrapperTags []str
buf := bytes.NewBuffer(nil)
// restore the line wrapper tags <span class="line"> and <span class="cl">, if necessary
for _, tag := range lineWrapperTags {
buf.WriteString(tag)
if hcd.diffCodeClose == 0 {
// tests can pre-set the placeholders
hcd.diffCodeAddedOpen = hcd.registerTokenAsPlaceholder(`<span class="added-code">`)
hcd.diffCodeRemovedOpen = hcd.registerTokenAsPlaceholder(`<span class="removed-code">`)
hcd.diffCodeClose = hcd.registerTokenAsPlaceholder(`</span><!-- diff-code-close -->`)
}
addedCodePrefix := hcd.registerTokenAsPlaceholder(`<span class="added-code">`)
removedCodePrefix := hcd.registerTokenAsPlaceholder(`<span class="removed-code">`)
codeTagSuffix := hcd.registerTokenAsPlaceholder(`</span>`)
equalPartSpaceOnly := true
for _, diff := range diffs {
if diff.Type != diffmatchpatch.DiffEqual {
continue
}
if equalPartSpaceOnly = hcd.diffEqualPartIsSpaceOnly(diff.Text); !equalPartSpaceOnly {
break
}
}
if codeTagSuffix != 0 {
// 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)
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(addedCodePrefix)
buf.WriteRune(hcd.diffCodeAddedOpen)
buf.WriteString(diff.Text)
buf.WriteRune(codeTagSuffix)
buf.WriteRune(hcd.diffCodeClose)
case diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel:
buf.WriteRune(removedCodePrefix)
buf.WriteRune(hcd.diffCodeRemovedOpen)
buf.WriteString(diff.Text)
buf.WriteRune(codeTagSuffix)
buf.WriteRune(hcd.diffCodeClose)
}
}
} else {
// placeholder map space is exhausted
// 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 {
@@ -137,19 +207,21 @@ func (hcd *highlightCodeDiff) diffLineWithHighlightWrapper(lineWrapperTags []str
}
}
}
for range lineWrapperTags {
buf.WriteString("</span>")
}
return hcd.recoverOneDiff(buf.String())
}
func (hcd *highlightCodeDiff) registerTokenAsPlaceholder(token string) rune {
recovered := token
if token[0] == '<' && token[1] != '<' {
// when recovering a single tag, only use the tag itself, ignore the trailing comment (for how the comment is generated, see the code in `convert` function)
recovered = token[:strings.IndexByte(token, '>')+1]
}
placeholder, ok := hcd.tokenPlaceholderMap[token]
if !ok {
placeholder = hcd.nextPlaceholder()
if placeholder != 0 {
hcd.tokenPlaceholderMap[token] = placeholder
hcd.placeholderTokenMap[placeholder] = token
hcd.placeholderTokenMap[placeholder] = recovered
}
}
return placeholder
@@ -160,44 +232,42 @@ func (hcd *highlightCodeDiff) convertToPlaceholders(htmlContent template.HTML) s
var tagStack []string
res := strings.Builder{}
firstRunForLineTags := hcd.lineWrapperTags == nil
htmlCode := string(htmlContent)
var beforeToken, token string
var valid bool
htmlCode := string(htmlContent)
// the standard chroma highlight HTML is "<span class="line [hl]"><span class="cl"> ... </span></span>"
for {
beforeToken, token, htmlCode, valid = extractHTMLToken(htmlCode)
beforeToken, token, htmlCode, valid = extractDiffToken(htmlCode)
if !valid || token == "" {
break
}
// write the content before the token into result string, and consume the token in the string
res.WriteString(beforeToken)
// the standard chroma highlight HTML is `<span class="line [hl]"><span class="cl"> ... </span></span>`
// the line wrapper tags should be removed before diff
if strings.HasPrefix(token, `<span class="line`) || strings.HasPrefix(token, `<span class="cl"`) {
if firstRunForLineTags {
// if this is the first run for converting, save the line wrapper tags for later use, they should be added back
hcd.lineWrapperTags = append(hcd.lineWrapperTags, token)
}
htmlCode = strings.TrimSuffix(htmlCode, "</span>")
continue
}
var tokenInMap string
if strings.HasSuffix(token, "</") { // for closing tag
if strings.HasPrefix(token, "</") { // for closing tag
if len(tagStack) == 0 {
break // invalid diff result, no opening tag but see closing tag
continue // no opening tag but see closing tag, skip it
}
// make sure the closing tag in map is related to the open tag, to make the diff algorithm can match the opening/closing tags
// the closing tag will be recorded in the map by key "</span><!-- <span the-opening> -->" for "<span the-opening>"
tokenInMap = token + "<!-- " + tagStack[len(tagStack)-1] + "-->"
tagStack = tagStack[:len(tagStack)-1]
} else if token[0] == '<' { // for opening tag
tokenInMap = token
tagStack = append(tagStack, token)
} else if token[0] == '&' { // for html entity
} else if token[0] == '<' {
if token[1] == '<' {
// full tag `<<span>content</span>>`, recover to `<span>content</span>`
tokenInMap = token
} else {
// opening tag
tokenInMap = token
tagStack = append(tagStack, token)
}
} else if token[0] == '&' { // for HTML entity
tokenInMap = token
} // else: impossible
@@ -210,8 +280,13 @@ func (hcd *highlightCodeDiff) convertToPlaceholders(htmlContent template.HTML) s
// unfortunately, all private use runes has been exhausted, no more placeholder could be used, no more converting
// usually, the exhausting won't occur in real cases, the magnitude of used placeholders is not larger than that of the CSS classes outputted by chroma.
hcd.placeholderOverflowCount++
if strings.HasPrefix(token, "<<") {
pos1 := strings.IndexByte(token, '>')
pos2 := strings.LastIndexByte(token, '<')
res.WriteString(token[pos1+1 : pos2]) // recover to `content` from "<<span>content</span>>"
}
if strings.HasPrefix(token, "&") {
// when the token is a html entity, something must be outputted even if there is no placeholder.
// when the token is an HTML entity, something must be outputted even if there is no placeholder.
res.WriteRune(0xFFFD) // replacement character TODO: how to handle this case more gracefully?
res.WriteString(token[1:]) // still output the entity code part, otherwise there will be no diff result.
}
@@ -223,43 +298,99 @@ func (hcd *highlightCodeDiff) convertToPlaceholders(htmlContent template.HTML) s
return res.String()
}
// recoverOneRune tries to recover one rune
// * if the rune is a placeholder, it will be recovered to the corresponding content
// * otherwise it will be returned as is
func (hcd *highlightCodeDiff) recoverOneRune(buf []byte) (r rune, runeLen int, isSingleTag bool, recovered string) {
r, runeLen = utf8.DecodeRune(buf)
token := hcd.placeholderTokenMap[r]
if token == "" {
return r, runeLen, false, "" // rune itself, not a placeholder
} else if token[0] == '<' {
if token[1] == '<' {
return 0, runeLen, false, token[1 : len(token)-1] // full tag `<<span>content</span>>`, recover to `<span>content</span>`
}
return r, runeLen, true, token // single tag
}
return 0, runeLen, false, token // HTML entity
}
func (hcd *highlightCodeDiff) recoverOneDiff(str string) template.HTML {
sb := strings.Builder{}
var tagStack []string
var diffCodeOpenTag string
diffCodeCloseTag := hcd.placeholderTokenMap[hcd.diffCodeClose]
strBytes := util.UnsafeStringToBytes(str)
for _, r := range str {
token, ok := hcd.placeholderTokenMap[r]
if !ok || token == "" {
sb.WriteRune(r) // if the rune is not a placeholder, write it as it is
continue
}
var tokenToRecover string
if strings.HasPrefix(token, "</") { // for closing tag
// only get the tag itself, ignore the trailing comment (for how the comment is generated, see the code in `convert` function)
tokenToRecover = token[:strings.IndexByte(token, '>')+1]
if len(tagStack) == 0 {
continue // if no opening tag in stack yet, skip the closing tag
// this loop is slightly longer than expected, for performance consideration
for idx := 0; idx < len(strBytes); {
// take a look at the next rune
r, runeLen, isSingleTag, recovered := hcd.recoverOneRune(strBytes[idx:])
idx += runeLen
// loop section 1: if it isn't a single tag, then try to find the following runes until the next single tag, and recover them together
if !isSingleTag {
if diffCodeOpenTag != "" {
// start the "added/removed diff tag" if the current token is in the diff part
sb.WriteString(diffCodeOpenTag)
}
tagStack = tagStack[:len(tagStack)-1]
} else if token[0] == '<' { // for opening tag
tokenToRecover = token
tagStack = append(tagStack, token)
} else if token[0] == '&' { // for html entity
tokenToRecover = token
} // else: impossible
sb.WriteString(tokenToRecover)
if recovered != "" {
sb.WriteString(recovered)
} else {
sb.WriteRune(r)
}
// inner loop to recover following runes until the next single tag
for idx < len(strBytes) {
r, runeLen, isSingleTag, recovered = hcd.recoverOneRune(strBytes[idx:])
idx += runeLen
if isSingleTag {
break
}
if recovered != "" {
sb.WriteString(recovered)
} else {
sb.WriteRune(r)
}
}
if diffCodeOpenTag != "" {
// end the "added/removed diff tag" if the current token is in the diff part
sb.WriteString(diffCodeCloseTag)
}
}
if !isSingleTag {
break // the inner loop has already consumed all remaining runes, no more single tag found
}
// loop section 2: for opening/closing HTML tags
placeholder := r
if recovered[1] != '/' { // opening tag
if placeholder == hcd.diffCodeAddedOpen || placeholder == hcd.diffCodeRemovedOpen {
diffCodeOpenTag = recovered
recovered = ""
} else {
tagStack = append(tagStack, recovered)
}
} else { // closing tag
if placeholder == hcd.diffCodeClose {
diffCodeOpenTag = "" // the highlighted diff is closed, no more diff
recovered = ""
} else if len(tagStack) != 0 {
tagStack = tagStack[:len(tagStack)-1]
} else {
recovered = ""
}
}
sb.WriteString(recovered)
}
if len(tagStack) > 0 {
// close all opening tags
for i := len(tagStack) - 1; i >= 0; i-- {
tagToClose := tagStack[i]
// get the closing tag "</span>" from "<span class=...>" or "<span>"
pos := strings.IndexAny(tagToClose, " >")
if pos != -1 {
sb.WriteString("</" + tagToClose[1:pos] + ">")
} // else: impossible. every tag was pushed into the stack by the code above and is valid HTML opening tag
}
// close all opening tags
for i := len(tagStack) - 1; i >= 0; i-- {
tagToClose := tagStack[i]
// get the closing tag "</span>" from "<span class=...>" or "<span>"
pos := strings.IndexAny(tagToClose, " >")
// pos must be positive, because the tags were pushed by us
sb.WriteString("</" + tagToClose[1:pos] + ">")
}
return template.HTML(sb.String())
}