• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

yuin / goldmark / 23470262150

24 Mar 2026 02:27AM UTC coverage: 82.558% (-0.7%) from 83.229%
23470262150

push

github

yuin
feat: add position information to all nodes

- add position information to all nodes, including inline nodes and link
  reference definition nodes.
- Now link reference definition nodes are represented as a new node
  type.
- Link and image nodes have a new field Reference which is a pointer to the reference
  link if this link is a reference link. This field is nil for non-reference
  links.

182 of 283 new or added lines in 21 files covered. (64.31%)

12 existing lines in 1 file now uncovered.

6286 of 7614 relevant lines covered (82.56%)

470127.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.18
/parser/link_ref.go
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) {
5,454✔
17
        lines := node.Lines()
5,454✔
18
        block := text.NewBlockReader(reader.Source(), lines)
5,454✔
19
        removes := [][2]int{}
5,454✔
20
        for {
11,436✔
21
                ref, start, end := parseLinkReferenceDefinition(block, pc)
5,982✔
22
                if start > -1 {
6,510✔
23
                        if start == 0 {
1,020✔
24
                                ref.SetBlankPreviousLines(node.HasBlankPreviousLines())
492✔
25
                        }
492✔
26
                        node.Parent().InsertBefore(node.Parent(), node, ref)
528✔
27
                        for i := start + 1; i < end; i++ {
618✔
28
                                ref.Lines().Append(lines.At(i))
90✔
29
                        }
90✔
30
                        seg := ref.Lines().At(ref.Lines().Len() - 1)
528✔
31
                        ref.Lines().Set(ref.Lines().Len()-1, seg.TrimRightSpace(reader.Source()))
528✔
32
                        if start == end {
534✔
33
                                end++
6✔
34
                        }
6✔
35
                        removes = append(removes, [2]int{start, end})
528✔
36
                        continue
528✔
37
                }
38
                break
5,454✔
39
        }
40

41
        offset := 0
5,454✔
42
        for _, remove := range removes {
5,982✔
43
                if lines.Len() == 0 {
528✔
44
                        break
×
45
                }
46
                s := lines.Sliced(remove[1]-offset, lines.Len())
528✔
47
                lines.SetSliced(0, remove[0]-offset)
528✔
48
                lines.AppendAll(s)
528✔
49
                offset = remove[1]
528✔
50
        }
51

52
        if lines.Len() == 0 {
5,916✔
53
                node.Parent().RemoveChild(node.Parent(), node)
462✔
54
                return
462✔
55
        }
462✔
56

57
        node.SetLines(lines)
4,992✔
58
}
59

60
func parseLinkReferenceDefinition(block text.Reader, pc Context) (ast.Node, int, int) {
5,982✔
61
        block.SkipSpaces()
5,982✔
62
        line, _ := block.PeekLine()
5,982✔
63
        if line == nil {
6,456✔
64
                return nil, -1, -1
474✔
65
        }
474✔
66
        startLine, _ := block.Position()
5,508✔
67
        width, pos := util.IndentWidth(line, 0)
5,508✔
68
        if width > 3 {
5,508✔
NEW
69
                return nil, -1, -1
×
70
        }
×
71
        if width != 0 {
5,508✔
72
                pos++
×
73
        }
×
74
        if line[pos] != '[' {
9,663✔
75
                return nil, -1, -1
4,155✔
76
        }
4,155✔
77
        _, startPos := block.Position()
1,353✔
78
        block.Advance(pos + 1)
1,353✔
79
        segments, found := block.FindClosure('[', ']', linkFindClosureOptions)
1,353✔
80
        if !found {
1,458✔
81
                return nil, -1, -1
105✔
82
        }
105✔
83
        var label []byte
1,248✔
84
        if segments.Len() == 1 {
2,469✔
85
                label = block.Value(segments.At(0))
1,221✔
86
        } else {
1,248✔
87
                for i := range segments.Len() {
87✔
88
                        s := segments.At(i)
60✔
89
                        label = append(label, block.Value(s)...)
60✔
90
                }
60✔
91
        }
92
        if util.IsBlank(label) {
1,299✔
93
                return nil, -1, -1
51✔
94
        }
51✔
95
        if block.Peek() != ':' {
1,842✔
96
                return nil, -1, -1
645✔
97
        }
645✔
98
        block.Advance(1)
552✔
99
        block.SkipSpaces()
552✔
100
        destination, ok := parseLinkDestination(block)
552✔
101
        if !ok {
558✔
102
                return nil, -1, -1
6✔
103
        }
6✔
104
        line, _ = block.PeekLine()
546✔
105
        isNewLine := line == nil || util.IsBlank(line)
546✔
106

546✔
107
        endLine, _ := block.Position()
546✔
108
        _, spaces, _ := block.SkipSpaces()
546✔
109
        opener := block.Peek()
546✔
110
        if opener != '"' && opener != '\'' && opener != '(' {
843✔
111
                if !isNewLine {
297✔
NEW
112
                        return nil, -1, -1
×
113
                }
×
114
                ref := ast.NewLinkReferenceDefinition(label, destination, nil)
297✔
115
                ref.Lines().Append(startPos)
297✔
116
                pc.AddReference(newASTReference(ref))
297✔
117
                return ref, startLine, endLine + 1
297✔
118
        }
119
        if spaces == 0 {
255✔
120
                return nil, -1, -1
6✔
121
        }
6✔
122
        block.Advance(1)
243✔
123
        closer := opener
243✔
124
        if opener == '(' {
243✔
125
                closer = ')'
×
126
        }
×
127
        segments, found = block.FindClosure(opener, closer, linkFindClosureOptions)
243✔
128
        if !found {
261✔
129
                if !isNewLine {
24✔
130
                        return nil, -1, -1
6✔
131
                }
6✔
132
                ref := ast.NewLinkReferenceDefinition(label, destination, nil)
12✔
133
                ref.Lines().Append(startPos)
12✔
134
                pc.AddReference(newASTReference(ref))
12✔
135
                block.AdvanceLine()
12✔
136
                return ref, startLine, endLine + 1
12✔
137
        }
138
        var title []byte
225✔
139
        if segments.Len() == 1 {
441✔
140
                title = block.Value(segments.At(0))
216✔
141
        } else {
225✔
142
                for i := range segments.Len() {
45✔
143
                        s := segments.At(i)
36✔
144
                        title = append(title, block.Value(s)...)
36✔
145
                }
36✔
146
        }
147

148
        line, _ = block.PeekLine()
225✔
149
        if line != nil && !util.IsBlank(line) {
237✔
150
                if !isNewLine {
18✔
151
                        return nil, -1, -1
6✔
152
                }
6✔
153
                ref := ast.NewLinkReferenceDefinition(label, destination, title)
6✔
154
                ref.Lines().Append(startPos)
6✔
155
                pc.AddReference(newASTReference(ref))
6✔
156
                return ref, startLine, endLine
6✔
157
        }
158

159
        endLine, _ = block.Position()
213✔
160
        ref := ast.NewLinkReferenceDefinition(label, destination, title)
213✔
161
        ref.Lines().Append(startPos)
213✔
162
        pc.AddReference(newASTReference(ref))
213✔
163
        return ref, startLine, endLine + 1
213✔
164
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc