Skip to content

Commit c566997

Browse files
committed
fix
1 parent 5bc030e commit c566997

File tree

9 files changed

+135
-148
lines changed

9 files changed

+135
-148
lines changed

Diff for: modules/markup/markdown/markdown.go

+15-13
Original file line numberDiff line numberDiff line change
@@ -78,26 +78,23 @@ func (r *GlodmarkRender) Renderer() renderer.Renderer {
7878

7979
func (r *GlodmarkRender) highlightingRenderer(w util.BufWriter, c highlighting.CodeBlockContext, entering bool) {
8080
if entering {
81-
language, _ := c.Language()
82-
if language == nil {
83-
language = []byte("text")
84-
}
85-
86-
languageStr := string(language)
81+
languageBytes, _ := c.Language()
82+
languageStr := giteautil.IfZero(string(languageBytes), "text")
8783

88-
preClasses := []string{"code-block"}
84+
preClasses := "code-block"
8985
if languageStr == "mermaid" || languageStr == "math" {
90-
preClasses = append(preClasses, "is-loading")
86+
preClasses += " is-loading"
9187
}
9288

93-
err := r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<pre class="%s">`, strings.Join(preClasses, " "))
89+
err := r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<pre class="%s">`, preClasses)
9490
if err != nil {
9591
return
9692
}
9793

98-
// include language-x class as part of commonmark spec
99-
// the "display" class is used by "js/markup/math.js" to render the code element as a block
100-
err = r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<code class="chroma language-%s display">`, string(language))
94+
// include language-x class as part of commonmark spec, "chroma" class is used to highlight the code
95+
// the "display" class is used by "js/markup/math.ts" to render the code element as a block
96+
// the "math.ts" strictly depends on the structure: <pre class="code-block is-loading"><code class="language-math display">...</code></pre>
97+
err = r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<code class="chroma language-%s display">`, languageStr)
10198
if err != nil {
10299
return
103100
}
@@ -128,7 +125,12 @@ func SpecializedMarkdown(ctx *markup.RenderContext) *GlodmarkRender {
128125
),
129126
highlighting.WithWrapperRenderer(r.highlightingRenderer),
130127
),
131-
math.NewExtension(&ctx.RenderInternal, math.Enabled(setting.Markdown.EnableMath)),
128+
math.NewExtension(&ctx.RenderInternal, math.Options{
129+
Enabled: setting.Markdown.EnableMath,
130+
ParseDollarInline: true,
131+
ParseDollarBlock: true,
132+
ParseSquareBlock: true, // TODO: this is a bad syntax, it should be deprecated in the future (by some config options)
133+
}),
132134
meta.Meta,
133135
),
134136
goldmark.WithParserOptions(

Diff for: modules/markup/markdown/markdown_math_test.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import (
1212
"github.com/stretchr/testify/assert"
1313
)
1414

15+
const nl = "\n"
16+
1517
func TestMathRender(t *testing.T) {
16-
const nl = "\n"
1718
testcases := []struct {
1819
testcase string
1920
expected string
@@ -86,6 +87,18 @@ func TestMathRender(t *testing.T) {
8687
`$\text{$b$}$`,
8788
`<p><code class="language-math">\text{$b$}</code></p>` + nl,
8889
},
90+
{
91+
"a$`b`$c",
92+
`<p>a<code class="language-math">b</code>c</p>` + nl,
93+
},
94+
{
95+
"a $`b`$ c",
96+
`<p>a <code class="language-math">b</code> c</p>` + nl,
97+
},
98+
{
99+
"a$``b``$c x$```y```$z",
100+
`<p>a<code class="language-math">b</code>c x<code class="language-math">y</code>z</p>` + nl,
101+
},
89102
}
90103

91104
for _, test := range testcases {
@@ -215,6 +228,11 @@ x
215228
</ol>
216229
`,
217230
},
231+
{
232+
"inline-non-math",
233+
`\[x]`,
234+
`<p>[x]</p>` + nl,
235+
},
218236
}
219237

220238
for _, test := range testcases {

Diff for: modules/markup/markdown/math/block_parser.go

+16-7
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@ import (
1616

1717
type blockParser struct {
1818
parseDollars bool
19+
parseSquare bool
1920
endBytesDollars []byte
20-
endBytesBracket []byte
21+
endBytesSquare []byte
2122
}
2223

2324
// NewBlockParser creates a new math BlockParser
24-
func NewBlockParser(parseDollarBlocks bool) parser.BlockParser {
25+
func NewBlockParser(parseDollars, parseSquare bool) parser.BlockParser {
2526
return &blockParser{
26-
parseDollars: parseDollarBlocks,
27+
parseDollars: parseDollars,
28+
parseSquare: parseSquare,
2729
endBytesDollars: []byte{'$', '$'},
28-
endBytesBracket: []byte{'\\', ']'},
30+
endBytesSquare: []byte{'\\', ']'},
2931
}
3032
}
3133

@@ -53,10 +55,10 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex
5355
node := NewBlock(dollars, pos)
5456

5557
// Now we need to check if the ending block is on the segment...
56-
endBytes := giteaUtil.Iif(dollars, b.endBytesDollars, b.endBytesBracket)
58+
endBytes := giteaUtil.Iif(dollars, b.endBytesDollars, b.endBytesSquare)
5759
idx := bytes.Index(line[pos+2:], endBytes)
5860
if idx >= 0 {
59-
// for case $$ ... $$ any other text
61+
// for case: "$$ ... $$ any other text" (this case will be handled by the inline parser)
6062
for i := pos + 2 + idx + 2; i < len(line); i++ {
6163
if line[i] != ' ' && line[i] != '\n' {
6264
return nil, parser.NoChildren
@@ -70,6 +72,13 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex
7072
return node, parser.Close | parser.NoChildren
7173
}
7274

75+
// for case "\[ ... ]" (no close marker on the same line)
76+
for i := pos + 2 + idx + 2; i < len(line); i++ {
77+
if line[i] != ' ' && line[i] != '\n' {
78+
return nil, parser.NoChildren
79+
}
80+
}
81+
7382
segment.Start += pos + 2
7483
node.Lines().Append(segment)
7584
return node, parser.NoChildren
@@ -85,7 +94,7 @@ func (b *blockParser) Continue(node ast.Node, reader text.Reader, pc parser.Cont
8594
line, segment := reader.PeekLine()
8695
w, pos := util.IndentWidth(line, reader.LineOffset())
8796
if w < 4 {
88-
endBytes := giteaUtil.Iif(block.Dollars, b.endBytesDollars, b.endBytesBracket)
97+
endBytes := giteaUtil.Iif(block.Dollars, b.endBytesDollars, b.endBytesSquare)
8998
if bytes.HasPrefix(line[pos:], endBytes) && util.IsBlank(line[pos+len(endBytes):]) {
9099
if util.IsBlank(line[pos+len(endBytes):]) {
91100
newline := giteaUtil.Iif(line[len(line)-1] != '\n', 0, 1)

Diff for: modules/markup/markdown/math/inline_block_node.go

-31
This file was deleted.

Diff for: modules/markup/markdown/math/inline_node.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"github.com/yuin/goldmark/util"
99
)
1010

11-
// Inline represents inline math e.g. $...$ or \(...\)
11+
// Inline struct represents inline math e.g. $...$ or \(...\)
1212
type Inline struct {
1313
ast.BaseInline
1414
}

Diff for: modules/markup/markdown/math/inline_parser.go

+51-39
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,25 @@ import (
1212
)
1313

1414
type inlineParser struct {
15-
start []byte
16-
end []byte
15+
trigger []byte
16+
endBytesSingleDollar []byte
17+
endBytesDoubleDollar []byte
18+
endBytesBracket []byte
1719
}
1820

1921
var defaultInlineDollarParser = &inlineParser{
20-
start: []byte{'$'},
21-
end: []byte{'$'},
22-
}
23-
24-
var defaultDualDollarParser = &inlineParser{
25-
start: []byte{'$', '$'},
26-
end: []byte{'$', '$'},
22+
trigger: []byte{'$'},
23+
endBytesSingleDollar: []byte{'$'},
24+
endBytesDoubleDollar: []byte{'$', '$'},
2725
}
2826

2927
func NewInlineDollarParser() parser.InlineParser {
3028
return defaultInlineDollarParser
3129
}
3230

33-
func NewInlineDualDollarParser() parser.InlineParser {
34-
return defaultDualDollarParser
35-
}
36-
3731
var defaultInlineBracketParser = &inlineParser{
38-
start: []byte{'\\', '('},
39-
end: []byte{'\\', ')'},
32+
trigger: []byte{'\\', '('},
33+
endBytesBracket: []byte{'\\', ')'},
4034
}
4135

4236
func NewInlineBracketParser() parser.InlineParser {
@@ -45,7 +39,7 @@ func NewInlineBracketParser() parser.InlineParser {
4539

4640
// Trigger triggers this parser on $ or \
4741
func (parser *inlineParser) Trigger() []byte {
48-
return parser.start
42+
return parser.trigger
4943
}
5044

5145
func isPunctuation(b byte) bool {
@@ -64,33 +58,60 @@ func isAlphanumeric(b byte) bool {
6458
func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
6559
line, _ := block.PeekLine()
6660

67-
if !bytes.HasPrefix(line, parser.start) {
61+
if !bytes.HasPrefix(line, parser.trigger) {
6862
// We'll catch this one on the next time round
6963
return nil
7064
}
7165

72-
precedingCharacter := block.PrecendingCharacter()
73-
if precedingCharacter < 256 && (isAlphanumeric(byte(precedingCharacter)) || isPunctuation(byte(precedingCharacter))) {
74-
// need to exclude things like `a$` from being considered a start
75-
return nil
66+
var startMarkLen int
67+
var stopMark []byte
68+
checkSurrounding := true
69+
if line[0] == '$' {
70+
startMarkLen = 1
71+
stopMark = parser.endBytesSingleDollar
72+
if len(line) > 1 {
73+
if line[1] == '$' {
74+
startMarkLen = 2
75+
stopMark = parser.endBytesDoubleDollar
76+
} else if line[1] == '`' {
77+
pos := 1
78+
for ; pos < len(line) && line[pos] == '`'; pos++ {
79+
}
80+
startMarkLen = pos
81+
stopMark = bytes.Repeat([]byte{'`'}, pos)
82+
stopMark[len(stopMark)-1] = '$'
83+
checkSurrounding = false
84+
}
85+
}
86+
} else {
87+
startMarkLen = 2
88+
stopMark = parser.endBytesBracket
89+
}
90+
91+
if checkSurrounding {
92+
precedingCharacter := block.PrecendingCharacter()
93+
if precedingCharacter < 256 && (isAlphanumeric(byte(precedingCharacter)) || isPunctuation(byte(precedingCharacter))) {
94+
// need to exclude things like `a$` from being considered a start
95+
return nil
96+
}
7697
}
7798

7899
// move the opener marker point at the start of the text
79-
opener := len(parser.start)
100+
opener := startMarkLen
80101

81102
// Now look for an ending line
82103
depth := 0
83104
ender := -1
84105
for i := opener; i < len(line); i++ {
85-
if depth == 0 && bytes.HasPrefix(line[i:], parser.end) {
106+
if depth == 0 && bytes.HasPrefix(line[i:], stopMark) {
86107
succeedingCharacter := byte(0)
87-
if i+len(parser.end) < len(line) {
88-
succeedingCharacter = line[i+len(parser.end)]
108+
if i+len(stopMark) < len(line) {
109+
succeedingCharacter = line[i+len(stopMark)]
89110
}
90111
// check valid ending character
91112
isValidEndingChar := isPunctuation(succeedingCharacter) || isBracket(succeedingCharacter) ||
92113
succeedingCharacter == ' ' || succeedingCharacter == '\n' || succeedingCharacter == 0
93-
if !isValidEndingChar {
114+
if checkSurrounding && !isValidEndingChar {
94115
break
95116
}
96117
ender = i
@@ -112,21 +133,12 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
112133

113134
block.Advance(opener)
114135
_, pos := block.Position()
115-
var node ast.Node
116-
if parser == defaultDualDollarParser {
117-
node = NewInlineBlock()
118-
} else {
119-
node = NewInline()
120-
}
136+
node := NewInline()
137+
121138
segment := pos.WithStop(pos.Start + ender - opener)
122139
node.AppendChild(node, ast.NewRawTextSegment(segment))
123-
block.Advance(ender - opener + len(parser.end))
124-
125-
if parser == defaultDualDollarParser {
126-
trimBlock(&(node.(*InlineBlock)).Inline, block)
127-
} else {
128-
trimBlock(node.(*Inline), block)
129-
}
140+
block.Advance(ender - opener + len(stopMark))
141+
trimBlock(node, block)
130142
return node
131143
}
132144

Diff for: modules/markup/markdown/math/inline_renderer.go

-1
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,4 @@ func (r *InlineRenderer) renderInline(w util.BufWriter, source []byte, n ast.Nod
5050
// RegisterFuncs registers the renderer for inline math nodes
5151
func (r *InlineRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
5252
reg.Register(KindInline, r.renderInline)
53-
reg.Register(KindInlineBlock, r.renderInline)
5453
}

0 commit comments

Comments
 (0)