gtsocial-umbx

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

code_block.go (2635B)


      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 }