mirror of
				https://gitcode.com/gitea/gitea.git
				synced 2025-11-04 04:14:26 +08:00 
			
		
		
		
	There is a small bug in #20571 whereby `$a a$b b$` will not be correctly detected as a math inline block of `a a$b b`. This PR fixes this. Also reenable test cases as per #21340 Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
		
			
				
	
	
		
			121 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2022 The Gitea Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a MIT-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
package math
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
 | 
						|
	"github.com/yuin/goldmark/ast"
 | 
						|
	"github.com/yuin/goldmark/parser"
 | 
						|
	"github.com/yuin/goldmark/text"
 | 
						|
)
 | 
						|
 | 
						|
type inlineParser struct {
 | 
						|
	start []byte
 | 
						|
	end   []byte
 | 
						|
}
 | 
						|
 | 
						|
var defaultInlineDollarParser = &inlineParser{
 | 
						|
	start: []byte{'$'},
 | 
						|
	end:   []byte{'$'},
 | 
						|
}
 | 
						|
 | 
						|
// NewInlineDollarParser returns a new inline parser
 | 
						|
func NewInlineDollarParser() parser.InlineParser {
 | 
						|
	return defaultInlineDollarParser
 | 
						|
}
 | 
						|
 | 
						|
var defaultInlineBracketParser = &inlineParser{
 | 
						|
	start: []byte{'\\', '('},
 | 
						|
	end:   []byte{'\\', ')'},
 | 
						|
}
 | 
						|
 | 
						|
// NewInlineDollarParser returns a new inline parser
 | 
						|
func NewInlineBracketParser() parser.InlineParser {
 | 
						|
	return defaultInlineBracketParser
 | 
						|
}
 | 
						|
 | 
						|
// Trigger triggers this parser on $ or \
 | 
						|
func (parser *inlineParser) Trigger() []byte {
 | 
						|
	return parser.start[0:1]
 | 
						|
}
 | 
						|
 | 
						|
func isAlphanumeric(b byte) bool {
 | 
						|
	// Github only cares about 0-9A-Za-z
 | 
						|
	return (b >= '0' && b <= '9') || (b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z')
 | 
						|
}
 | 
						|
 | 
						|
// Parse parses the current line and returns a result of parsing.
 | 
						|
func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
 | 
						|
	line, _ := block.PeekLine()
 | 
						|
 | 
						|
	if !bytes.HasPrefix(line, parser.start) {
 | 
						|
		// We'll catch this one on the next time round
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	precedingCharacter := block.PrecendingCharacter()
 | 
						|
	if precedingCharacter < 256 && isAlphanumeric(byte(precedingCharacter)) {
 | 
						|
		// need to exclude things like `a$` from being considered a start
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// move the opener marker point at the start of the text
 | 
						|
	opener := len(parser.start)
 | 
						|
 | 
						|
	// Now look for an ending line
 | 
						|
	ender := opener
 | 
						|
	for {
 | 
						|
		pos := bytes.Index(line[ender:], parser.end)
 | 
						|
		if pos < 0 {
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
		ender += pos
 | 
						|
 | 
						|
		// Now we want to check the character at the end of our parser section
 | 
						|
		// that is ender + len(parser.end)
 | 
						|
		pos = ender + len(parser.end)
 | 
						|
		if len(line) <= pos {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		if !isAlphanumeric(line[pos]) {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		// move the pointer onwards
 | 
						|
		ender += len(parser.end)
 | 
						|
	}
 | 
						|
 | 
						|
	block.Advance(opener)
 | 
						|
	_, pos := block.Position()
 | 
						|
	node := NewInline()
 | 
						|
	segment := pos.WithStop(pos.Start + ender - opener)
 | 
						|
	node.AppendChild(node, ast.NewRawTextSegment(segment))
 | 
						|
	block.Advance(ender - opener + len(parser.end))
 | 
						|
 | 
						|
	trimBlock(node, block)
 | 
						|
	return node
 | 
						|
}
 | 
						|
 | 
						|
func trimBlock(node *Inline, block text.Reader) {
 | 
						|
	if node.IsBlank(block.Source()) {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// trim first space and last space
 | 
						|
	first := node.FirstChild().(*ast.Text)
 | 
						|
	if !(!first.Segment.IsEmpty() && block.Source()[first.Segment.Start] == ' ') {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	last := node.LastChild().(*ast.Text)
 | 
						|
	if !(!last.Segment.IsEmpty() && block.Source()[last.Segment.Stop-1] == ' ') {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	first.Segment = first.Segment.WithStart(first.Segment.Start + 1)
 | 
						|
	last.Segment = last.Segment.WithStop(last.Segment.Stop - 1)
 | 
						|
}
 |