|
- package parser
-
- import (
- "github.com/yuin/goldmark/ast"
- "github.com/yuin/goldmark/text"
- "github.com/yuin/goldmark/util"
- )
-
- var temporaryParagraphKey = NewContextKey()
-
- type setextHeadingParser struct {
- HeadingConfig
- }
-
- func matchesSetextHeadingBar(line []byte) (byte, bool) {
- start := 0
- end := len(line)
- space := util.TrimLeftLength(line, []byte{' '})
- if space > 3 {
- return 0, false
- }
- start += space
- level1 := util.TrimLeftLength(line[start:end], []byte{'='})
- c := byte('=')
- var level2 int
- if level1 == 0 {
- level2 = util.TrimLeftLength(line[start:end], []byte{'-'})
- c = '-'
- }
- if util.IsSpace(line[end-1]) {
- end -= util.TrimRightSpaceLength(line[start:end])
- }
- if !((level1 > 0 && start+level1 == end) || (level2 > 0 && start+level2 == end)) {
- return 0, false
- }
- return c, true
- }
-
- // NewSetextHeadingParser return a new BlockParser that can parse Setext headings.
- func NewSetextHeadingParser(opts ...HeadingOption) BlockParser {
- p := &setextHeadingParser{}
- for _, o := range opts {
- o.SetHeadingOption(&p.HeadingConfig)
- }
- return p
- }
-
- func (b *setextHeadingParser) Trigger() []byte {
- return []byte{'-', '='}
- }
-
- func (b *setextHeadingParser) Open(parent ast.Node, reader text.Reader, pc Context) (ast.Node, State) {
- last := pc.LastOpenedBlock().Node
- if last == nil {
- return nil, NoChildren
- }
- paragraph, ok := last.(*ast.Paragraph)
- if !ok || paragraph.Parent() != parent {
- return nil, NoChildren
- }
- line, segment := reader.PeekLine()
- c, ok := matchesSetextHeadingBar(line)
- if !ok {
- return nil, NoChildren
- }
- level := 1
- if c == '-' {
- level = 2
- }
- node := ast.NewHeading(level)
- node.Lines().Append(segment)
- pc.Set(temporaryParagraphKey, last)
- return node, NoChildren | RequireParagraph
- }
-
- func (b *setextHeadingParser) Continue(node ast.Node, reader text.Reader, pc Context) State {
- return Close
- }
-
- func (b *setextHeadingParser) Close(node ast.Node, reader text.Reader, pc Context) {
- heading := node.(*ast.Heading)
- segment := node.Lines().At(0)
- heading.Lines().Clear()
- tmp := pc.Get(temporaryParagraphKey).(*ast.Paragraph)
- pc.Set(temporaryParagraphKey, nil)
- if tmp.Lines().Len() == 0 {
- next := heading.NextSibling()
- segment = segment.TrimLeftSpace(reader.Source())
- if next == nil || !ast.IsParagraph(next) {
- para := ast.NewParagraph()
- para.Lines().Append(segment)
- heading.Parent().InsertAfter(heading.Parent(), heading, para)
- } else {
- next.(ast.Node).Lines().Unshift(segment)
- }
- heading.Parent().RemoveChild(heading.Parent(), heading)
- } else {
- heading.SetLines(tmp.Lines())
- heading.SetBlankPreviousLines(tmp.HasBlankPreviousLines())
- tp := tmp.Parent()
- if tp != nil {
- tp.RemoveChild(tp, tmp)
- }
- }
-
- if b.Attribute {
- parseLastLineAttributes(node, reader, pc)
- }
-
- if b.AutoHeadingID {
- id, ok := node.AttributeString("id")
- if !ok {
- generateAutoHeadingID(heading, reader, pc)
- } else {
- pc.IDs().Put(id.([]byte))
- }
- }
- }
-
- func (b *setextHeadingParser) CanInterruptParagraph() bool {
- return true
- }
-
- func (b *setextHeadingParser) CanAcceptIndentedLine() bool {
- return false
- }
|