print.go

  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}