Skip to content

Commit e8f2337

Browse files
committed
fix: block positions
1 parent dfa1ae1 commit e8f2337

8 files changed

Lines changed: 98 additions & 25 deletions

File tree

ast/ast.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ type Node interface {
5757
// If this node position is not defined, Pos returns -1.
5858
Pos() int
5959

60+
// SetPos sets a position of this node in a source.
61+
// Some node may ignore this method. For example, Paragraph node ignores this method because
62+
// it calculates its position from its lines.
63+
SetPos(v int)
64+
6065
// NextSibling returns a next sibling node of this node.
6166
NextSibling() Node
6267

@@ -179,6 +184,23 @@ type Node interface {
179184
RemoveAttributes()
180185
}
181186

187+
type pos struct {
188+
has bool
189+
value int
190+
}
191+
192+
func (p *pos) Pos() int {
193+
if p.has {
194+
return p.value
195+
}
196+
return -1
197+
}
198+
199+
func (p *pos) SetPos(v int) {
200+
p.has = true
201+
p.value = v
202+
}
203+
182204
// A BaseNode struct implements the Node interface partialliy.
183205
type BaseNode struct {
184206
firstChild Node
@@ -188,6 +210,7 @@ type BaseNode struct {
188210
prev Node
189211
childCount int
190212
attributes []Attribute
213+
pos pos
191214
}
192215

193216
func ensureIsolated(v Node) {
@@ -196,6 +219,16 @@ func ensureIsolated(v Node) {
196219
}
197220
}
198221

222+
// Pos implements Node.Pos .
223+
func (n *BaseNode) Pos() int {
224+
return n.pos.Pos()
225+
}
226+
227+
// SetPos implements Node.SetPos .
228+
func (n *BaseNode) SetPos(v int) {
229+
n.pos.SetPos(v)
230+
}
231+
199232
// HasChildren implements Node.HasChildren .
200233
func (n *BaseNode) HasChildren() bool {
201234
return n.firstChild != nil

ast/block.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,6 @@ func (b *BaseBlock) Type() NodeType {
1919
return TypeBlock
2020
}
2121

22-
// Pos implements Node.Pos.
23-
func (b *BaseBlock) Pos() int {
24-
if b.lines.Len() == 0 {
25-
return -1
26-
}
27-
return b.lines.At(0).Start
28-
}
29-
3022
// IsRaw implements Node.IsRaw.
3123
func (b *BaseBlock) IsRaw() bool {
3224
return false
@@ -132,6 +124,14 @@ func (n *TextBlock) Dump(source []byte, level int) {
132124
DumpHelper(n, source, level, nil, nil)
133125
}
134126

127+
// Pos implements Node.Pos.
128+
func (n *TextBlock) Pos() int {
129+
if n.lines.Len() == 0 {
130+
return -1
131+
}
132+
return n.lines.At(0).Start
133+
}
134+
135135
// KindTextBlock is a NodeKind of the TextBlock node.
136136
var KindTextBlock = NewNodeKind("TextBlock")
137137

@@ -164,6 +164,14 @@ func (n *Paragraph) Dump(source []byte, level int) {
164164
DumpHelper(n, source, level, nil, nil)
165165
}
166166

167+
// Pos implements Node.Pos.
168+
func (n *Paragraph) Pos() int {
169+
if n.lines.Len() == 0 {
170+
return -1
171+
}
172+
return n.lines.At(0).Start
173+
}
174+
167175
// KindParagraph is a NodeKind of the Paragraph node.
168176
var KindParagraph = NewNodeKind("Paragraph")
169177

@@ -512,6 +520,7 @@ func (n *HTMLBlock) Dump(source []byte, level int) {
512520
indent := strings.Repeat(" ", level)
513521
fmt.Printf("%s%s {\n", indent, "HTMLBlock")
514522
indent2 := strings.Repeat(" ", level+1)
523+
fmt.Printf("%sPos: %d\n", indent2, n.Pos())
515524
fmt.Printf("%sRawText: \"", indent2)
516525
for i := range n.Lines().Len() {
517526
s := n.Lines().At(i)
@@ -576,6 +585,14 @@ func (l *LinkReferenceDefinition) IsRaw() bool {
576585
return true
577586
}
578587

588+
// Pos implements Node.Pos.
589+
func (l *LinkReferenceDefinition) Pos() int {
590+
if l.lines.Len() == 0 {
591+
return -1
592+
}
593+
return l.lines.At(0).Start
594+
}
595+
579596
// Dump implements Node.Dump.
580597
func (l *LinkReferenceDefinition) Dump(source []byte, level int) {
581598
m := map[string]string{

ast/inline.go

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,13 @@ import (
1111
// A BaseInline struct implements the Node interface partialliy.
1212
type BaseInline struct {
1313
BaseNode
14-
15-
pos int
16-
hasPos bool
1714
}
1815

1916
// Type implements Node.Type.
2017
func (b *BaseInline) Type() NodeType {
2118
return TypeInline
2219
}
2320

24-
// Pos implements Node.Pos.
25-
func (b *BaseInline) Pos() int {
26-
if !b.hasPos {
27-
return -1
28-
}
29-
return b.pos
30-
}
31-
32-
// SetPos sets a position of this node.
33-
func (b *BaseInline) SetPos(pos int) {
34-
b.pos = pos
35-
b.hasPos = true
36-
}
37-
3821
// IsRaw implements Node.IsRaw.
3922
func (b *BaseInline) IsRaw() bool {
4023
return false

ast_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
. "github.com/yuin/goldmark"
88
"github.com/yuin/goldmark/ast"
9+
"github.com/yuin/goldmark/extension"
910
"github.com/yuin/goldmark/parser"
1011
"github.com/yuin/goldmark/testutil"
1112
"github.com/yuin/goldmark/text"
@@ -334,3 +335,16 @@ aaaa **b**
334335
t.Error("unexpected position for 1st image")
335336
}
336337
}
338+
339+
func TestBlockPos(t *testing.T) {
340+
markdown := New(
341+
WithExtensions(extension.GFM, extension.Footnote, extension.DefinitionList),
342+
)
343+
344+
source := []byte(`
345+
- [ ] hoge
346+
`)
347+
c := parser.NewContext()
348+
n := markdown.Parser().Parse(text.NewReader(source), parser.WithContext(c))
349+
n.Dump(source, 0)
350+
}

extension/ast/definition_list.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ func (n *DefinitionList) Dump(source []byte, level int) {
1717
gast.DumpHelper(n, source, level, nil, nil)
1818
}
1919

20+
// Pos implements Node.Pos.
21+
func (n *DefinitionList) Pos() int {
22+
if n.FirstChild() != nil {
23+
return n.FirstChild().Pos()
24+
}
25+
return -1
26+
}
27+
2028
// KindDefinitionList is a NodeKind of the DefinitionList node.
2129
var KindDefinitionList = gast.NewNodeKind("DefinitionList")
2230

@@ -44,6 +52,14 @@ func (n *DefinitionTerm) Dump(source []byte, level int) {
4452
gast.DumpHelper(n, source, level, nil, nil)
4553
}
4654

55+
// Pos implements Node.Pos.
56+
func (n *DefinitionTerm) Pos() int {
57+
if n.Lines().Len() == 0 {
58+
return -1
59+
}
60+
return n.Lines().At(0).Start
61+
}
62+
4763
// KindDefinitionTerm is a NodeKind of the DefinitionTerm node.
4864
var KindDefinitionTerm = gast.NewNodeKind("DefinitionTerm")
4965

extension/ast/table.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ func (n *TableHeader) Dump(source []byte, level int) {
123123
// NewTableHeader returns a new TableHeader node.
124124
func NewTableHeader(row *TableRow) *TableHeader {
125125
n := &TableHeader{}
126+
n.SetPos(row.Pos())
126127
for c := row.FirstChild(); c != nil; {
127128
next := c.NextSibling()
128129
n.AppendChild(n, c)

extension/table.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ func NewTableParagraphTransformer() parser.ParagraphTransformer {
154154
}
155155

156156
func (b *tableParagraphTransformer) Transform(node *gast.Paragraph, reader text.Reader, pc parser.Context) {
157+
ppos := node.Pos()
157158
lines := node.Lines()
158159
if lines.Len() < 2 {
159160
return
@@ -169,6 +170,7 @@ func (b *tableParagraphTransformer) Transform(node *gast.Paragraph, reader text.
169170
}
170171
table := ast.NewTable()
171172
table.Alignments = alignments
173+
table.SetPos(ppos)
172174
table.AppendChild(table, ast.NewTableHeader(header))
173175
for j := i + 1; j < lines.Len(); j++ {
174176
table.AppendChild(table, b.parseRow(lines.At(j), alignments, false, reader, pc))
@@ -187,13 +189,15 @@ func (b *tableParagraphTransformer) Transform(node *gast.Paragraph, reader text.
187189

188190
func (b *tableParagraphTransformer) parseRow(segment text.Segment,
189191
alignments []ast.Alignment, isHeader bool, reader text.Reader, pc parser.Context) *ast.TableRow {
192+
npos := segment
190193
source := reader.Source()
191194
segment = segment.TrimLeftSpace(source)
192195
segment = segment.TrimRightSpace(source)
193196
line := segment.Value(source)
194197
pos := 0
195198
limit := len(line)
196199
row := ast.NewTableRow(alignments)
200+
row.SetPos(npos.Start)
197201
if len(line) > 0 && line[pos] == '|' {
198202
pos++
199203
}
@@ -213,6 +217,7 @@ func (b *tableParagraphTransformer) parseRow(segment text.Segment,
213217

214218
var escapedCell *escapedPipeCell
215219
node := ast.NewTableCell()
220+
node.SetPos(npos.Start + pos - npos.Padding)
216221
node.Alignment = alignment
217222
hasBacktick := false
218223
closure := pos

parser/parser.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,13 +985,17 @@ retry:
985985
if continuable && result == noBlocksOpened && !bp.CanInterruptParagraph() {
986986
continue
987987
}
988+
988989
if w > 3 && !bp.CanAcceptIndentedLine() {
989990
continue
990991
}
991992
lastBlock = pc.LastOpenedBlock()
992993
last := lastBlock.Node
994+
_, blockPos := reader.Position()
993995
node, state := bp.Open(parent, reader, pc)
994996
if node != nil {
997+
node.SetPos(blockPos.Start + max(pc.BlockOffset(), 0))
998+
995999
// Parser requires last node to be a paragraph.
9961000
// With table extension:
9971001
//

0 commit comments

Comments
 (0)