This commit is contained in:
@@ -6,15 +6,18 @@ package markup
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/htmlutil"
|
||||
"code.gitea.io/gitea/modules/markup/common"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
"gitea.dev/modules/htmlutil"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/markup/common"
|
||||
"gitea.dev/modules/translation"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
"golang.org/x/net/html/atom"
|
||||
@@ -149,9 +152,8 @@ func PostProcessDefault(ctx *RenderContext, input io.Reader, output io.Writer) e
|
||||
return postProcess(ctx, procs, input, output)
|
||||
}
|
||||
|
||||
// PostProcessCommitMessage will use the same logic as PostProcess, but will disable
|
||||
// the shortLinkProcessor.
|
||||
func PostProcessCommitMessage(ctx *RenderContext, content string) (string, error) {
|
||||
// PostProcessCommitMessage will use the same logic as PostProcess, but will disable the shortLinkProcessor.
|
||||
func PostProcessCommitMessage(ctx *RenderContext, content template.HTML) template.HTML {
|
||||
procs := []processor{
|
||||
fullIssuePatternProcessor,
|
||||
comparePatternProcessor,
|
||||
@@ -165,7 +167,7 @@ func PostProcessCommitMessage(ctx *RenderContext, content string) (string, error
|
||||
emojiProcessor,
|
||||
emojiShortCodeProcessor,
|
||||
}
|
||||
return postProcessString(ctx, procs, content)
|
||||
return postProcessHTML(ctx, procs, content)
|
||||
}
|
||||
|
||||
var emojiProcessors = []processor{
|
||||
@@ -173,16 +175,25 @@ var emojiProcessors = []processor{
|
||||
emojiProcessor,
|
||||
}
|
||||
|
||||
// isBareURLSubject reports whether the (HTML-escaped) commit subject content
|
||||
// is entirely a single URL, ignoring leading/trailing whitespace.
|
||||
func isBareURLSubject(content string) bool {
|
||||
s := strings.TrimSpace(html.UnescapeString(content))
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
m := common.GlobalVars().LinkRegex.FindStringIndex(s)
|
||||
return m != nil && m[0] == 0 && m[1] == len(s)
|
||||
}
|
||||
|
||||
// PostProcessCommitMessageSubject will use the same logic as PostProcess and
|
||||
// PostProcessCommitMessage, but will disable the shortLinkProcessor and
|
||||
// emailAddressProcessor, will add a defaultLinkProcessor if defaultLink is set,
|
||||
// which changes every text node into a link to the passed default link.
|
||||
func PostProcessCommitMessageSubject(ctx *RenderContext, defaultLink, content string) (string, error) {
|
||||
// emailAddressProcessor, and wraps the whole subject in defaultLink.
|
||||
func PostProcessCommitMessageSubject(ctx *RenderContext, defaultLink string, content template.HTML) template.HTML {
|
||||
procs := []processor{
|
||||
fullIssuePatternProcessor,
|
||||
comparePatternProcessor,
|
||||
fullHashPatternProcessor,
|
||||
linkProcessor,
|
||||
mentionProcessor,
|
||||
issueIndexPatternProcessor,
|
||||
commitCrossReferencePatternProcessor,
|
||||
@@ -190,32 +201,42 @@ func PostProcessCommitMessageSubject(ctx *RenderContext, defaultLink, content st
|
||||
emojiShortCodeProcessor,
|
||||
emojiProcessor,
|
||||
}
|
||||
// When the whole subject is a bare URL, linkProcessor would turn it into
|
||||
// a competing anchor and hijack the surrounding defaultLink wrapper, leaving
|
||||
// the subject visually unclickable. Match GitHub: render such subjects as
|
||||
// plain text inside defaultLink. Partial URLs inside larger text still become
|
||||
// their own links (nested anchors aren't legal HTML, so the outer defaultLink
|
||||
// naturally breaks on that span, same as on GitHub).
|
||||
if !isBareURLSubject(string(content)) {
|
||||
procs = append(procs, linkProcessor)
|
||||
}
|
||||
procs = append(procs, func(ctx *RenderContext, node *html.Node) {
|
||||
ch := &html.Node{Parent: node, Type: html.TextNode, Data: node.Data}
|
||||
node.Type = html.ElementNode
|
||||
node.Data = "a"
|
||||
node.DataAtom = atom.A
|
||||
node.Attr = []html.Attribute{{Key: "href", Val: defaultLink}, {Key: "class", Val: "muted"}}
|
||||
node.Attr = []html.Attribute{{Key: "href", Val: defaultLink}, {Key: "class", Val: "muted title-full-link"}}
|
||||
node.FirstChild, node.LastChild = ch, ch
|
||||
})
|
||||
return postProcessString(ctx, procs, content)
|
||||
rendered := postProcessHTML(ctx, procs, content)
|
||||
return htmlutil.HTMLFormat(`<span class="title-full-link-hover">%s</span>`, rendered)
|
||||
}
|
||||
|
||||
// PostProcessIssueTitle to process title on individual issue/pull page
|
||||
func PostProcessIssueTitle(ctx *RenderContext, title string) (string, error) {
|
||||
return postProcessString(ctx, []processor{
|
||||
func PostProcessIssueTitle(ctx *RenderContext, titleHTML template.HTML) template.HTML {
|
||||
return postProcessHTML(ctx, []processor{
|
||||
issueIndexPatternProcessor,
|
||||
commitCrossReferencePatternProcessor,
|
||||
hashCurrentPatternProcessor,
|
||||
emojiShortCodeProcessor,
|
||||
emojiProcessor,
|
||||
}, title)
|
||||
}, titleHTML)
|
||||
}
|
||||
|
||||
// PostProcessDescriptionHTML will use similar logic as PostProcess, but will
|
||||
// use a single special linkProcessor.
|
||||
func PostProcessDescriptionHTML(ctx *RenderContext, content string) (string, error) {
|
||||
return postProcessString(ctx, []processor{
|
||||
func PostProcessDescriptionHTML(ctx *RenderContext, content template.HTML) template.HTML {
|
||||
return postProcessHTML(ctx, []processor{
|
||||
descriptionLinkProcessor,
|
||||
emojiShortCodeProcessor,
|
||||
emojiProcessor,
|
||||
@@ -223,17 +244,18 @@ func PostProcessDescriptionHTML(ctx *RenderContext, content string) (string, err
|
||||
}
|
||||
|
||||
// PostProcessEmoji for when we want to just process emoji and shortcodes
|
||||
// in various places it isn't already run through the normal markdown processor
|
||||
func PostProcessEmoji(ctx *RenderContext, content string) (string, error) {
|
||||
return postProcessString(ctx, emojiProcessors, content)
|
||||
// in various places it isn't already run through the normal Markdown processor
|
||||
func PostProcessEmoji(ctx *RenderContext, content template.HTML) template.HTML {
|
||||
return postProcessHTML(ctx, emojiProcessors, content)
|
||||
}
|
||||
|
||||
func postProcessString(ctx *RenderContext, procs []processor, content string) (string, error) {
|
||||
func postProcessHTML(ctx *RenderContext, procs []processor, content template.HTML) template.HTML {
|
||||
var buf strings.Builder
|
||||
if err := postProcess(ctx, procs, strings.NewReader(content), &buf); err != nil {
|
||||
return "", err
|
||||
if err := postProcess(ctx, procs, strings.NewReader(string(content)), &buf); err != nil {
|
||||
log.Warn("postProcessHTML err: %v, input: %s", err, util.TruncateRunes(string(content), 200))
|
||||
return content
|
||||
}
|
||||
return buf.String(), nil
|
||||
return template.HTML(buf.String())
|
||||
}
|
||||
|
||||
func RenderTocHeadingItems(ctx *RenderContext, nodeDetailsAttrs map[string]string, out io.Writer) {
|
||||
|
||||
Reference in New Issue
Block a user