Fix markdown preview $$ support (#31514)

close #31481

currently `$$A + B$$ test` will ignore text after $$ block

test text

![圖片](https://github.com/go-gitea/gitea/assets/30816317/39b2974b-c0b6-48a0-87d0-5f4a13615eed)

before fix

![圖片](https://github.com/go-gitea/gitea/assets/30816317/15469e4c-474d-4128-b46f-d6cadaafbd68)

after fix

![圖片](https://github.com/go-gitea/gitea/assets/30816317/c1025eef-177f-4ade-988f-510e7039f3f9)

github display

![圖片](https://github.com/go-gitea/gitea/assets/30816317/97cd1e10-ac94-4899-86d8-8e359ef1d694)

(cherry picked from commit f0033051d5ce07f5a18d2aacef6bfdca5fc69014)
This commit is contained in:
charles 2024-06-30 07:23:47 +08:00 committed by Earl Warren
parent e1b90212e4
commit f92591b825
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
6 changed files with 79 additions and 6 deletions

View file

@ -552,6 +552,14 @@ func TestMathBlock(t *testing.T) {
"$a$ ($b$) [$c$] {$d$}", "$a$ ($b$) [$c$] {$d$}",
`<p><code class="language-math is-loading">a</code> (<code class="language-math is-loading">b</code>) [$c$] {$d$}</p>` + nl, `<p><code class="language-math is-loading">a</code> (<code class="language-math is-loading">b</code>) [$c$] {$d$}</p>` + nl,
}, },
{
"$$a$$ test",
`<p><code class="language-math display is-loading">a</code> test</p>` + nl,
},
{
"test $$a$$",
`<p>test <code class="language-math display is-loading">a</code></p>` + nl,
},
} }
for _, test := range testcases { for _, test := range testcases {

View file

@ -47,6 +47,12 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex
} }
idx := bytes.Index(line[pos+2:], endBytes) idx := bytes.Index(line[pos+2:], endBytes)
if idx >= 0 { if idx >= 0 {
// for case $$ ... $$ any other text
for i := pos + idx + 4; i < len(line); i++ {
if line[i] != ' ' && line[i] != '\n' {
return nil, parser.NoChildren
}
}
segment.Stop = segment.Start + idx + 2 segment.Stop = segment.Start + idx + 2
reader.Advance(segment.Len() - 1) reader.Advance(segment.Len() - 1)
segment.Start += 2 segment.Start += 2

View file

@ -0,0 +1,31 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package math
import (
"github.com/yuin/goldmark/ast"
)
// InlineBlock represents inline math e.g. $$...$$
type InlineBlock struct {
Inline
}
// InlineBlock implements InlineBlock.
func (n *InlineBlock) InlineBlock() {}
// KindInlineBlock is the kind for math inline block
var KindInlineBlock = ast.NewNodeKind("MathInlineBlock")
// Kind returns KindInlineBlock
func (n *InlineBlock) Kind() ast.NodeKind {
return KindInlineBlock
}
// NewInlineBlock creates a new ast math inline block node
func NewInlineBlock() *InlineBlock {
return &InlineBlock{
Inline{},
}
}

View file

@ -21,11 +21,20 @@ var defaultInlineDollarParser = &inlineParser{
end: []byte{'$'}, end: []byte{'$'},
} }
var defaultDualDollarParser = &inlineParser{
start: []byte{'$', '$'},
end: []byte{'$', '$'},
}
// NewInlineDollarParser returns a new inline parser // NewInlineDollarParser returns a new inline parser
func NewInlineDollarParser() parser.InlineParser { func NewInlineDollarParser() parser.InlineParser {
return defaultInlineDollarParser return defaultInlineDollarParser
} }
func NewInlineDualDollarParser() parser.InlineParser {
return defaultDualDollarParser
}
var defaultInlineBracketParser = &inlineParser{ var defaultInlineBracketParser = &inlineParser{
start: []byte{'\\', '('}, start: []byte{'\\', '('},
end: []byte{'\\', ')'}, end: []byte{'\\', ')'},
@ -38,7 +47,7 @@ func NewInlineBracketParser() parser.InlineParser {
// Trigger triggers this parser on $ or \ // Trigger triggers this parser on $ or \
func (parser *inlineParser) Trigger() []byte { func (parser *inlineParser) Trigger() []byte {
return parser.start[0:1] return parser.start
} }
func isPunctuation(b byte) bool { func isPunctuation(b byte) bool {
@ -88,7 +97,11 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
break break
} }
suceedingCharacter := line[pos] suceedingCharacter := line[pos]
if !isPunctuation(suceedingCharacter) && !(suceedingCharacter == ' ') && !isBracket(suceedingCharacter) { // check valid ending character
if !isPunctuation(suceedingCharacter) &&
!(suceedingCharacter == ' ') &&
!(suceedingCharacter == '\n') &&
!isBracket(suceedingCharacter) {
return nil return nil
} }
if line[ender-1] != '\\' { if line[ender-1] != '\\' {
@ -101,12 +114,21 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
block.Advance(opener) block.Advance(opener)
_, pos := block.Position() _, pos := block.Position()
node := NewInline() var node ast.Node
if parser == defaultDualDollarParser {
node = NewInlineBlock()
} else {
node = NewInline()
}
segment := pos.WithStop(pos.Start + ender - opener) segment := pos.WithStop(pos.Start + ender - opener)
node.AppendChild(node, ast.NewRawTextSegment(segment)) node.AppendChild(node, ast.NewRawTextSegment(segment))
block.Advance(ender - opener + len(parser.end)) block.Advance(ender - opener + len(parser.end))
trimBlock(node, block) if parser == defaultDualDollarParser {
trimBlock(&(node.(*InlineBlock)).Inline, block)
} else {
trimBlock(node.(*Inline), block)
}
return node return node
} }

View file

@ -21,7 +21,11 @@ func NewInlineRenderer() renderer.NodeRenderer {
func (r *InlineRenderer) renderInline(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { func (r *InlineRenderer) renderInline(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering { if entering {
_, _ = w.WriteString(`<code class="language-math is-loading">`) extraClass := ""
if _, ok := n.(*InlineBlock); ok {
extraClass = "display "
}
_, _ = w.WriteString(`<code class="language-math ` + extraClass + `is-loading">`)
for c := n.FirstChild(); c != nil; c = c.NextSibling() { for c := n.FirstChild(); c != nil; c = c.NextSibling() {
segment := c.(*ast.Text).Segment segment := c.(*ast.Text).Segment
value := util.EscapeHTML(segment.Value(source)) value := util.EscapeHTML(segment.Value(source))
@ -43,4 +47,5 @@ func (r *InlineRenderer) renderInline(w util.BufWriter, source []byte, n ast.Nod
// RegisterFuncs registers the renderer for inline math nodes // RegisterFuncs registers the renderer for inline math nodes
func (r *InlineRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { func (r *InlineRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(KindInline, r.renderInline) reg.Register(KindInline, r.renderInline)
reg.Register(KindInlineBlock, r.renderInline)
} }

View file

@ -96,7 +96,8 @@ func (e *Extension) Extend(m goldmark.Markdown) {
util.Prioritized(NewInlineBracketParser(), 501), util.Prioritized(NewInlineBracketParser(), 501),
} }
if e.parseDollarInline { if e.parseDollarInline {
inlines = append(inlines, util.Prioritized(NewInlineDollarParser(), 501)) inlines = append(inlines, util.Prioritized(NewInlineDollarParser(), 503),
util.Prioritized(NewInlineDualDollarParser(), 502))
} }
m.Parser().AddOptions(parser.WithInlineParsers(inlines...)) m.Parser().AddOptions(parser.WithInlineParsers(inlines...))