...

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

Documentation: github.com/yuin/goldmark/parser

     1  package parser
     2  
     3  import (
     4  	"github.com/yuin/goldmark/ast"
     5  	"github.com/yuin/goldmark/text"
     6  	"github.com/yuin/goldmark/util"
     7  )
     8  
     9  type codeBlockParser struct {
    10  }
    11  
    12  // CodeBlockParser is a BlockParser implementation that parses indented code blocks.
    13  var defaultCodeBlockParser = &codeBlockParser{}
    14  
    15  // NewCodeBlockParser returns a new BlockParser that
    16  // parses code blocks.
    17  func NewCodeBlockParser() BlockParser {
    18  	return defaultCodeBlockParser
    19  }
    20  
    21  func (b *codeBlockParser) Trigger() []byte {
    22  	return nil
    23  }
    24  
    25  func (b *codeBlockParser) Open(parent ast.Node, reader text.Reader, pc Context) (ast.Node, State) {
    26  	line, segment := reader.PeekLine()
    27  	pos, padding := util.IndentPosition(line, reader.LineOffset(), 4)
    28  	if pos < 0 || util.IsBlank(line) {
    29  		return nil, NoChildren
    30  	}
    31  	node := ast.NewCodeBlock()
    32  	reader.AdvanceAndSetPadding(pos, padding)
    33  	_, segment = reader.PeekLine()
    34  	// if code block line starts with a tab, keep a tab as it is.
    35  	if segment.Padding != 0 {
    36  		preserveLeadingTabInCodeBlock(&segment, reader, 0)
    37  	}
    38  	node.Lines().Append(segment)
    39  	reader.Advance(segment.Len() - 1)
    40  	return node, NoChildren
    41  
    42  }
    43  
    44  func (b *codeBlockParser) Continue(node ast.Node, reader text.Reader, pc Context) State {
    45  	line, segment := reader.PeekLine()
    46  	if util.IsBlank(line) {
    47  		node.Lines().Append(segment.TrimLeftSpaceWidth(4, reader.Source()))
    48  		return Continue | NoChildren
    49  	}
    50  	pos, padding := util.IndentPosition(line, reader.LineOffset(), 4)
    51  	if pos < 0 {
    52  		return Close
    53  	}
    54  	reader.AdvanceAndSetPadding(pos, padding)
    55  	_, segment = reader.PeekLine()
    56  
    57  	// if code block line starts with a tab, keep a tab as it is.
    58  	if segment.Padding != 0 {
    59  		preserveLeadingTabInCodeBlock(&segment, reader, 0)
    60  	}
    61  
    62  	node.Lines().Append(segment)
    63  	reader.Advance(segment.Len() - 1)
    64  	return Continue | NoChildren
    65  }
    66  
    67  func (b *codeBlockParser) Close(node ast.Node, reader text.Reader, pc Context) {
    68  	// trim trailing blank lines
    69  	lines := node.Lines()
    70  	length := lines.Len() - 1
    71  	source := reader.Source()
    72  	for length >= 0 {
    73  		line := lines.At(length)
    74  		if util.IsBlank(line.Value(source)) {
    75  			length--
    76  		} else {
    77  			break
    78  		}
    79  	}
    80  	lines.SetSliced(0, length+1)
    81  }
    82  
    83  func (b *codeBlockParser) CanInterruptParagraph() bool {
    84  	return false
    85  }
    86  
    87  func (b *codeBlockParser) CanAcceptIndentedLine() bool {
    88  	return true
    89  }
    90  
    91  func preserveLeadingTabInCodeBlock(segment *text.Segment, reader text.Reader, indent int) {
    92  	offsetWithPadding := reader.LineOffset() + indent
    93  	sl, ss := reader.Position()
    94  	reader.SetPosition(sl, text.NewSegment(ss.Start-1, ss.Stop))
    95  	if offsetWithPadding == reader.LineOffset() {
    96  		segment.Padding = 0
    97  		segment.Start--
    98  	}
    99  	reader.SetPosition(sl, ss)
   100  }
   101  

View as plain text