gtsocial-umbx

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

setext_headings.go (3079B)


      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 var temporaryParagraphKey = NewContextKey()
     10 
     11 type setextHeadingParser struct {
     12 	HeadingConfig
     13 }
     14 
     15 func matchesSetextHeadingBar(line []byte) (byte, bool) {
     16 	start := 0
     17 	end := len(line)
     18 	space := util.TrimLeftLength(line, []byte{' '})
     19 	if space > 3 {
     20 		return 0, false
     21 	}
     22 	start += space
     23 	level1 := util.TrimLeftLength(line[start:end], []byte{'='})
     24 	c := byte('=')
     25 	var level2 int
     26 	if level1 == 0 {
     27 		level2 = util.TrimLeftLength(line[start:end], []byte{'-'})
     28 		c = '-'
     29 	}
     30 	if util.IsSpace(line[end-1]) {
     31 		end -= util.TrimRightSpaceLength(line[start:end])
     32 	}
     33 	if !((level1 > 0 && start+level1 == end) || (level2 > 0 && start+level2 == end)) {
     34 		return 0, false
     35 	}
     36 	return c, true
     37 }
     38 
     39 // NewSetextHeadingParser return a new BlockParser that can parse Setext headings.
     40 func NewSetextHeadingParser(opts ...HeadingOption) BlockParser {
     41 	p := &setextHeadingParser{}
     42 	for _, o := range opts {
     43 		o.SetHeadingOption(&p.HeadingConfig)
     44 	}
     45 	return p
     46 }
     47 
     48 func (b *setextHeadingParser) Trigger() []byte {
     49 	return []byte{'-', '='}
     50 }
     51 
     52 func (b *setextHeadingParser) Open(parent ast.Node, reader text.Reader, pc Context) (ast.Node, State) {
     53 	last := pc.LastOpenedBlock().Node
     54 	if last == nil {
     55 		return nil, NoChildren
     56 	}
     57 	paragraph, ok := last.(*ast.Paragraph)
     58 	if !ok || paragraph.Parent() != parent {
     59 		return nil, NoChildren
     60 	}
     61 	line, segment := reader.PeekLine()
     62 	c, ok := matchesSetextHeadingBar(line)
     63 	if !ok {
     64 		return nil, NoChildren
     65 	}
     66 	level := 1
     67 	if c == '-' {
     68 		level = 2
     69 	}
     70 	node := ast.NewHeading(level)
     71 	node.Lines().Append(segment)
     72 	pc.Set(temporaryParagraphKey, last)
     73 	return node, NoChildren | RequireParagraph
     74 }
     75 
     76 func (b *setextHeadingParser) Continue(node ast.Node, reader text.Reader, pc Context) State {
     77 	return Close
     78 }
     79 
     80 func (b *setextHeadingParser) Close(node ast.Node, reader text.Reader, pc Context) {
     81 	heading := node.(*ast.Heading)
     82 	segment := node.Lines().At(0)
     83 	heading.Lines().Clear()
     84 	tmp := pc.Get(temporaryParagraphKey).(*ast.Paragraph)
     85 	pc.Set(temporaryParagraphKey, nil)
     86 	if tmp.Lines().Len() == 0 {
     87 		next := heading.NextSibling()
     88 		segment = segment.TrimLeftSpace(reader.Source())
     89 		if next == nil || !ast.IsParagraph(next) {
     90 			para := ast.NewParagraph()
     91 			para.Lines().Append(segment)
     92 			heading.Parent().InsertAfter(heading.Parent(), heading, para)
     93 		} else {
     94 			next.(ast.Node).Lines().Unshift(segment)
     95 		}
     96 		heading.Parent().RemoveChild(heading.Parent(), heading)
     97 	} else {
     98 		heading.SetLines(tmp.Lines())
     99 		heading.SetBlankPreviousLines(tmp.HasBlankPreviousLines())
    100 		tp := tmp.Parent()
    101 		if tp != nil {
    102 			tp.RemoveChild(tp, tmp)
    103 		}
    104 	}
    105 
    106 	if b.Attribute {
    107 		parseLastLineAttributes(node, reader, pc)
    108 	}
    109 
    110 	if b.AutoHeadingID {
    111 		id, ok := node.AttributeString("id")
    112 		if !ok {
    113 			generateAutoHeadingID(heading, reader, pc)
    114 		} else {
    115 			pc.IDs().Put(id.([]byte))
    116 		}
    117 	}
    118 }
    119 
    120 func (b *setextHeadingParser) CanInterruptParagraph() bool {
    121 	return true
    122 }
    123 
    124 func (b *setextHeadingParser) CanAcceptIndentedLine() bool {
    125 	return false
    126 }