125 lines · 3.4 KB · Go
RawDownload
// Copyright 2022 The Gitea Authors. All rights reserved.// SPDX-License-Identifier: MIT package math import (	"bytes" 	"github.com/yuin/goldmark/ast"	"github.com/yuin/goldmark/parser"	"github.com/yuin/goldmark/text"	"github.com/yuin/goldmark/util") type blockParser struct {	parseDollars bool} // NewBlockParser creates a new math BlockParserfunc NewBlockParser(parseDollarBlocks bool) parser.BlockParser {	return &blockParser{		parseDollars: parseDollarBlocks,	}} // Open parses the current line and returns a result of parsing.func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Context) (ast.Node, parser.State) {	line, segment := reader.PeekLine()	pos := pc.BlockOffset()	if pos == -1 || len(line[pos:]) < 2 {		return nil, parser.NoChildren	} 	dollars := false	if b.parseDollars && line[pos] == '$' && line[pos+1] == '$' {		dollars = true	} else if line[pos] != '\\' || line[pos+1] != '[' {		return nil, parser.NoChildren	} 	node := NewBlock(dollars, pos) 	// Now we need to check if the ending block is on the segment...	endBytes := []byte{'\\', ']'}	if dollars {		endBytes = []byte{'$', '$'}	}	idx := bytes.Index(line[pos+2:], endBytes)	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		reader.Advance(segment.Len() - 1)		segment.Start += 2		node.Lines().Append(segment)		node.Closed = true		return node, parser.Close | parser.NoChildren	} 	return nil, parser.NoChildren} // Continue parses the current line and returns a result of parsing.func (b *blockParser) Continue(node ast.Node, reader text.Reader, pc parser.Context) parser.State {	block := node.(*Block)	if block.Closed {		return parser.Close	} 	line, segment := reader.PeekLine()	w, pos := util.IndentWidth(line, 0)	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				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)	node.Lines().Append(seg)	reader.AdvanceAndSetPadding(segment.Stop-segment.Start-pos-1, padding)	return parser.Continue | parser.NoChildren} // Close will be called when the parser returns Close.func (b *blockParser) Close(node ast.Node, reader text.Reader, pc parser.Context) {	// noop} // CanInterruptParagraph returns true if the parser can interrupt paragraphs,// otherwise false.func (b *blockParser) CanInterruptParagraph() bool {	return true} // CanAcceptIndentedLine returns true if the parser can open new node when// the given line is being indented more than 3 spaces.func (b *blockParser) CanAcceptIndentedLine() bool {	return false} // Trigger returns a list of characters that triggers Parse method of// this parser.// If Trigger returns a nil, Open will be called with any lines.//// We leave this as nil as our parse method is quick enoughfunc (b *blockParser) Trigger() []byte {	return nil}