1package ast
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "strings"
8)
9
10// Print is for debugging. It prints a string representation of parsed
11// markdown doc (result of parser.Parse()) to dst.
12//
13// To make output readable, it shortens text output.
14func Print(dst io.Writer, doc Node) {
15 PrintWithPrefix(dst, doc, " ")
16}
17
18// PrintWithPrefix is like Print but allows customizing prefix used for
19// indentation. By default it's 2 spaces. You can change it to e.g. tab
20// by passing "\t"
21func PrintWithPrefix(w io.Writer, doc Node, prefix string) {
22 // for more compact output, don't print outer Document
23 if _, ok := doc.(*Document); ok {
24 for _, c := range doc.GetChildren() {
25 printRecur(w, c, prefix, 0)
26 }
27 } else {
28 printRecur(w, doc, prefix, 0)
29 }
30}
31
32// ToString is like Dump but returns result as a string
33func ToString(doc Node) string {
34 var buf bytes.Buffer
35 Print(&buf, doc)
36 return buf.String()
37}
38
39func contentToString(d1 []byte, d2 []byte) string {
40 if d1 != nil {
41 return string(d1)
42 }
43 if d2 != nil {
44 return string(d2)
45 }
46 return ""
47}
48
49func getContent(node Node) string {
50 if c := node.AsContainer(); c != nil {
51 return contentToString(c.Literal, c.Content)
52 }
53 leaf := node.AsLeaf()
54 return contentToString(leaf.Literal, leaf.Content)
55}
56
57func shortenString(s string, maxLen int) string {
58 // for cleaner, one-line ouput, replace some white-space chars
59 // with their escaped version
60 s = strings.Replace(s, "\n", `\n`, -1)
61 s = strings.Replace(s, "\r", `\r`, -1)
62 s = strings.Replace(s, "\t", `\t`, -1)
63 if maxLen < 0 {
64 return s
65 }
66 if len(s) < maxLen {
67 return s
68 }
69 // add "..." to indicate truncation
70 return s[:maxLen-3] + "..."
71}
72
73// get a short name of the type of v which excludes package name
74// and strips "()" from the end
75func getNodeType(node Node) string {
76 s := fmt.Sprintf("%T", node)
77 s = strings.TrimSuffix(s, "()")
78 if idx := strings.Index(s, "."); idx != -1 {
79 return s[idx+1:]
80 }
81 return s
82}
83
84func printDefault(w io.Writer, indent string, typeName string, content string) {
85 content = strings.TrimSpace(content)
86 if len(content) > 0 {
87 fmt.Fprintf(w, "%s%s '%s'\n", indent, typeName, content)
88 } else {
89 fmt.Fprintf(w, "%s%s\n", indent, typeName)
90 }
91}
92
93func getListFlags(f ListType) string {
94 var s string
95 if f&ListTypeOrdered != 0 {
96 s += "ordered "
97 }
98 if f&ListTypeDefinition != 0 {
99 s += "definition "
100 }
101 if f&ListTypeTerm != 0 {
102 s += "term "
103 }
104 if f&ListItemContainsBlock != 0 {
105 s += "has_block "
106 }
107 if f&ListItemBeginningOfList != 0 {
108 s += "start "
109 }
110 if f&ListItemEndOfList != 0 {
111 s += "end "
112 }
113 s = strings.TrimSpace(s)
114 return s
115}
116
117func printRecur(w io.Writer, node Node, prefix string, depth int) {
118 if node == nil {
119 return
120 }
121 indent := strings.Repeat(prefix, depth)
122
123 content := shortenString(getContent(node), 40)
124 typeName := getNodeType(node)
125 switch v := node.(type) {
126 case *Link:
127 content := "url=" + string(v.Destination)
128 printDefault(w, indent, typeName, content)
129 case *Image:
130 content := "url=" + string(v.Destination)
131 printDefault(w, indent, typeName, content)
132 case *List:
133 if v.Start > 1 {
134 content += fmt.Sprintf("start=%d ", v.Start)
135 }
136 if v.Tight {
137 content += "tight "
138 }
139 if v.IsFootnotesList {
140 content += "footnotes "
141 }
142 flags := getListFlags(v.ListFlags)
143 if len(flags) > 0 {
144 content += "flags=" + flags + " "
145 }
146 printDefault(w, indent, typeName, content)
147 case *ListItem:
148 if v.Tight {
149 content += "tight "
150 }
151 if v.IsFootnotesList {
152 content += "footnotes "
153 }
154 flags := getListFlags(v.ListFlags)
155 if len(flags) > 0 {
156 content += "flags=" + flags + " "
157 }
158 printDefault(w, indent, typeName, content)
159 default:
160 printDefault(w, indent, typeName, content)
161 }
162 for _, child := range node.GetChildren() {
163 printRecur(w, child, prefix, depth+1)
164 }
165}