forgejo/vendor/github.com/yuin/goldmark/parser/fcode_block.go
zeripath 27757714d0 Change markdown rendering from blackfriday to goldmark (#9533)
* Move to goldmark

Markdown rendering moved from blackfriday to the goldmark.

Multiple subtle changes required to the goldmark extensions to keep
current rendering and defaults.

Can go further with goldmark linkify and have this work within markdown
rendering making the link processor unnecessary.

Need to think about how to go about allowing extensions - at present it
seems that these would be hard to do without recompilation.

* linter fixes

Co-authored-by: Lauris BH <lauris@nix.lv>
2019-12-31 03:53:28 +02:00

111 lines
2.9 KiB
Go
Vendored

package parser
import (
"bytes"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
type fencedCodeBlockParser struct {
}
var defaultFencedCodeBlockParser = &fencedCodeBlockParser{}
// NewFencedCodeBlockParser returns a new BlockParser that
// parses fenced code blocks.
func NewFencedCodeBlockParser() BlockParser {
return defaultFencedCodeBlockParser
}
type fenceData struct {
char byte
indent int
length int
node ast.Node
}
var fencedCodeBlockInfoKey = NewContextKey()
func (b *fencedCodeBlockParser) Trigger() []byte {
return []byte{'~', '`'}
}
func (b *fencedCodeBlockParser) Open(parent ast.Node, reader text.Reader, pc Context) (ast.Node, State) {
line, segment := reader.PeekLine()
pos := pc.BlockOffset()
if pos < 0 || (line[pos] != '`' && line[pos] != '~') {
return nil, NoChildren
}
findent := pos
fenceChar := line[pos]
i := pos
for ; i < len(line) && line[i] == fenceChar; i++ {
}
oFenceLength := i - pos
if oFenceLength < 3 {
return nil, NoChildren
}
var info *ast.Text
if i < len(line)-1 {
rest := line[i:]
left := util.TrimLeftSpaceLength(rest)
right := util.TrimRightSpaceLength(rest)
if left < len(rest)-right {
infoStart, infoStop := segment.Start-segment.Padding+i+left, segment.Stop-right
value := rest[left : len(rest)-right]
if fenceChar == '`' && bytes.IndexByte(value, '`') > -1 {
return nil, NoChildren
} else if infoStart != infoStop {
info = ast.NewTextSegment(text.NewSegment(infoStart, infoStop))
}
}
}
node := ast.NewFencedCodeBlock(info)
pc.Set(fencedCodeBlockInfoKey, &fenceData{fenceChar, findent, oFenceLength, node})
return node, NoChildren
}
func (b *fencedCodeBlockParser) Continue(node ast.Node, reader text.Reader, pc Context) State {
line, segment := reader.PeekLine()
fdata := pc.Get(fencedCodeBlockInfoKey).(*fenceData)
w, pos := util.IndentWidth(line, reader.LineOffset())
if w < 4 {
i := pos
for ; i < len(line) && line[i] == fdata.char; i++ {
}
length := i - pos
if length >= fdata.length && util.IsBlank(line[i:]) {
newline := 1
if line[len(line)-1] != '\n' {
newline = 0
}
reader.Advance(segment.Stop - segment.Start - newline - segment.Padding)
return Close
}
}
pos, padding := util.DedentPositionPadding(line, reader.LineOffset(), segment.Padding, fdata.indent)
seg := text.NewSegmentPadding(segment.Start+pos, segment.Stop, padding)
node.Lines().Append(seg)
reader.AdvanceAndSetPadding(segment.Stop-segment.Start-pos-1, padding)
return Continue | NoChildren
}
func (b *fencedCodeBlockParser) Close(node ast.Node, reader text.Reader, pc Context) {
fdata := pc.Get(fencedCodeBlockInfoKey).(*fenceData)
if fdata.node == node {
pc.Set(fencedCodeBlockInfoKey, nil)
}
}
func (b *fencedCodeBlockParser) CanInterruptParagraph() bool {
return true
}
func (b *fencedCodeBlockParser) CanAcceptIndentedLine() bool {
return false
}