diff --git a/modules/markup/html_emoji.go b/modules/markup/html_emoji.go index 99e28371503..e7c2d1ba6c4 100644 --- a/modules/markup/html_emoji.go +++ b/modules/markup/html_emoji.go @@ -69,6 +69,11 @@ func emojiShortCodeProcessor(ctx *RenderContext, node *html.Node) { m[1] += start start = m[1] + // don't render a shortcode whose ":" directly follows a backtick (an unclosed code span) + if m[0] > 0 && node.Data[m[0]-1] == '`' { + continue + } + alias := node.Data[m[0]:m[1]] var nextChar byte diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 518fe7cf240..f3aec68b6ad 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -377,6 +377,9 @@ func TestRender_emoji(t *testing.T) { test(":100:200", `
:100:200
`) test("std::thread::something", `std::thread::something
`) test(":not exist:", `:not exist:
`) + test("foo `:smile:", "foo `:smile:
") + test("foo `:smile:`", `foo :smile:
foo ` 😄
") } func TestRender_ShortLinks(t *testing.T) { diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index 2ff2f758f50..3c7f9b0a7a1 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -107,19 +107,26 @@ func renderCodeBlock(htmlEscapedTextToRender template.HTML) template.HTML { // RenderIssueTitle renders issue/pull title with defined post processors func (ut *RenderUtils) RenderIssueTitle(text string, repo *repo.Repository) template.HTML { - renderedText, err := markup.PostProcessIssueTitle(renderhelper.NewRenderContextRepoComment(ut.ctx, repo), template.HTMLEscapeString(text)) + // wrap "`…`" in before post-processing so code-span content stays literal, like comment bodies
+ htmlWithCode := renderCodeBlock(template.HTML(template.HTMLEscapeString(text)))
+ renderedText, err := markup.PostProcessIssueTitle(renderhelper.NewRenderContextRepoComment(ut.ctx, repo), string(htmlWithCode))
if err != nil {
log.Error("PostProcessIssueTitle: %v", err)
return ""
}
- return renderCodeBlock(template.HTML(renderedText))
+ return template.HTML(renderedText)
}
// RenderIssueSimpleTitle only renders with emoji and inline code block
func (ut *RenderUtils) RenderIssueSimpleTitle(text string) template.HTML {
- ret := ut.RenderEmoji(text)
- ret = renderCodeBlock(ret)
- return ret
+ // see RenderIssueTitle: wrap code spans before processing emoji
+ htmlWithCode := renderCodeBlock(template.HTML(template.HTMLEscapeString(text)))
+ renderedText, err := markup.PostProcessEmoji(markup.NewRenderContext(ut.ctx), string(htmlWithCode))
+ if err != nil {
+ log.Error("RenderIssueSimpleTitle: %v", err)
+ return ""
+ }
+ return template.HTML(renderedText)
}
func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML {
diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go
index be1190cc496..50a443c7468 100644
--- a/modules/templates/util_render_test.go
+++ b/modules/templates/util_render_test.go
@@ -180,6 +180,38 @@ mail@domain.com
})
}
+func TestRenderIssueTitleCodeSpan(t *testing.T) {
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
+ mockRepo := &repo.Repository{
+ ID: 1, OwnerName: "user13", Name: "repo11",
+ Owner: &user_model.User{ID: 13, Name: "user13"},
+ Units: []*repo.RepoUnit{},
+ }
+ ut := newTestRenderUtils(t)
+
+ cases := []struct {
+ input string
+ expected string
+ emojiSafe bool
+ }{
+ {"foo `:100:`", `foo :100:`, true},
+ {"`#123`", `#123`, false},
+ {"`88fc37a3c0a4dda553bdcfc80c178a58247f42fb`", `88fc37a3c0a4dda553bdcfc80c178a58247f42fb`, false},
+ {"foo `:100:", "foo `:100:", true},
+ {"foo ` :100:", `foo ` + "`" + ` 💯`, true},
+ {":100:", `💯`, true},
+ {"#123", `#123`, false},
+ {"`x`:100:", `x💯`, true},
+ {"a `:100:` b `:+1:` c", `a :100: b :+1: c`, true},
+ }
+ for _, c := range cases {
+ assert.Equal(t, c.expected, string(ut.RenderIssueTitle(c.input, mockRepo)), "input=%q", c.input)
+ if c.emojiSafe {
+ assert.Equal(t, c.expected, string(ut.RenderIssueSimpleTitle(c.input)), "simple input=%q", c.input)
+ }
+ }
+}
+
func TestRenderMarkdownToHtml(t *testing.T) {
defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
expected := `space @mention-user
diff --git a/web_src/css/repo/issue-list.css b/web_src/css/repo/issue-list.css
index 2d55ef83dce..1a635f20199 100644
--- a/web_src/css/repo/issue-list.css
+++ b/web_src/css/repo/issue-list.css
@@ -39,6 +39,7 @@
font-weight: var(--font-weight-semibold);
color: var(--color-text);
text-decoration: none;
+ overflow-wrap: anywhere;
}
#issue-list .branches {