From 3f9c3e7bc394c115ccc4818d6505f1f68de350d2 Mon Sep 17 00:00:00 2001
From: wxiaoguang
Date: Thu, 14 Nov 2024 13:02:11 +0800
Subject: [PATCH] Refactor render system (#32492)
There were too many patches to the Render system, it's really difficult
to make further improvements.
This PR clears the legacy problems and fix TODOs.
1. Rename `RenderContext.Type` to `RenderContext.MarkupType` to clarify
its usage.
2. Use `ContentMode` to replace `meta["mode"]` and `IsWiki`, to clarify
the rendering behaviors.
3. Use "wiki" mode instead of "mode=gfm + wiki=true"
4. Merge `renderByType` and `renderByFile`
5. Add more comments
----
The problem of "mode=document": in many cases it is not set, so many
non-comment places use comment's hard line break incorrectly
---
models/repo/repo.go | 2 -
modules/markup/console/console.go | 23 +---
modules/markup/html.go | 9 +-
modules/markup/html_codepreview_test.go | 5 +-
modules/markup/html_internal_test.go | 22 ++--
modules/markup/html_issue.go | 5 +-
modules/markup/html_link.go | 4 +-
modules/markup/html_node.go | 4 +-
modules/markup/html_test.go | 52 ++++-----
modules/markup/markdown/goldmark.go | 7 +-
modules/markup/markdown/markdown.go | 4 +-
modules/markup/markdown/markdown_test.go | 40 ++++---
modules/markup/markdown/transform_image.go | 2 +-
modules/markup/orgmode/orgmode.go | 5 +-
modules/markup/orgmode/orgmode_test.go | 3 +-
modules/markup/render.go | 108 ++++++++++--------
modules/structs/miscellaneous.go | 10 +-
modules/templates/util_render.go | 10 +-
modules/templates/util_render_test.go | 15 ++-
routers/api/v1/misc/markup.go | 12 +-
routers/api/v1/misc/markup_test.go | 13 ++-
routers/common/markup.go | 70 +++++-------
routers/web/feed/convert.go | 1 -
routers/web/misc/markup.go | 4 +-
routers/web/repo/view.go | 12 +-
routers/web/repo/wiki.go | 6 +-
routers/web/user/profile.go | 1 -
services/context/org.go | 3 +-
templates/swagger/v1_json.tmpl | 8 +-
tests/integration/markup_external_test.go | 27 ++++-
.../js/features/comp/ComboMarkdownEditor.ts | 3 -
web_src/js/features/repo-wiki.ts | 4 +-
32 files changed, 237 insertions(+), 257 deletions(-)
diff --git a/models/repo/repo.go b/models/repo/repo.go
index 68f8e16a21..4776ff0b9c 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -479,7 +479,6 @@ func (repo *Repository) ComposeMetas(ctx context.Context) map[string]string {
metas := map[string]string{
"user": repo.OwnerName,
"repo": repo.Name,
- "mode": "comment",
}
unit, err := repo.GetUnit(ctx, unit.TypeExternalTracker)
@@ -521,7 +520,6 @@ func (repo *Repository) ComposeDocumentMetas(ctx context.Context) map[string]str
for k, v := range repo.ComposeMetas(ctx) {
metas[k] = v
}
- metas["mode"] = "document"
repo.DocumentRenderingMetas = metas
}
return repo.DocumentRenderingMetas
diff --git a/modules/markup/console/console.go b/modules/markup/console/console.go
index 01653565fe..d991527b80 100644
--- a/modules/markup/console/console.go
+++ b/modules/markup/console/console.go
@@ -8,7 +8,6 @@ import (
"io"
"path/filepath"
"regexp"
- "strings"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
@@ -17,9 +16,6 @@ import (
"github.com/go-enry/go-enry/v2"
)
-// MarkupName describes markup's name
-var MarkupName = "console"
-
func init() {
markup.RegisterRenderer(Renderer{})
}
@@ -29,7 +25,7 @@ type Renderer struct{}
// Name implements markup.Renderer
func (Renderer) Name() string {
- return MarkupName
+ return "console"
}
// Extensions implements markup.Renderer
@@ -67,20 +63,3 @@ func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Wri
_, err = output.Write(buf)
return err
}
-
-// Render renders terminal colors to HTML with all specific handling stuff.
-func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
- if ctx.Type == "" {
- ctx.Type = MarkupName
- }
- return markup.Render(ctx, input, output)
-}
-
-// RenderString renders terminal colors in string to HTML with all specific handling stuff and return string
-func RenderString(ctx *markup.RenderContext, content string) (string, error) {
- var buf strings.Builder
- if err := Render(ctx, strings.NewReader(content), &buf); err != nil {
- return "", err
- }
- return buf.String(), nil
-}
diff --git a/modules/markup/html.go b/modules/markup/html.go
index e2eefefc4b..54c65c95d2 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -442,12 +442,11 @@ func createLink(href, content, class string) *html.Node {
a := &html.Node{
Type: html.ElementNode,
Data: atom.A.String(),
- Attr: []html.Attribute{
- {Key: "href", Val: href},
- {Key: "data-markdown-generated-content"},
- },
+ Attr: []html.Attribute{{Key: "href", Val: href}},
+ }
+ if !RenderBehaviorForTesting.DisableInternalAttributes {
+ a.Attr = append(a.Attr, html.Attribute{Key: "data-markdown-generated-content"})
}
-
if class != "" {
a.Attr = append(a.Attr, html.Attribute{Key: "class", Val: class})
}
diff --git a/modules/markup/html_codepreview_test.go b/modules/markup/html_codepreview_test.go
index a90de278f5..5054627dde 100644
--- a/modules/markup/html_codepreview_test.go
+++ b/modules/markup/html_codepreview_test.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/markup/markdown"
"github.com/stretchr/testify/assert"
)
@@ -23,8 +24,8 @@ func TestRenderCodePreview(t *testing.T) {
})
test := func(input, expected string) {
buffer, err := markup.RenderString(&markup.RenderContext{
- Ctx: git.DefaultContext,
- Type: "markdown",
+ Ctx: git.DefaultContext,
+ MarkupType: markdown.MarkupName,
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go
index 8f516751b0..2fb657f56b 100644
--- a/modules/markup/html_internal_test.go
+++ b/modules/markup/html_internal_test.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
+ testModule "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
@@ -123,8 +124,9 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
}
expectedNil := fmt.Sprintf(expectedFmt, links...)
testRenderIssueIndexPattern(t, s, expectedNil, &RenderContext{
- Ctx: git.DefaultContext,
- Metas: localMetas,
+ Ctx: git.DefaultContext,
+ Metas: localMetas,
+ ContentMode: RenderContentAsComment,
})
class := "ref-issue"
@@ -137,8 +139,9 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
}
expectedNum := fmt.Sprintf(expectedFmt, links...)
testRenderIssueIndexPattern(t, s, expectedNum, &RenderContext{
- Ctx: git.DefaultContext,
- Metas: numericMetas,
+ Ctx: git.DefaultContext,
+ Metas: numericMetas,
+ ContentMode: RenderContentAsComment,
})
}
@@ -266,7 +269,6 @@ func TestRender_IssueIndexPattern_Document(t *testing.T) {
"user": "someUser",
"repo": "someRepo",
"style": IssueNameStyleNumeric,
- "mode": "document",
}
testRenderIssueIndexPattern(t, "#1", "#1", &RenderContext{
@@ -316,8 +318,8 @@ func TestRender_AutoLink(t *testing.T) {
Links: Links{
Base: TestRepoURL,
},
- Metas: localMetas,
- IsWiki: true,
+ Metas: localMetas,
+ ContentMode: RenderContentAsWiki,
}, strings.NewReader(input), &buffer)
assert.Equal(t, err, nil)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
@@ -340,7 +342,7 @@ func TestRender_AutoLink(t *testing.T) {
func TestRender_FullIssueURLs(t *testing.T) {
setting.AppURL = TestAppURL
-
+ defer testModule.MockVariableValue(&RenderBehaviorForTesting.DisableInternalAttributes, true)()
test := func(input, expected string) {
var result strings.Builder
err := postProcess(&RenderContext{
@@ -351,9 +353,7 @@ func TestRender_FullIssueURLs(t *testing.T) {
Metas: localMetas,
}, []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result)
assert.NoError(t, err)
- actual := result.String()
- actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "")
- assert.Equal(t, expected, actual)
+ assert.Equal(t, expected, result.String())
}
test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6",
"Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6")
diff --git a/modules/markup/html_issue.go b/modules/markup/html_issue.go
index b6d4ed6a8e..fa630656ce 100644
--- a/modules/markup/html_issue.go
+++ b/modules/markup/html_issue.go
@@ -67,9 +67,8 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
return
}
- // FIXME: the use of "mode" is quite dirty and hacky, for example: what is a "document"? how should it be rendered?
- // The "mode" approach should be refactored to some other more clear&reliable way.
- crossLinkOnly := ctx.Metas["mode"] == "document" && !ctx.IsWiki
+ // crossLinkOnly if not comment and not wiki
+ crossLinkOnly := ctx.ContentMode != RenderContentAsTitle && ctx.ContentMode != RenderContentAsComment && ctx.ContentMode != RenderContentAsWiki
var (
found bool
diff --git a/modules/markup/html_link.go b/modules/markup/html_link.go
index 9350634568..30564da548 100644
--- a/modules/markup/html_link.go
+++ b/modules/markup/html_link.go
@@ -20,7 +20,7 @@ func ResolveLink(ctx *RenderContext, link, userContentAnchorPrefix string) (resu
isAnchorFragment := link != "" && link[0] == '#'
if !isAnchorFragment && !IsFullURLString(link) {
linkBase := ctx.Links.Base
- if ctx.IsWiki {
+ if ctx.ContentMode == RenderContentAsWiki {
// no need to check if the link should be resolved as a wiki link or a wiki raw link
// just use wiki link here and it will be redirected to a wiki raw link if necessary
linkBase = ctx.Links.WikiLink()
@@ -147,7 +147,7 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
}
if image {
if !absoluteLink {
- link = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), link)
+ link = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.ContentMode == RenderContentAsWiki), link)
}
title := props["title"]
if title == "" {
diff --git a/modules/markup/html_node.go b/modules/markup/html_node.go
index 6d784975b9..c499854053 100644
--- a/modules/markup/html_node.go
+++ b/modules/markup/html_node.go
@@ -17,7 +17,7 @@ func visitNodeImg(ctx *RenderContext, img *html.Node) (next *html.Node) {
}
if IsNonEmptyRelativePath(attr.Val) {
- attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
+ attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.ContentMode == RenderContentAsWiki), attr.Val)
// By default, the "" tag should also be clickable,
// because frontend use `` to paste the re-scaled image into the markdown,
@@ -53,7 +53,7 @@ func visitNodeVideo(ctx *RenderContext, node *html.Node) (next *html.Node) {
continue
}
if IsNonEmptyRelativePath(attr.Val) {
- attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
+ attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.ContentMode == RenderContentAsWiki), attr.Val)
}
attr.Val = camoHandleLink(attr.Val)
node.Attr[i] = attr
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index 82aded4407..262d0fc4dd 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
+ testModule "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
@@ -104,7 +105,7 @@ func TestRender_Commits(t *testing.T) {
func TestRender_CrossReferences(t *testing.T) {
setting.AppURL = markup.TestAppURL
-
+ defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
test := func(input, expected string) {
buffer, err := markup.RenderString(&markup.RenderContext{
Ctx: git.DefaultContext,
@@ -116,9 +117,7 @@ func TestRender_CrossReferences(t *testing.T) {
Metas: localMetas,
}, input)
assert.NoError(t, err)
- actual := strings.TrimSpace(buffer)
- actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "")
- assert.Equal(t, strings.TrimSpace(expected), actual)
+ assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
test(
@@ -148,7 +147,7 @@ func TestRender_CrossReferences(t *testing.T) {
func TestRender_links(t *testing.T) {
setting.AppURL = markup.TestAppURL
-
+ defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
test := func(input, expected string) {
buffer, err := markup.RenderString(&markup.RenderContext{
Ctx: git.DefaultContext,
@@ -158,9 +157,7 @@ func TestRender_links(t *testing.T) {
},
}, input)
assert.NoError(t, err)
- actual := strings.TrimSpace(buffer)
- actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "")
- assert.Equal(t, strings.TrimSpace(expected), actual)
+ assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
oldCustomURLSchemes := setting.Markdown.CustomURLSchemes
@@ -261,7 +258,7 @@ func TestRender_links(t *testing.T) {
func TestRender_email(t *testing.T) {
setting.AppURL = markup.TestAppURL
-
+ defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
test := func(input, expected string) {
res, err := markup.RenderString(&markup.RenderContext{
Ctx: git.DefaultContext,
@@ -271,9 +268,7 @@ func TestRender_email(t *testing.T) {
},
}, input)
assert.NoError(t, err)
- actual := strings.TrimSpace(res)
- actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "")
- assert.Equal(t, strings.TrimSpace(expected), actual)
+ assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
}
// Text that should be turned into email link
@@ -302,10 +297,10 @@ func TestRender_email(t *testing.T) {
j.doe@example.com;
j.doe@example.com?
j.doe@example.com!`,
- `j.doe@example.com,
-j.doe@example.com.
-j.doe@example.com;
-j.doe@example.com?
+ `
j.doe@example.com,
+j.doe@example.com.
+j.doe@example.com;
+j.doe@example.com?
j.doe@example.com!
`)
// Test that should *not* be turned into email links
@@ -418,8 +413,8 @@ func TestRender_ShortLinks(t *testing.T) {
Links: markup.Links{
Base: markup.TestRepoURL,
},
- Metas: localMetas,
- IsWiki: true,
+ Metas: localMetas,
+ ContentMode: markup.RenderContentAsWiki,
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer)))
@@ -531,10 +526,10 @@ func TestRender_ShortLinks(t *testing.T) {
func TestRender_RelativeMedias(t *testing.T) {
render := func(input string, isWiki bool, links markup.Links) string {
buffer, err := markdown.RenderString(&markup.RenderContext{
- Ctx: git.DefaultContext,
- Links: links,
- Metas: localMetas,
- IsWiki: isWiki,
+ Ctx: git.DefaultContext,
+ Links: links,
+ Metas: localMetas,
+ ContentMode: util.Iif(isWiki, markup.RenderContentAsWiki, markup.RenderContentAsComment),
}, input)
assert.NoError(t, err)
return strings.TrimSpace(string(buffer))
@@ -604,12 +599,7 @@ func Test_ParseClusterFuzz(t *testing.T) {
func TestPostProcess_RenderDocument(t *testing.T) {
setting.AppURL = markup.TestAppURL
setting.StaticURLPrefix = markup.TestAppURL // can't run standalone
-
- localMetas := map[string]string{
- "user": "go-gitea",
- "repo": "gitea",
- "mode": "document",
- }
+ defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
test := func(input, expected string) {
var res strings.Builder
@@ -619,12 +609,10 @@ func TestPostProcess_RenderDocument(t *testing.T) {
AbsolutePrefix: true,
Base: "https://example.com",
},
- Metas: localMetas,
+ Metas: map[string]string{"user": "go-gitea", "repo": "gitea"},
}, strings.NewReader(input), &res)
assert.NoError(t, err)
- actual := strings.TrimSpace(res.String())
- actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "")
- assert.Equal(t, strings.TrimSpace(expected), actual)
+ assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res.String()))
}
// Issue index shouldn't be post processing in a document.
diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go
index 0cd9dc5f30..c8488cfb50 100644
--- a/modules/markup/markdown/goldmark.go
+++ b/modules/markup/markdown/goldmark.go
@@ -72,7 +72,12 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
g.transformList(ctx, v, rc)
case *ast.Text:
if v.SoftLineBreak() && !v.HardLineBreak() {
- if ctx.Metas["mode"] != "document" {
+ // TODO: this was a quite unclear part, old code: `if metas["mode"] != "document" { use comment link break setting }`
+ // many places render non-comment contents with no mode=document, then these contents also use comment's hard line break setting
+ // especially in many tests.
+ if markup.RenderBehaviorForTesting.ForceHardLineBreak {
+ v.SetHardLineBreak(true)
+ } else if ctx.ContentMode == markup.RenderContentAsComment {
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInComments)
} else {
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInDocuments)
diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go
index db4e5706f6..6af0deb27b 100644
--- a/modules/markup/markdown/markdown.go
+++ b/modules/markup/markdown/markdown.go
@@ -257,9 +257,7 @@ func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Wri
// Render renders Markdown to HTML with all specific handling stuff.
func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
- if ctx.Type == "" {
- ctx.Type = MarkupName
- }
+ ctx.MarkupType = MarkupName
return markup.Render(ctx, input, output)
}
diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index ad38e7a088..315eed2e62 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/svg"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
@@ -74,7 +75,7 @@ func TestRender_StandardLinks(t *testing.T) {
Links: markup.Links{
Base: FullURL,
},
- IsWiki: true,
+ ContentMode: markup.RenderContentAsWiki,
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer)))
@@ -296,23 +297,22 @@ This PR has been generated by [Renovate Bot](https://github.com/renovatebot/reno
}
func TestTotal_RenderWiki(t *testing.T) {
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)()
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
setting.AppURL = AppURL
-
answers := testAnswers(util.URLJoin(FullURL, "wiki"), util.URLJoin(FullURL, "wiki", "raw"))
-
for i := 0; i < len(sameCases); i++ {
line, err := markdown.RenderString(&markup.RenderContext{
Ctx: git.DefaultContext,
Links: markup.Links{
Base: FullURL,
},
- Repo: newMockRepo(testRepoOwnerName, testRepoName),
- Metas: localMetas,
- IsWiki: true,
+ Repo: newMockRepo(testRepoOwnerName, testRepoName),
+ Metas: localMetas,
+ ContentMode: markup.RenderContentAsWiki,
}, sameCases[i])
assert.NoError(t, err)
- actual := strings.ReplaceAll(string(line), ` data-markdown-generated-content=""`, "")
- assert.Equal(t, answers[i], actual)
+ assert.Equal(t, answers[i], string(line))
}
testCases := []string{
@@ -334,19 +334,18 @@ func TestTotal_RenderWiki(t *testing.T) {
Links: markup.Links{
Base: FullURL,
},
- IsWiki: true,
+ ContentMode: markup.RenderContentAsWiki,
}, testCases[i])
assert.NoError(t, err)
- actual := strings.ReplaceAll(string(line), ` data-markdown-generated-content=""`, "")
- assert.EqualValues(t, testCases[i+1], actual)
+ assert.EqualValues(t, testCases[i+1], string(line))
}
}
func TestTotal_RenderString(t *testing.T) {
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)()
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
setting.AppURL = AppURL
-
answers := testAnswers(util.URLJoin(FullURL, "src", "master"), util.URLJoin(FullURL, "media", "master"))
-
for i := 0; i < len(sameCases); i++ {
line, err := markdown.RenderString(&markup.RenderContext{
Ctx: git.DefaultContext,
@@ -358,8 +357,7 @@ func TestTotal_RenderString(t *testing.T) {
Metas: localMetas,
}, sameCases[i])
assert.NoError(t, err)
- actual := strings.ReplaceAll(string(line), ` data-markdown-generated-content=""`, "")
- assert.Equal(t, answers[i], actual)
+ assert.Equal(t, answers[i], string(line))
}
testCases := []string{}
@@ -428,6 +426,7 @@ func TestRenderSiblingImages_Issue12925(t *testing.T) {
expected := `
`
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)()
res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase)
assert.NoError(t, err)
assert.Equal(t, expected, res)
@@ -996,11 +995,16 @@ space
},
}
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)()
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
for i, c := range cases {
- result, err := markdown.RenderString(&markup.RenderContext{Ctx: context.Background(), Links: c.Links, IsWiki: c.IsWiki}, input)
+ result, err := markdown.RenderString(&markup.RenderContext{
+ Ctx: context.Background(),
+ Links: c.Links,
+ ContentMode: util.Iif(c.IsWiki, markup.RenderContentAsWiki, markup.RenderContentAsDefault),
+ }, input)
assert.NoError(t, err, "Unexpected error in testcase: %v", i)
- actual := strings.ReplaceAll(string(result), ` data-markdown-generated-content=""`, "")
- assert.Equal(t, c.Expected, actual, "Unexpected result in testcase %v", i)
+ assert.Equal(t, c.Expected, string(result), "Unexpected result in testcase %v", i)
}
}
diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go
index 812e24f0a2..4ed4118854 100644
--- a/modules/markup/markdown/transform_image.go
+++ b/modules/markup/markdown/transform_image.go
@@ -21,7 +21,7 @@ func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image)
// Check if the destination is a real link
if len(v.Destination) > 0 && !markup.IsFullURLBytes(v.Destination) {
v.Destination = []byte(giteautil.URLJoin(
- ctx.Links.ResolveMediaLink(ctx.IsWiki),
+ ctx.Links.ResolveMediaLink(ctx.ContentMode == markup.RenderContentAsWiki),
strings.TrimLeft(string(v.Destination), "/"),
))
}
diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go
index 25f8d15ef4..6b9c963157 100644
--- a/modules/markup/orgmode/orgmode.go
+++ b/modules/markup/orgmode/orgmode.go
@@ -144,14 +144,15 @@ func (r *Writer) resolveLink(kind, link string) string {
}
base := r.Ctx.Links.Base
- if r.Ctx.IsWiki {
+ isWiki := r.Ctx.ContentMode == markup.RenderContentAsWiki
+ if isWiki {
base = r.Ctx.Links.WikiLink()
} else if r.Ctx.Links.HasBranchInfo() {
base = r.Ctx.Links.SrcLink()
}
if kind == "image" || kind == "video" {
- base = r.Ctx.Links.ResolveMediaLink(r.Ctx.IsWiki)
+ base = r.Ctx.Links.ResolveMediaLink(isWiki)
}
link = util.URLJoin(base, link)
diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go
index 75b60ed81f..b882678c7e 100644
--- a/modules/markup/orgmode/orgmode_test.go
+++ b/modules/markup/orgmode/orgmode_test.go
@@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@@ -26,7 +27,7 @@ func TestRender_StandardLinks(t *testing.T) {
Base: "/relative-path",
BranchPath: "branch/main",
},
- IsWiki: isWiki,
+ ContentMode: util.Iif(isWiki, markup.RenderContentAsWiki, markup.RenderContentAsDefault),
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
diff --git a/modules/markup/render.go b/modules/markup/render.go
index f2ce9229af..add50f4382 100644
--- a/modules/markup/render.go
+++ b/modules/markup/render.go
@@ -5,11 +5,9 @@ package markup
import (
"context"
- "errors"
"fmt"
"io"
"net/url"
- "path/filepath"
"strings"
"sync"
@@ -29,15 +27,44 @@ const (
RenderMetaAsTable RenderMetaMode = "table"
)
+type RenderContentMode string
+
+const (
+ RenderContentAsDefault RenderContentMode = "" // empty means "default", no special handling, maybe just a simple "document"
+ RenderContentAsComment RenderContentMode = "comment"
+ RenderContentAsTitle RenderContentMode = "title"
+ RenderContentAsWiki RenderContentMode = "wiki"
+)
+
+var RenderBehaviorForTesting struct {
+ // Markdown line break rendering has 2 default behaviors:
+ // * Use hard: replace "\n" with "
" for comments, setting.Markdown.EnableHardLineBreakInComments=true
+ // * Keep soft: "\n" for non-comments (a.k.a. documents), setting.Markdown.EnableHardLineBreakInDocuments=false
+ // In history, there was a mess:
+ // * The behavior was controlled by `Metas["mode"] != "document",
+ // * However, many places render the content without setting "mode" in Metas, all these places used comment line break setting incorrectly
+ ForceHardLineBreak bool
+
+ // Gitea will emit some internal attributes for various purposes, these attributes don't affect rendering.
+ // But there are too many hard-coded test cases, to avoid changing all of them again and again, we can disable emitting these internal attributes.
+ DisableInternalAttributes bool
+}
+
// RenderContext represents a render context
type RenderContext struct {
- Ctx context.Context
- RelativePath string // relative path from tree root of the branch
- Type string
- IsWiki bool
- Links Links
- Metas map[string]string // user, repo, mode(comment/document)
- DefaultLink string
+ Ctx context.Context
+ RelativePath string // relative path from tree root of the branch
+
+ // eg: "orgmode", "asciicast", "console"
+ // for file mode, it could be left as empty, and will be detected by file extension in RelativePath
+ MarkupType string
+
+ // what the content will be used for: eg: for comment or for wiki? or just render a file?
+ ContentMode RenderContentMode
+
+ Links Links // special link references for rendering, especially when there is a branch/tree path
+ Metas map[string]string // user&repo, format&style®exp (for external issue pattern), teams&org (for mention), BranchNameSubURL(for iframe&asciicast)
+ DefaultLink string // TODO: need to figure out
GitRepo *git.Repository
Repo gitrepo.Repository
ShaExistCache map[string]bool
@@ -77,12 +104,29 @@ func (ctx *RenderContext) AddCancel(fn func()) {
// Render renders markup file to HTML with all specific handling stuff.
func Render(ctx *RenderContext, input io.Reader, output io.Writer) error {
- if ctx.Type != "" {
- return renderByType(ctx, input, output)
- } else if ctx.RelativePath != "" {
- return renderFile(ctx, input, output)
+ if ctx.MarkupType == "" && ctx.RelativePath != "" {
+ ctx.MarkupType = DetectMarkupTypeByFileName(ctx.RelativePath)
+ if ctx.MarkupType == "" {
+ return util.NewInvalidArgumentErrorf("unsupported file to render: %q", ctx.RelativePath)
+ }
}
- return errors.New("render options both filename and type missing")
+
+ renderer := renderers[ctx.MarkupType]
+ if renderer == nil {
+ return util.NewInvalidArgumentErrorf("unsupported markup type: %q", ctx.MarkupType)
+ }
+
+ if ctx.RelativePath != "" {
+ if externalRender, ok := renderer.(ExternalRenderer); ok && externalRender.DisplayInIFrame() {
+ if !ctx.InStandalonePage {
+ // for an external "DisplayInIFrame" render, it could only output its content in a standalone page
+ // otherwise, a