...

Source file src/github.com/yuin/goldmark/parser/link_ref.go

Documentation: github.com/yuin/goldmark/parser

     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 linkReferenceParagraphTransformer struct {
    10  }
    11  
    12  // LinkReferenceParagraphTransformer is a ParagraphTransformer implementation
    13  // that parses and extracts link reference from paragraphs.
    14  var LinkReferenceParagraphTransformer = &linkReferenceParagraphTransformer{}
    15  
    16  func (p *linkReferenceParagraphTransformer) Transform(node *ast.Paragraph, reader text.Reader, pc Context) {
    17  	lines := node.Lines()
    18  	block := text.NewBlockReader(reader.Source(), lines)
    19  	removes := [][2]int{}
    20  	for {
    21  		start, end := parseLinkReferenceDefinition(block, pc)
    22  		if start > -1 {
    23  			if start == end {
    24  				end++
    25  			}
    26  			removes = append(removes, [2]int{start, end})
    27  			continue
    28  		}
    29  		break
    30  	}
    31  
    32  	offset := 0
    33  	for _, remove := range removes {
    34  		if lines.Len() == 0 {
    35  			break
    36  		}
    37  		s := lines.Sliced(remove[1]-offset, lines.Len())
    38  		lines.SetSliced(0, remove[0]-offset)
    39  		lines.AppendAll(s)
    40  		offset = remove[1]
    41  	}
    42  
    43  	if lines.Len() == 0 {
    44  		t := ast.NewTextBlock()
    45  		t.SetBlankPreviousLines(node.HasBlankPreviousLines())
    46  		node.Parent().ReplaceChild(node.Parent(), node, t)
    47  		return
    48  	}
    49  
    50  	node.SetLines(lines)
    51  }
    52  
    53  func parseLinkReferenceDefinition(block text.Reader, pc Context) (int, int) {
    54  	block.SkipSpaces()
    55  	line, _ := block.PeekLine()
    56  	if line == nil {
    57  		return -1, -1
    58  	}
    59  	startLine, _ := block.Position()
    60  	width, pos := util.IndentWidth(line, 0)
    61  	if width > 3 {
    62  		return -1, -1
    63  	}
    64  	if width != 0 {
    65  		pos++
    66  	}
    67  	if line[pos] != '[' {
    68  		return -1, -1
    69  	}
    70  	block.Advance(pos + 1)
    71  	segments, found := block.FindClosure('[', ']', linkFindClosureOptions)
    72  	if !found {
    73  		return -1, -1
    74  	}
    75  	var label []byte
    76  	if segments.Len() == 1 {
    77  		label = block.Value(segments.At(0))
    78  	} else {
    79  		for i := 0; i < segments.Len(); i++ {
    80  			s := segments.At(i)
    81  			label = append(label, block.Value(s)...)
    82  		}
    83  	}
    84  	if util.IsBlank(label) {
    85  		return -1, -1
    86  	}
    87  	if block.Peek() != ':' {
    88  		return -1, -1
    89  	}
    90  	block.Advance(1)
    91  	block.SkipSpaces()
    92  	destination, ok := parseLinkDestination(block)
    93  	if !ok {
    94  		return -1, -1
    95  	}
    96  	line, _ = block.PeekLine()
    97  	isNewLine := line == nil || util.IsBlank(line)
    98  
    99  	endLine, _ := block.Position()
   100  	_, spaces, _ := block.SkipSpaces()
   101  	opener := block.Peek()
   102  	if opener != '"' && opener != '\'' && opener != '(' {
   103  		if !isNewLine {
   104  			return -1, -1
   105  		}
   106  		ref := NewReference(label, destination, nil)
   107  		pc.AddReference(ref)
   108  		return startLine, endLine + 1
   109  	}
   110  	if spaces == 0 {
   111  		return -1, -1
   112  	}
   113  	block.Advance(1)
   114  	closer := opener
   115  	if opener == '(' {
   116  		closer = ')'
   117  	}
   118  	segments, found = block.FindClosure(opener, closer, linkFindClosureOptions)
   119  	if !found {
   120  		if !isNewLine {
   121  			return -1, -1
   122  		}
   123  		ref := NewReference(label, destination, nil)
   124  		pc.AddReference(ref)
   125  		block.AdvanceLine()
   126  		return startLine, endLine + 1
   127  	}
   128  	var title []byte
   129  	if segments.Len() == 1 {
   130  		title = block.Value(segments.At(0))
   131  	} else {
   132  		for i := 0; i < segments.Len(); i++ {
   133  			s := segments.At(i)
   134  			title = append(title, block.Value(s)...)
   135  		}
   136  	}
   137  
   138  	line, _ = block.PeekLine()
   139  	if line != nil && !util.IsBlank(line) {
   140  		if !isNewLine {
   141  			return -1, -1
   142  		}
   143  		ref := NewReference(label, destination, title)
   144  		pc.AddReference(ref)
   145  		return startLine, endLine
   146  	}
   147  
   148  	endLine, _ = block.Position()
   149  	ref := NewReference(label, destination, title)
   150  	pc.AddReference(ref)
   151  	return startLine, endLine + 1
   152  }
   153  

View as plain text