Skip to content

Commit 32d9c47

Browse files
Add RTL rendering support to Markdown (#24816)
Support RTL content in Markdown: ![image](https://github.com/go-gitea/gitea/assets/115237/dedb1b0c-2f05-40dc-931a-0d9dc81f7c97) Example document: https://try.gitea.io/silverwind/symlink-test/src/branch/master/bidi-text.md Same on GitHub: https://github.com/silverwind/symlink-test/blob/master/bidi-text.md `dir=auto` enables a browser heuristic that sets the text direction automatically. It is the only way to get automatic text direction. Ref: https://codeberg.org/Codeberg/Community/issues/1021 --------- Co-authored-by: wxiaoguang <[email protected]>
1 parent 1698c15 commit 32d9c47

File tree

7 files changed

+23
-5
lines changed

7 files changed

+23
-5
lines changed

modules/markup/html.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
630630
}
631631
mentionedUsername := mention[1:]
632632

633-
if processorHelper.IsUsernameMentionable != nil && processorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
633+
if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
634634
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mentionedUsername), mention, "mention"))
635635
node = node.NextSibling.NextSibling
636636
} else {

modules/markup/markdown/goldmark.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
4747
tocMode = rc.TOC
4848
}
4949

50+
applyElementDir := func(n ast.Node) {
51+
if markup.DefaultProcessorHelper.ElementDir != "" {
52+
n.SetAttributeString("dir", []byte(markup.DefaultProcessorHelper.ElementDir))
53+
}
54+
}
55+
5056
attentionMarkedBlockquotes := make(container.Set[*ast.Blockquote])
5157
_ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
5258
if !entering {
@@ -69,6 +75,9 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
6975
header.ID = util.BytesToReadOnlyString(id.([]byte))
7076
}
7177
tocList = append(tocList, header)
78+
applyElementDir(v)
79+
case *ast.Paragraph:
80+
applyElementDir(v)
7281
case *ast.Image:
7382
// Images need two things:
7483
//
@@ -171,6 +180,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
171180
v.AppendChild(v, newChild)
172181
}
173182
}
183+
applyElementDir(v)
174184
case *ast.Text:
175185
if v.SoftLineBreak() && !v.HardLineBreak() {
176186
renderMetas := pc.Get(renderMetasKey).(map[string]string)

modules/markup/renderer.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@ const (
3030

3131
type ProcessorHelper struct {
3232
IsUsernameMentionable func(ctx context.Context, username string) bool
33+
34+
ElementDir string // the direction of the elements, eg: "ltr", "rtl", "auto", default to no direction attribute
3335
}
3436

35-
var processorHelper ProcessorHelper
37+
var DefaultProcessorHelper ProcessorHelper
3638

3739
// Init initialize regexps for markdown parsing
3840
func Init(ph *ProcessorHelper) {
3941
if ph != nil {
40-
processorHelper = *ph
42+
DefaultProcessorHelper = *ph
4143
}
4244

4345
NewSanitizer()

services/markup/processorhelper.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
func ProcessorHelper() *markup.ProcessorHelper {
1515
return &markup.ProcessorHelper{
16+
ElementDir: "auto", // set dir="auto" for necessary (eg: <p>, <h?>, etc) tags
1617
IsUsernameMentionable: func(ctx context.Context, username string) bool {
1718
mentionedUser, err := user.GetUserByName(ctx, username)
1819
if err != nil {

tests/integration/user_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ func TestGetUserRss(t *testing.T) {
250250
title, _ := rssDoc.ChildrenFiltered("title").Html()
251251
assert.EqualValues(t, "Feed of &#34;the_1-user.with.all.allowedChars&#34;", title)
252252
description, _ := rssDoc.ChildrenFiltered("description").Html()
253-
assert.EqualValues(t, "&lt;p&gt;some &lt;a href=&#34;https://commonmark.org/&#34; rel=&#34;nofollow&#34;&gt;commonmark&lt;/a&gt;!&lt;/p&gt;\n", description)
253+
assert.EqualValues(t, "&lt;p dir=&#34;auto&#34;&gt;some &lt;a href=&#34;https://commonmark.org/&#34; rel=&#34;nofollow&#34;&gt;commonmark&lt;/a&gt;!&lt;/p&gt;\n", description)
254254
}
255255
}
256256

web_src/css/base.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,7 @@ a.label,
10911091
color: var(--color-text);
10921092
background: var(--color-box-body);
10931093
border-color: var(--color-secondary);
1094+
text-align: start; /* Override fomantic's `text-align: left` to make RTL work via HTML `dir="auto"` */
10941095
}
10951096

10961097
.ui.table th,

web_src/css/markup/content.css

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
}
2424

2525
.markup .anchor {
26+
float: left;
2627
padding-right: 4px;
2728
margin-left: -20px;
28-
line-height: 1;
2929
color: inherit;
3030
}
3131

@@ -37,6 +37,10 @@
3737
outline: none;
3838
}
3939

40+
.markup h1 .anchor {
41+
margin-top: -2px; /* re-align to center */
42+
}
43+
4044
.markup h1 .anchor .svg,
4145
.markup h2 .anchor .svg,
4246
.markup h3 .anchor .svg,

0 commit comments

Comments
 (0)