Skip to content

Commit b6ce2d6

Browse files
authored
Refactor markup render system (#32645)
This PR mainly removes some global variables, moves some code and renames some functions to make code clearer. This PR also removes a testing-only option ForceHardLineBreak during refactoring since the behavior is clear now.
1 parent 87bb5ed commit b6ce2d6

File tree

7 files changed

+179
-216
lines changed

7 files changed

+179
-216
lines changed

modules/markup/html.go

Lines changed: 53 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ package markup
55

66
import (
77
"bytes"
8+
"fmt"
89
"io"
910
"regexp"
10-
"slices"
1111
"strings"
1212
"sync"
1313

@@ -133,75 +133,49 @@ func CustomLinkURLSchemes(schemes []string) {
133133
common.GlobalVars().LinkRegex, _ = xurls.StrictMatchingScheme(strings.Join(withAuth, "|"))
134134
}
135135

136-
type postProcessError struct {
137-
context string
138-
err error
139-
}
140-
141-
func (p *postProcessError) Error() string {
142-
return "PostProcess: " + p.context + ", " + p.err.Error()
143-
}
144-
145136
type processor func(ctx *RenderContext, node *html.Node)
146137

147-
var defaultProcessors = []processor{
148-
fullIssuePatternProcessor,
149-
comparePatternProcessor,
150-
codePreviewPatternProcessor,
151-
fullHashPatternProcessor,
152-
shortLinkProcessor,
153-
linkProcessor,
154-
mentionProcessor,
155-
issueIndexPatternProcessor,
156-
commitCrossReferencePatternProcessor,
157-
hashCurrentPatternProcessor,
158-
emailAddressProcessor,
159-
emojiProcessor,
160-
emojiShortCodeProcessor,
161-
}
162-
163-
// PostProcess does the final required transformations to the passed raw HTML
138+
// PostProcessDefault does the final required transformations to the passed raw HTML
164139
// data, and ensures its validity. Transformations include: replacing links and
165140
// emails with HTML links, parsing shortlinks in the format of [[Link]], like
166141
// MediaWiki, linking issues in the format #ID, and mentions in the format
167142
// @user, and others.
168-
func PostProcess(ctx *RenderContext, input io.Reader, output io.Writer) error {
169-
return postProcess(ctx, defaultProcessors, input, output)
170-
}
171-
172-
var commitMessageProcessors = []processor{
173-
fullIssuePatternProcessor,
174-
comparePatternProcessor,
175-
fullHashPatternProcessor,
176-
linkProcessor,
177-
mentionProcessor,
178-
issueIndexPatternProcessor,
179-
commitCrossReferencePatternProcessor,
180-
hashCurrentPatternProcessor,
181-
emailAddressProcessor,
182-
emojiProcessor,
183-
emojiShortCodeProcessor,
143+
func PostProcessDefault(ctx *RenderContext, input io.Reader, output io.Writer) error {
144+
procs := []processor{
145+
fullIssuePatternProcessor,
146+
comparePatternProcessor,
147+
codePreviewPatternProcessor,
148+
fullHashPatternProcessor,
149+
shortLinkProcessor,
150+
linkProcessor,
151+
mentionProcessor,
152+
issueIndexPatternProcessor,
153+
commitCrossReferencePatternProcessor,
154+
hashCurrentPatternProcessor,
155+
emailAddressProcessor,
156+
emojiProcessor,
157+
emojiShortCodeProcessor,
158+
}
159+
return postProcess(ctx, procs, input, output)
184160
}
185161

186162
// RenderCommitMessage will use the same logic as PostProcess, but will disable
187-
// the shortLinkProcessor and will add a defaultLinkProcessor if defaultLink is
188-
// set, which changes every text node into a link to the passed default link.
163+
// the shortLinkProcessor.
189164
func RenderCommitMessage(ctx *RenderContext, content string) (string, error) {
190-
procs := commitMessageProcessors
191-
return renderProcessString(ctx, procs, content)
192-
}
193-
194-
var commitMessageSubjectProcessors = []processor{
195-
fullIssuePatternProcessor,
196-
comparePatternProcessor,
197-
fullHashPatternProcessor,
198-
linkProcessor,
199-
mentionProcessor,
200-
issueIndexPatternProcessor,
201-
commitCrossReferencePatternProcessor,
202-
hashCurrentPatternProcessor,
203-
emojiShortCodeProcessor,
204-
emojiProcessor,
165+
procs := []processor{
166+
fullIssuePatternProcessor,
167+
comparePatternProcessor,
168+
fullHashPatternProcessor,
169+
linkProcessor,
170+
mentionProcessor,
171+
issueIndexPatternProcessor,
172+
commitCrossReferencePatternProcessor,
173+
hashCurrentPatternProcessor,
174+
emailAddressProcessor,
175+
emojiProcessor,
176+
emojiShortCodeProcessor,
177+
}
178+
return postProcessString(ctx, procs, content)
205179
}
206180

207181
var emojiProcessors = []processor{
@@ -214,7 +188,18 @@ var emojiProcessors = []processor{
214188
// emailAddressProcessor, will add a defaultLinkProcessor if defaultLink is set,
215189
// which changes every text node into a link to the passed default link.
216190
func RenderCommitMessageSubject(ctx *RenderContext, defaultLink, content string) (string, error) {
217-
procs := slices.Clone(commitMessageSubjectProcessors)
191+
procs := []processor{
192+
fullIssuePatternProcessor,
193+
comparePatternProcessor,
194+
fullHashPatternProcessor,
195+
linkProcessor,
196+
mentionProcessor,
197+
issueIndexPatternProcessor,
198+
commitCrossReferencePatternProcessor,
199+
hashCurrentPatternProcessor,
200+
emojiShortCodeProcessor,
201+
emojiProcessor,
202+
}
218203
procs = append(procs, func(ctx *RenderContext, node *html.Node) {
219204
ch := &html.Node{Parent: node, Type: html.TextNode, Data: node.Data}
220205
node.Type = html.ElementNode
@@ -223,19 +208,19 @@ func RenderCommitMessageSubject(ctx *RenderContext, defaultLink, content string)
223208
node.Attr = []html.Attribute{{Key: "href", Val: defaultLink}, {Key: "class", Val: "muted"}}
224209
node.FirstChild, node.LastChild = ch, ch
225210
})
226-
return renderProcessString(ctx, procs, content)
211+
return postProcessString(ctx, procs, content)
227212
}
228213

229214
// RenderIssueTitle to process title on individual issue/pull page
230215
func RenderIssueTitle(ctx *RenderContext, title string) (string, error) {
231216
// do not render other issue/commit links in an issue's title - which in most cases is already a link.
232-
return renderProcessString(ctx, []processor{
217+
return postProcessString(ctx, []processor{
233218
emojiShortCodeProcessor,
234219
emojiProcessor,
235220
}, title)
236221
}
237222

238-
func renderProcessString(ctx *RenderContext, procs []processor, content string) (string, error) {
223+
func postProcessString(ctx *RenderContext, procs []processor, content string) (string, error) {
239224
var buf strings.Builder
240225
if err := postProcess(ctx, procs, strings.NewReader(content), &buf); err != nil {
241226
return "", err
@@ -246,7 +231,7 @@ func renderProcessString(ctx *RenderContext, procs []processor, content string)
246231
// RenderDescriptionHTML will use similar logic as PostProcess, but will
247232
// use a single special linkProcessor.
248233
func RenderDescriptionHTML(ctx *RenderContext, content string) (string, error) {
249-
return renderProcessString(ctx, []processor{
234+
return postProcessString(ctx, []processor{
250235
descriptionLinkProcessor,
251236
emojiShortCodeProcessor,
252237
emojiProcessor,
@@ -256,7 +241,7 @@ func RenderDescriptionHTML(ctx *RenderContext, content string) (string, error) {
256241
// RenderEmoji for when we want to just process emoji and shortcodes
257242
// in various places it isn't already run through the normal markdown processor
258243
func RenderEmoji(ctx *RenderContext, content string) (string, error) {
259-
return renderProcessString(ctx, emojiProcessors, content)
244+
return postProcessString(ctx, emojiProcessors, content)
260245
}
261246

262247
func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output io.Writer) error {
@@ -276,7 +261,7 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output
276261
strings.NewReader("</body></html>"),
277262
))
278263
if err != nil {
279-
return &postProcessError{"invalid HTML", err}
264+
return fmt.Errorf("markup.postProcess: invalid HTML: %w", err)
280265
}
281266

282267
if node.Type == html.DocumentNode {
@@ -308,7 +293,7 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output
308293
// Render everything to buf.
309294
for _, node := range newNodes {
310295
if err := html.Render(output, node); err != nil {
311-
return &postProcessError{"error rendering processed HTML", err}
296+
return fmt.Errorf("markup.postProcess: html.Render: %w", err)
312297
}
313298
}
314299
return nil

modules/markup/html_internal_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,12 +277,12 @@ func TestRender_AutoLink(t *testing.T) {
277277

278278
test := func(input, expected string) {
279279
var buffer strings.Builder
280-
err := PostProcess(NewTestRenderContext(localMetas), strings.NewReader(input), &buffer)
280+
err := PostProcessDefault(NewTestRenderContext(localMetas), strings.NewReader(input), &buffer)
281281
assert.Equal(t, err, nil)
282282
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
283283

284284
buffer.Reset()
285-
err = PostProcess(NewTestRenderContext(localMetas), strings.NewReader(input), &buffer)
285+
err = PostProcessDefault(NewTestRenderContext(localMetas), strings.NewReader(input), &buffer)
286286
assert.Equal(t, err, nil)
287287
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
288288
}

modules/markup/html_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -445,14 +445,14 @@ func Test_ParseClusterFuzz(t *testing.T) {
445445
data := "<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
446446

447447
var res strings.Builder
448-
err := markup.PostProcess(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
448+
err := markup.PostProcessDefault(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
449449
assert.NoError(t, err)
450450
assert.NotContains(t, res.String(), "<html")
451451

452452
data = "<!DOCTYPE html>\n<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
453453

454454
res.Reset()
455-
err = markup.PostProcess(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
455+
err = markup.PostProcessDefault(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
456456

457457
assert.NoError(t, err)
458458
assert.NotContains(t, res.String(), "<html")
@@ -464,7 +464,7 @@ func TestPostProcess_RenderDocument(t *testing.T) {
464464

465465
test := func(input, expected string) {
466466
var res strings.Builder
467-
err := markup.PostProcess(markup.NewTestRenderContext(markup.TestAppURL, map[string]string{"user": "go-gitea", "repo": "gitea"}), strings.NewReader(input), &res)
467+
err := markup.PostProcessDefault(markup.NewTestRenderContext(markup.TestAppURL, map[string]string{"user": "go-gitea", "repo": "gitea"}), strings.NewReader(input), &res)
468468
assert.NoError(t, err)
469469
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res.String()))
470470
}
@@ -501,7 +501,7 @@ func TestIssue16020(t *testing.T) {
501501
data := `<img src="data:image/png;base64,i//V"/>`
502502

503503
var res strings.Builder
504-
err := markup.PostProcess(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
504+
err := markup.PostProcessDefault(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
505505
assert.NoError(t, err)
506506
assert.Equal(t, data, res.String())
507507
}
@@ -514,23 +514,23 @@ func BenchmarkEmojiPostprocess(b *testing.B) {
514514
b.ResetTimer()
515515
for i := 0; i < b.N; i++ {
516516
var res strings.Builder
517-
err := markup.PostProcess(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
517+
err := markup.PostProcessDefault(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
518518
assert.NoError(b, err)
519519
}
520520
}
521521

522522
func TestFuzz(t *testing.T) {
523523
s := "t/l/issues/8#/../../a"
524524
renderContext := markup.NewTestRenderContext()
525-
err := markup.PostProcess(renderContext, strings.NewReader(s), io.Discard)
525+
err := markup.PostProcessDefault(renderContext, strings.NewReader(s), io.Discard)
526526
assert.NoError(t, err)
527527
}
528528

529529
func TestIssue18471(t *testing.T) {
530530
data := `http://domain/org/repo/compare/783b039...da951ce`
531531

532532
var res strings.Builder
533-
err := markup.PostProcess(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
533+
err := markup.PostProcessDefault(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
534534

535535
assert.NoError(t, err)
536536
assert.Equal(t, `<a href="http://domain/org/repo/compare/783b039...da951ce" class="compare"><code class="nohighlight">783b039...da951ce</code></a>`, res.String())

modules/markup/markdown/goldmark.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
8080
// many places render non-comment contents with no mode=document, then these contents also use comment's hard line break setting
8181
// especially in many tests.
8282
markdownLineBreakStyle := ctx.RenderOptions.Metas["markdownLineBreakStyle"]
83-
if markup.RenderBehaviorForTesting.ForceHardLineBreak {
84-
v.SetHardLineBreak(true)
85-
} else if markdownLineBreakStyle == "comment" {
83+
if markdownLineBreakStyle == "comment" {
8684
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInComments)
8785
} else if markdownLineBreakStyle == "document" {
8886
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInDocuments)

0 commit comments

Comments
 (0)