86 lines
2.1 KiB
Go
86 lines
2.1 KiB
Go
|
package parser
|
||
|
|
||
|
import (
|
||
|
"github.com/yuin/goldmark/ast"
|
||
|
"github.com/yuin/goldmark/text"
|
||
|
"github.com/yuin/goldmark/util"
|
||
|
)
|
||
|
|
||
|
type listItemParser struct {
|
||
|
}
|
||
|
|
||
|
var defaultListItemParser = &listItemParser{}
|
||
|
|
||
|
// NewListItemParser returns a new BlockParser that
|
||
|
// parses list items.
|
||
|
func NewListItemParser() BlockParser {
|
||
|
return defaultListItemParser
|
||
|
}
|
||
|
|
||
|
var skipListParser = NewContextKey()
|
||
|
var skipListParserValue interface{} = true
|
||
|
|
||
|
func (b *listItemParser) Trigger() []byte {
|
||
|
return []byte{'-', '+', '*', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
|
||
|
}
|
||
|
|
||
|
func (b *listItemParser) Open(parent ast.Node, reader text.Reader, pc Context) (ast.Node, State) {
|
||
|
list, lok := parent.(*ast.List)
|
||
|
if !lok { // list item must be a child of a list
|
||
|
return nil, NoChildren
|
||
|
}
|
||
|
offset := lastOffset(list)
|
||
|
line, _ := reader.PeekLine()
|
||
|
match, typ := matchesListItem(line, false)
|
||
|
if typ == notList {
|
||
|
return nil, NoChildren
|
||
|
}
|
||
|
if match[1]-offset > 3 {
|
||
|
return nil, NoChildren
|
||
|
}
|
||
|
itemOffset := calcListOffset(line, match)
|
||
|
node := ast.NewListItem(match[3] + itemOffset)
|
||
|
if match[4] < 0 || match[5]-match[4] == 1 {
|
||
|
return node, NoChildren
|
||
|
}
|
||
|
|
||
|
pos, padding := util.IndentPosition(line[match[4]:], match[4], itemOffset)
|
||
|
child := match[3] + pos
|
||
|
reader.AdvanceAndSetPadding(child, padding)
|
||
|
return node, HasChildren
|
||
|
}
|
||
|
|
||
|
func (b *listItemParser) Continue(node ast.Node, reader text.Reader, pc Context) State {
|
||
|
line, _ := reader.PeekLine()
|
||
|
if util.IsBlank(line) {
|
||
|
return Continue | HasChildren
|
||
|
}
|
||
|
|
||
|
indent, _ := util.IndentWidth(line, reader.LineOffset())
|
||
|
offset := lastOffset(node.Parent())
|
||
|
if indent < offset && indent < 4 {
|
||
|
_, typ := matchesListItem(line, true)
|
||
|
// new list item found
|
||
|
if typ != notList {
|
||
|
pc.Set(skipListParser, skipListParserValue)
|
||
|
}
|
||
|
return Close
|
||
|
}
|
||
|
pos, padding := util.IndentPosition(line, reader.LineOffset(), offset)
|
||
|
reader.AdvanceAndSetPadding(pos, padding)
|
||
|
|
||
|
return Continue | HasChildren
|
||
|
}
|
||
|
|
||
|
func (b *listItemParser) Close(node ast.Node, reader text.Reader, pc Context) {
|
||
|
// nothing to do
|
||
|
}
|
||
|
|
||
|
func (b *listItemParser) CanInterruptParagraph() bool {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (b *listItemParser) CanAcceptIndentedLine() bool {
|
||
|
return false
|
||
|
}
|