forked from Kunde21/markdownfmt
/
renderer_table.go
140 lines (127 loc) · 3.85 KB
/
renderer_table.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package markdown
import (
"bytes"
"github.com/mattn/go-runewidth"
"github.com/pkg/errors"
"github.com/yuin/goldmark/ast"
extAST "github.com/yuin/goldmark/extension/ast"
)
func (r *render) renderTable(node *extAST.Table) error {
var (
columnAligns []extAST.Alignment
columnWidths []int
colIndex int
cellBuf bytes.Buffer
)
// Walk tree initially to count column widths and alignments.
for n := node.FirstChild(); n != nil; n = n.NextSibling() {
if err := ast.Walk(n, func(inner ast.Node, entering bool) (ast.WalkStatus, error) {
switch tnode := inner.(type) {
case *extAST.TableRow, *extAST.TableHeader:
if entering {
colIndex = 0
}
case *extAST.TableCell:
if entering {
if _, isHeader := tnode.Parent().(*extAST.TableHeader); isHeader {
columnAligns = append(columnAligns, tnode.Alignment)
}
cellBuf.Reset()
if err := ast.Walk(tnode, r.mr.newRender(&cellBuf, r.source).renderNode); err != nil {
return ast.WalkStop, err
}
width := runewidth.StringWidth(cellBuf.String())
if len(columnWidths) <= colIndex {
columnWidths = append(columnWidths, width)
} else if width > columnWidths[colIndex] {
columnWidths[colIndex] = width
}
colIndex++
return ast.WalkSkipChildren, nil
}
default:
return ast.WalkStop, errors.Errorf("detected unexpected tree type %s", tnode.Kind().String())
}
return ast.WalkContinue, nil
}); err != nil {
return err
}
}
// Write all according to alignments and width.
for n := node.FirstChild(); n != nil; n = n.NextSibling() {
if err := ast.Walk(n, func(inner ast.Node, entering bool) (ast.WalkStatus, error) {
switch tnode := inner.(type) {
case *extAST.TableRow:
if entering {
colIndex = 0
_, _ = r.w.Write(newLineChar)
break
}
_, _ = r.w.Write([]byte("|"))
case *extAST.TableHeader:
if entering {
colIndex = 0
break
}
_, _ = r.w.Write([]byte("|\n"))
for i, align := range columnAligns {
_, _ = r.w.Write([]byte{'|'})
width := columnWidths[i]
left, right := tableHeaderColChar, tableHeaderColChar
switch align {
case extAST.AlignLeft:
left = tableHeaderAlignColChar
case extAST.AlignRight:
right = tableHeaderAlignColChar
case extAST.AlignCenter:
left, right = tableHeaderAlignColChar, tableHeaderAlignColChar
}
_, _ = r.w.Write(left)
_, _ = r.w.Write(bytes.Repeat(tableHeaderColChar, width))
_, _ = r.w.Write(right)
}
_, _ = r.w.Write([]byte("|"))
case *extAST.TableCell:
if !entering {
break
}
width := columnWidths[colIndex]
align := columnAligns[colIndex]
if tnode.Parent().Kind() == extAST.KindTableHeader {
align = extAST.AlignLeft
}
cellBuf.Reset()
if err := ast.Walk(tnode, r.mr.newRender(&cellBuf, r.source).renderNode); err != nil {
return ast.WalkStop, err
}
_, _ = r.w.Write([]byte("| "))
whitespaceWidth := width - runewidth.StringWidth(cellBuf.String())
switch align {
default:
fallthrough
case extAST.AlignLeft:
_, _ = r.w.Write(cellBuf.Bytes())
_, _ = r.w.Write(bytes.Repeat([]byte{' '}, 1+whitespaceWidth))
case extAST.AlignCenter:
first := whitespaceWidth / 2
_, _ = r.w.Write(bytes.Repeat([]byte{' '}, first))
_, _ = r.w.Write(cellBuf.Bytes())
_, _ = r.w.Write(bytes.Repeat([]byte{' '}, whitespaceWidth-first))
_, _ = r.w.Write([]byte{' '})
case extAST.AlignRight:
_, _ = r.w.Write(bytes.Repeat([]byte{' '}, whitespaceWidth))
_, _ = r.w.Write(cellBuf.Bytes())
_, _ = r.w.Write([]byte{' '})
}
colIndex++
return ast.WalkSkipChildren, nil
default:
return ast.WalkStop, errors.Errorf("detected unexpected tree type %s", tnode.Kind().String())
}
return ast.WalkContinue, nil
}); err != nil {
return err
}
}
return nil
}