gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

fcode_block.go (3194B)


      1 package parser
      2 
      3 import (
      4 	"bytes"
      5 
      6 	"github.com/yuin/goldmark/ast"
      7 	"github.com/yuin/goldmark/text"
      8 	"github.com/yuin/goldmark/util"
      9 )
     10 
     11 type fencedCodeBlockParser struct {
     12 }
     13 
     14 var defaultFencedCodeBlockParser = &fencedCodeBlockParser{}
     15 
     16 // NewFencedCodeBlockParser returns a new BlockParser that
     17 // parses fenced code blocks.
     18 func NewFencedCodeBlockParser() BlockParser {
     19 	return defaultFencedCodeBlockParser
     20 }
     21 
     22 type fenceData struct {
     23 	char   byte
     24 	indent int
     25 	length int
     26 	node   ast.Node
     27 }
     28 
     29 var fencedCodeBlockInfoKey = NewContextKey()
     30 
     31 func (b *fencedCodeBlockParser) Trigger() []byte {
     32 	return []byte{'~', '`'}
     33 }
     34 
     35 func (b *fencedCodeBlockParser) Open(parent ast.Node, reader text.Reader, pc Context) (ast.Node, State) {
     36 	line, segment := reader.PeekLine()
     37 	pos := pc.BlockOffset()
     38 	if pos < 0 || (line[pos] != '`' && line[pos] != '~') {
     39 		return nil, NoChildren
     40 	}
     41 	findent := pos
     42 	fenceChar := line[pos]
     43 	i := pos
     44 	for ; i < len(line) && line[i] == fenceChar; i++ {
     45 	}
     46 	oFenceLength := i - pos
     47 	if oFenceLength < 3 {
     48 		return nil, NoChildren
     49 	}
     50 	var info *ast.Text
     51 	if i < len(line)-1 {
     52 		rest := line[i:]
     53 		left := util.TrimLeftSpaceLength(rest)
     54 		right := util.TrimRightSpaceLength(rest)
     55 		if left < len(rest)-right {
     56 			infoStart, infoStop := segment.Start-segment.Padding+i+left, segment.Stop-right
     57 			value := rest[left : len(rest)-right]
     58 			if fenceChar == '`' && bytes.IndexByte(value, '`') > -1 {
     59 				return nil, NoChildren
     60 			} else if infoStart != infoStop {
     61 				info = ast.NewTextSegment(text.NewSegment(infoStart, infoStop))
     62 			}
     63 		}
     64 	}
     65 	node := ast.NewFencedCodeBlock(info)
     66 	pc.Set(fencedCodeBlockInfoKey, &fenceData{fenceChar, findent, oFenceLength, node})
     67 	return node, NoChildren
     68 
     69 }
     70 
     71 func (b *fencedCodeBlockParser) Continue(node ast.Node, reader text.Reader, pc Context) State {
     72 	line, segment := reader.PeekLine()
     73 	fdata := pc.Get(fencedCodeBlockInfoKey).(*fenceData)
     74 
     75 	w, pos := util.IndentWidth(line, reader.LineOffset())
     76 	if w < 4 {
     77 		i := pos
     78 		for ; i < len(line) && line[i] == fdata.char; i++ {
     79 		}
     80 		length := i - pos
     81 		if length >= fdata.length && util.IsBlank(line[i:]) {
     82 			newline := 1
     83 			if line[len(line)-1] != '\n' {
     84 				newline = 0
     85 			}
     86 			reader.Advance(segment.Stop - segment.Start - newline + segment.Padding)
     87 			return Close
     88 		}
     89 	}
     90 	pos, padding := util.IndentPositionPadding(line, reader.LineOffset(), segment.Padding, fdata.indent)
     91 	if pos < 0 {
     92 		pos = util.FirstNonSpacePosition(line)
     93 		if pos < 0 {
     94 			pos = 0
     95 		}
     96 		padding = 0
     97 	}
     98 	seg := text.NewSegmentPadding(segment.Start+pos, segment.Stop, padding)
     99 	// if code block line starts with a tab, keep a tab as it is.
    100 	if padding != 0 {
    101 		preserveLeadingTabInCodeBlock(&seg, reader, fdata.indent)
    102 	}
    103 	node.Lines().Append(seg)
    104 	reader.AdvanceAndSetPadding(segment.Stop-segment.Start-pos-1, padding)
    105 	return Continue | NoChildren
    106 }
    107 
    108 func (b *fencedCodeBlockParser) Close(node ast.Node, reader text.Reader, pc Context) {
    109 	fdata := pc.Get(fencedCodeBlockInfoKey).(*fenceData)
    110 	if fdata.node == node {
    111 		pc.Set(fencedCodeBlockInfoKey, nil)
    112 	}
    113 }
    114 
    115 func (b *fencedCodeBlockParser) CanInterruptParagraph() bool {
    116 	return true
    117 }
    118 
    119 func (b *fencedCodeBlockParser) CanAcceptIndentedLine() bool {
    120 	return false
    121 }