gtsocial-umbx

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

link_ref.go (3383B)


      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 }