fix(markup): keep commit title emoji inside links (#39986) (#40032)

Backport #39986 by @Hello-Liar

Render emoji inside the default commit-title link so the whole title
remains clickable, including emoji-only titles. Explicit links and
mentions keep their own destinations.

Fixes https://github.com/go-gitea/gitea/issues/31873

Verified failing-before/passing-after native, shortcode, custom, and
emoji-only cases; markup/template tests and Go lint pass.
Browser-checked actual renderer output; blue outlines show link
boundaries.


![Before](https://github.com/user-attachments/assets/f56a4607-3265-4536-9fa2-ecc297dada6f)

![After](https://github.com/user-attachments/assets/7e0ad96d-6732-4d41-b7c6-cc28e90b747a)

AI-assisted by Codex (GPT-6); prepared and tested by the assistant.

Co-authored-by: leo <80651389+Hello-Liar@users.noreply.github.com>
This commit is contained in:
Giteabotandleo authored and GitHub committed 2026-10-26 20:07:13 +00:00
1 parent 2042caf945
commit 8e83bb0e61
2 files changed
+20 -2

No files matched your search

+3 -2
View File
@@ -153,8 +153,6 @@ func PostProcessCommitMessageSubject(ctx *RenderContext, defaultLink, content st
issueIndexPatternProcessor,
commitCrossReferencePatternProcessor,
hashCurrentPatternProcessor,
emojiShortCodeProcessor,
emojiProcessor,
linkProcessor,
}
@@ -175,6 +173,9 @@ func PostProcessCommitMessageSubject(ctx *RenderContext, defaultLink, content st
node.DataAtom = atom.A
node.Attr = []html.Attribute{{Key: "href", Val: defaultLink}, {Key: "class", Val: "muted title-full-link"}}
node.FirstChild, node.LastChild = ch, ch
for n := node.FirstChild; n != nil; {
n = visitNode(ctx, emojiProcessors, n)
}
})
rendered := postProcessHTML(ctx, procs, htmlutil.EscapeString(content))
return htmlutil.HTMLFormat(`<span class="title-full-link-hover">%s</span>`, rendered)
+17
View File
@@ -158,6 +158,23 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
assert.EqualValues(t, expected, newTestRenderUtils(t).RenderCommitMessageLinkSubject("see https://example.com/x here", "https://example.com/link", mockRepo))
})
t.Run("RenderCommitMessageLinkSubjectEmoji", func(t *testing.T) {
defer test.MockVariableValue(&setting.UI.CustomEmojisMap, map[string]string{"custom-emoji": ":custom-emoji:"})()
for _, tt := range []struct {
name, subject, content string
}{
{"native", "Bug ❌ Feature ✅", `Bug <span class="emoji" data-alias="x">❌</span> Feature <span class="emoji" data-alias="white_check_mark">✅</span>`},
{"shortcode", "Ship :+1: today", `Ship <span class="emoji" data-alias="+1">👍</span> today`},
{"emoji only", "👍", `<span class="emoji" data-alias="+1">👍</span>`},
{"custom", "Ship :custom-emoji:", `Ship <span class="emoji"><img alt=":custom-emoji:" src="/assets/img/emoji/custom-emoji.png"/></span>`},
} {
t.Run(tt.name, func(t *testing.T) {
expected := `<span class="title-full-link-hover"><a href="https://example.com/link" class="muted title-full-link">` + tt.content + `</a></span>`
assert.EqualValues(t, expected, newTestRenderUtils(t).RenderCommitMessageLinkSubject(tt.subject, "https://example.com/link", mockRepo))
})
}
})
t.Run("RenderIssueTitle", func(t *testing.T) {
defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
expected := ` space @mention-user<SPACE><SPACE>