mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Fix markdown preview $$ support (#31514)
close #31481 currently `$$A + B$$ test` will ignore text after $$ block test text  before fix  after fix  github display 
This commit is contained in:
		| @@ -555,6 +555,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 { | ||||||
|   | |||||||
| @@ -53,6 +53,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 | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								modules/markup/markdown/math/inline_block_node.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								modules/markup/markdown/math/inline_block_node.go
									
									
									
									
									
										Normal 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{}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -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 | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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...)) | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user