|
|
- package parser
-
- import (
- "github.com/yuin/goldmark/ast"
- "github.com/yuin/goldmark/text"
- "github.com/yuin/goldmark/util"
- )
-
- type linkReferenceParagraphTransformer struct {
- }
-
- // LinkReferenceParagraphTransformer is a ParagraphTransformer implementation
- // that parses and extracts link reference from paragraphs.
- var LinkReferenceParagraphTransformer = &linkReferenceParagraphTransformer{}
-
- func (p *linkReferenceParagraphTransformer) Transform(node *ast.Paragraph, reader text.Reader, pc Context) {
- lines := node.Lines()
- block := text.NewBlockReader(reader.Source(), lines)
- removes := [][2]int{}
- for {
- start, end := parseLinkReferenceDefinition(block, pc)
- if start > -1 {
- if start == end {
- end++
- }
- removes = append(removes, [2]int{start, end})
- continue
- }
- break
- }
-
- offset := 0
- for _, remove := range removes {
- if lines.Len() == 0 {
- break
- }
- s := lines.Sliced(remove[1]-offset, lines.Len())
- lines.SetSliced(0, remove[0]-offset)
- lines.AppendAll(s)
- offset = remove[1]
- }
-
- if lines.Len() == 0 {
- t := ast.NewTextBlock()
- t.SetBlankPreviousLines(node.HasBlankPreviousLines())
- node.Parent().ReplaceChild(node.Parent(), node, t)
- return
- }
-
- node.SetLines(lines)
- }
-
- func parseLinkReferenceDefinition(block text.Reader, pc Context) (int, int) {
- block.SkipSpaces()
- line, segment := block.PeekLine()
- if line == nil {
- return -1, -1
- }
- startLine, _ := block.Position()
- width, pos := util.IndentWidth(line, 0)
- if width > 3 {
- return -1, -1
- }
- if width != 0 {
- pos++
- }
- if line[pos] != '[' {
- return -1, -1
- }
- open := segment.Start + pos + 1
- closes := -1
- block.Advance(pos + 1)
- for {
- line, segment = block.PeekLine()
- if line == nil {
- return -1, -1
- }
- closure := util.FindClosure(line, '[', ']', false, false)
- if closure > -1 {
- closes = segment.Start + closure
- next := closure + 1
- if next >= len(line) || line[next] != ':' {
- return -1, -1
- }
- block.Advance(next + 1)
- break
- }
- block.AdvanceLine()
- }
- if closes < 0 {
- return -1, -1
- }
- label := block.Value(text.NewSegment(open, closes))
- if util.IsBlank(label) {
- return -1, -1
- }
- block.SkipSpaces()
- destination, ok := parseLinkDestination(block)
- if !ok {
- return -1, -1
- }
- line, segment = block.PeekLine()
- isNewLine := line == nil || util.IsBlank(line)
-
- endLine, _ := block.Position()
- _, spaces, _ := block.SkipSpaces()
- opener := block.Peek()
- if opener != '"' && opener != '\'' && opener != '(' {
- if !isNewLine {
- return -1, -1
- }
- ref := NewReference(label, destination, nil)
- pc.AddReference(ref)
- return startLine, endLine + 1
- }
- if spaces == 0 {
- return -1, -1
- }
- block.Advance(1)
- open = -1
- closes = -1
- closer := opener
- if opener == '(' {
- closer = ')'
- }
- for {
- line, segment = block.PeekLine()
- if line == nil {
- return -1, -1
- }
- if open < 0 {
- open = segment.Start
- }
- closure := util.FindClosure(line, opener, closer, false, true)
- if closure > -1 {
- closes = segment.Start + closure
- block.Advance(closure + 1)
- break
- }
- block.AdvanceLine()
- }
- if closes < 0 {
- return -1, -1
- }
-
- line, segment = block.PeekLine()
- if line != nil && !util.IsBlank(line) {
- if !isNewLine {
- return -1, -1
- }
- title := block.Value(text.NewSegment(open, closes))
- ref := NewReference(label, destination, title)
- pc.AddReference(ref)
- return startLine, endLine
- }
-
- title := block.Value(text.NewSegment(open, closes))
-
- endLine, _ = block.Position()
- ref := NewReference(label, destination, title)
- pc.AddReference(ref)
- return startLine, endLine + 1
- }
|