From 3c4a06273f776df340459c3775d90eb8d20b71e5 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 6 Dec 2024 20:00:24 +0800 Subject: [PATCH] Refactor markdown render (#32736) and add some tests --- modules/markup/markdown/markdown_math_test.go | 78 +++++++++++++++++-- modules/markup/markdown/math/block_node.go | 1 + modules/markup/markdown/math/block_parser.go | 40 +++++----- .../markup/markdown/math/block_renderer.go | 6 +- modules/markup/markdown/math/inline_parser.go | 8 +- 5 files changed, 101 insertions(+), 32 deletions(-) diff --git a/modules/markup/markdown/markdown_math_test.go b/modules/markup/markdown/markdown_math_test.go index 0e5adeeac8..e371b1c74a 100644 --- a/modules/markup/markdown/markdown_math_test.go +++ b/modules/markup/markdown/markdown_math_test.go @@ -68,7 +68,7 @@ func TestMathRender(t *testing.T) { }, { "$$a$$", - `
a
` + nl, + `a` + nl, }, { "$$a$$ test", @@ -79,9 +79,13 @@ func TestMathRender(t *testing.T) { `

test a

` + nl, }, { - "foo $x=\\$$ bar", + `foo $x=\$$ bar`, `

foo x=\$ bar

` + nl, }, + { + `$\text{$b$}$`, + `

\text{$b$}

` + nl, + }, } for _, test := range testcases { @@ -124,14 +128,36 @@ func TestMathRenderBlockIndent(t *testing.T) { `, }, { - "indent-2", + "indent-2-mismatch", ` \[ - \alpha +a + b + c + d \] `, `

-\alpha
+a
+b
+c
+ d
+
+`, + }, + { + "indent-2", + ` + \[ + a + b + c + \] +`, + `

+a
+ b
+c
 
`, }, @@ -139,7 +165,7 @@ func TestMathRenderBlockIndent(t *testing.T) { "indent-0-oneline", `$$ x $$ foo`, - `
 x 
+ ` x

foo

`, }, @@ -147,8 +173,46 @@ foo`, "indent-3-oneline", ` $$ x $$ foo`, - `
 x 
+ ` x

foo

+`, + }, + { + "quote-block", + ` +> \[ +> a +> \] +> \[ +> b +> \] +`, + `
+

+a
+
+

+b
+
+
+`, + }, + { + "list-block", + ` +1. a + \[ + x + \] +2. b`, + `
    +
  1. a +
    
    +x
    +
    +
  2. +
  3. b
  4. +
`, }, } diff --git a/modules/markup/markdown/math/block_node.go b/modules/markup/markdown/math/block_node.go index 10d17ff8d3..d2293133cd 100644 --- a/modules/markup/markdown/math/block_node.go +++ b/modules/markup/markdown/math/block_node.go @@ -11,6 +11,7 @@ type Block struct { Dollars bool Indent int Closed bool + Inline bool } // KindBlock is the node kind for math blocks diff --git a/modules/markup/markdown/math/block_parser.go b/modules/markup/markdown/math/block_parser.go index f31cfb09ad..3f37ce8333 100644 --- a/modules/markup/markdown/math/block_parser.go +++ b/modules/markup/markdown/math/block_parser.go @@ -6,6 +6,8 @@ package math import ( "bytes" + giteaUtil "code.gitea.io/gitea/modules/util" + "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/text" @@ -13,13 +15,17 @@ import ( ) type blockParser struct { - parseDollars bool + parseDollars bool + endBytesDollars []byte + endBytesBracket []byte } // NewBlockParser creates a new math BlockParser func NewBlockParser(parseDollarBlocks bool) parser.BlockParser { return &blockParser{ - parseDollars: parseDollarBlocks, + parseDollars: parseDollarBlocks, + endBytesDollars: []byte{'$', '$'}, + endBytesBracket: []byte{'\\', ']'}, } } @@ -47,10 +53,7 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex node := NewBlock(dollars, pos) // Now we need to check if the ending block is on the segment... - endBytes := []byte{'\\', ']'} - if dollars { - endBytes = []byte{'$', '$'} - } + endBytes := giteaUtil.Iif(dollars, b.endBytesDollars, b.endBytesBracket) idx := bytes.Index(line[pos+2:], endBytes) if idx >= 0 { // for case $$ ... $$ any other text @@ -63,6 +66,7 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex segment.Stop = segment.Start + idx node.Lines().Append(segment) node.Closed = true + node.Inline = true return node, parser.Close | parser.NoChildren } @@ -79,27 +83,19 @@ func (b *blockParser) Continue(node ast.Node, reader text.Reader, pc parser.Cont } line, segment := reader.PeekLine() - w, pos := util.IndentWidth(line, 0) + w, pos := util.IndentWidth(line, reader.LineOffset()) if w < 4 { - if block.Dollars { - i := pos - for ; i < len(line) && line[i] == '$'; i++ { - } - length := i - pos - if length >= 2 && util.IsBlank(line[i:]) { - reader.Advance(segment.Stop - segment.Start - segment.Padding) - block.Closed = true + endBytes := giteaUtil.Iif(block.Dollars, b.endBytesDollars, b.endBytesBracket) + if bytes.HasPrefix(line[pos:], endBytes) && util.IsBlank(line[pos+len(endBytes):]) { + if util.IsBlank(line[pos+len(endBytes):]) { + newline := giteaUtil.Iif(line[len(line)-1] != '\n', 0, 1) + reader.Advance(segment.Stop - segment.Start - newline + segment.Padding) return parser.Close } - } else if len(line[pos:]) > 1 && line[pos] == '\\' && line[pos+1] == ']' && util.IsBlank(line[pos+2:]) { - reader.Advance(segment.Stop - segment.Start - segment.Padding) - block.Closed = true - return parser.Close } } - - pos, padding := util.IndentPosition(line, 0, block.Indent) - seg := text.NewSegmentPadding(segment.Start+pos, segment.Stop, padding) + start := segment.Start + giteaUtil.Iif(pos > block.Indent, block.Indent, pos) + seg := text.NewSegmentPadding(start, segment.Stop, segment.Padding) node.Lines().Append(seg) return parser.Continue | parser.NoChildren } diff --git a/modules/markup/markdown/math/block_renderer.go b/modules/markup/markdown/math/block_renderer.go index 0d2a966102..a770efa01c 100644 --- a/modules/markup/markdown/math/block_renderer.go +++ b/modules/markup/markdown/math/block_renderer.go @@ -5,6 +5,7 @@ package math import ( "code.gitea.io/gitea/modules/markup/internal" + giteaUtil "code.gitea.io/gitea/modules/util" gast "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/renderer" @@ -37,10 +38,11 @@ func (r *BlockRenderer) writeLines(w util.BufWriter, source []byte, n gast.Node) func (r *BlockRenderer) renderBlock(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) { n := node.(*Block) if entering { - _ = r.renderInternal.FormatWithSafeAttrs(w, `
`)
+		code := giteaUtil.Iif(n.Inline, "", `
`) + ``
+		_ = r.renderInternal.FormatWithSafeAttrs(w, code)
 		r.writeLines(w, source, n)
 	} else {
-		_, _ = w.WriteString(`
` + "\n") + _, _ = w.WriteString(`
` + giteaUtil.Iif(n.Inline, "", `
`) + "\n") } return gast.WalkContinue, nil } diff --git a/modules/markup/markdown/math/inline_parser.go b/modules/markup/markdown/math/inline_parser.go index 56ae3d57eb..191d1e5a31 100644 --- a/modules/markup/markdown/math/inline_parser.go +++ b/modules/markup/markdown/math/inline_parser.go @@ -79,9 +79,10 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser. opener := len(parser.start) // Now look for an ending line + depth := 0 ender := -1 for i := opener; i < len(line); i++ { - if bytes.HasPrefix(line[i:], parser.end) { + if depth == 0 && bytes.HasPrefix(line[i:], parser.end) { succeedingCharacter := byte(0) if i+len(parser.end) < len(line) { succeedingCharacter = line[i+len(parser.end)] @@ -99,6 +100,11 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser. i++ continue } + if line[i] == '{' { + depth++ + } else if line[i] == '}' { + depth-- + } } if ender == -1 { return nil