Skip to content

Commit 6a369a0

Browse files
committed
feat: add indent to list node
1 parent c40262b commit 6a369a0

File tree

3 files changed

+173
-2
lines changed

3 files changed

+173
-2
lines changed

ast/block.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ type List struct {
125125
BaseBlock
126126

127127
Kind ListKind
128+
Indent int
128129
Children []Node
129130
}
130131

parser/parser.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,22 +119,45 @@ func mergeListItemNodes(nodes []ast.Node) []ast.Node {
119119
switch nodes[i].(type) {
120120
case *ast.OrderedListItem, *ast.UnorderedListItem, *ast.TaskListItem:
121121
var listKind ast.ListKind
122+
var indent int
122123
switch nodes[i].(type) {
123124
case *ast.OrderedListItem:
124125
listKind = ast.OrderedList
126+
indent = nodes[i].(*ast.OrderedListItem).Indent
125127
case *ast.UnorderedListItem:
126128
listKind = ast.UnorderedList
129+
indent = nodes[i].(*ast.UnorderedListItem).Indent
127130
case *ast.TaskListItem:
128131
listKind = ast.DescrpitionList
132+
indent = nodes[i].(*ast.TaskListItem).Indent
129133
}
130-
if prevResultNode == nil || prevResultNode.Type() != ast.ListNode || prevResultNode.(*ast.List).Kind != listKind {
134+
indent = indent / 2
135+
if prevResultNode == nil || prevResultNode.Type() != ast.ListNode || prevResultNode.(*ast.List).Kind != listKind || prevResultNode.(*ast.List).Indent > indent {
131136
prevResultNode = &ast.List{
132137
BaseBlock: ast.BaseBlock{},
133138
Kind: listKind,
139+
Indent: indent,
140+
Children: []ast.Node{nodes[i]},
134141
}
135142
result = append(result, prevResultNode)
143+
continue
144+
}
145+
146+
listNode := prevResultNode.(*ast.List)
147+
if listNode.Indent != indent {
148+
parent := findPossibleParent(listNode, indent)
149+
if parent == nil {
150+
parent = &ast.List{
151+
BaseBlock: ast.BaseBlock{},
152+
Kind: listKind,
153+
Indent: indent,
154+
}
155+
listNode.Children = append(listNode.Children, parent)
156+
}
157+
parent.Children = append(parent.Children, nodes[i])
158+
} else {
159+
listNode.Children = append(listNode.Children, nodes[i])
136160
}
137-
prevResultNode.(*ast.List).Children = append(prevResultNode.(*ast.List).Children, nodes[i])
138161
case *ast.LineBreak:
139162
if prevResultNode != nil && prevResultNode.Type() == ast.ListNode &&
140163
// Check if the prev node is not a line break node.
@@ -166,3 +189,20 @@ func mergeTextNodes(nodes []ast.Node) []ast.Node {
166189
}
167190
return result
168191
}
192+
193+
func findPossibleParent(listNode *ast.List, indent int) *ast.List {
194+
if listNode.Indent == indent {
195+
return listNode
196+
}
197+
if listNode.Indent < indent {
198+
return nil
199+
}
200+
if len(listNode.Children) == 0 {
201+
return nil
202+
}
203+
lastChild := listNode.Children[len(listNode.Children)-1]
204+
if lastChild.Type() != ast.ListNode {
205+
return nil
206+
}
207+
return findPossibleParent(lastChild.(*ast.List), indent)
208+
}

parser/tests/list_test.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package tests
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/usememos/gomark/ast"
10+
"github.com/usememos/gomark/parser"
11+
"github.com/usememos/gomark/parser/tokenizer"
12+
)
13+
14+
func TestListParser(t *testing.T) {
15+
tests := []struct {
16+
text string
17+
nodes []ast.Node
18+
}{
19+
{
20+
text: "1. hello\n2. world",
21+
nodes: []ast.Node{
22+
&ast.List{
23+
Kind: ast.OrderedList,
24+
Children: []ast.Node{
25+
&ast.OrderedListItem{
26+
Number: "1",
27+
Children: []ast.Node{
28+
&ast.Text{
29+
Content: "hello",
30+
},
31+
},
32+
},
33+
&ast.LineBreak{},
34+
&ast.OrderedListItem{
35+
Number: "2",
36+
Children: []ast.Node{
37+
&ast.Text{
38+
Content: "world",
39+
},
40+
},
41+
},
42+
},
43+
},
44+
},
45+
},
46+
{
47+
text: "1. hello\n 2. world",
48+
nodes: []ast.Node{
49+
&ast.List{
50+
Kind: ast.OrderedList,
51+
Children: []ast.Node{
52+
&ast.OrderedListItem{
53+
Number: "1",
54+
Children: []ast.Node{
55+
&ast.Text{
56+
Content: "hello",
57+
},
58+
},
59+
},
60+
&ast.LineBreak{},
61+
&ast.List{
62+
Kind: ast.OrderedList,
63+
Indent: 1,
64+
Children: []ast.Node{
65+
&ast.OrderedListItem{
66+
Number: "2",
67+
Indent: 2,
68+
Children: []ast.Node{
69+
&ast.Text{
70+
Content: "world",
71+
},
72+
},
73+
},
74+
},
75+
},
76+
},
77+
},
78+
},
79+
},
80+
{
81+
text: "* hello\n * world\n* gomark",
82+
nodes: []ast.Node{
83+
&ast.List{
84+
Kind: ast.UnorderedList,
85+
Children: []ast.Node{
86+
&ast.UnorderedListItem{
87+
Symbol: "*",
88+
Children: []ast.Node{
89+
&ast.Text{
90+
Content: "hello",
91+
},
92+
},
93+
},
94+
&ast.LineBreak{},
95+
&ast.List{
96+
Kind: ast.UnorderedList,
97+
Indent: 1,
98+
Children: []ast.Node{
99+
&ast.UnorderedListItem{
100+
Symbol: "*",
101+
Indent: 2,
102+
Children: []ast.Node{
103+
&ast.Text{
104+
Content: "world",
105+
},
106+
},
107+
},
108+
},
109+
},
110+
&ast.LineBreak{},
111+
&ast.UnorderedListItem{
112+
Symbol: "*",
113+
Children: []ast.Node{
114+
&ast.Text{
115+
Content: "gomark",
116+
},
117+
},
118+
},
119+
},
120+
},
121+
},
122+
},
123+
}
124+
125+
for _, test := range tests {
126+
tokens := tokenizer.Tokenize(test.text)
127+
nodes, _ := parser.Parse(tokens)
128+
require.Equal(t, test.nodes, nodes, fmt.Sprintf("Test case: %s", test.text))
129+
}
130+
}

0 commit comments

Comments
 (0)