...

Source file src/github.com/yuin/goldmark/parser/fcode_block.go

Documentation: github.com/yuin/goldmark/parser

     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  }
   122  

View as plain text