123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- package ast
- import (
- "bytes"
- "fmt"
- "io"
- "strings"
- "unicode/utf8"
- )
- // Print is for debugging. It prints a string representation of parsed
- // markdown doc (result of parser.Parse()) to dst.
- //
- // To make output readable, it shortens text output.
- func Print(dst io.Writer, doc Node) {
- PrintWithPrefix(dst, doc, " ")
- }
- // PrintWithPrefix is like Print but allows customizing prefix used for
- // indentation. By default it's 2 spaces. You can change it to e.g. tab
- // by passing "\t"
- func PrintWithPrefix(w io.Writer, doc Node, prefix string) {
- // for more compact output, don't print outer Document
- if _, ok := doc.(*Document); ok {
- for _, c := range doc.GetChildren() {
- printRecur(w, c, prefix, 0)
- }
- } else {
- printRecur(w, doc, prefix, 0)
- }
- }
- // ToString is like Dump but returns result as a string
- func ToString(doc Node) string {
- var buf bytes.Buffer
- Print(&buf, doc)
- return buf.String()
- }
- func contentToString(d1 []byte, d2 []byte) string {
- if d1 != nil {
- return string(d1)
- }
- if d2 != nil {
- return string(d2)
- }
- return ""
- }
- func getContent(node Node) string {
- if c := node.AsContainer(); c != nil {
- return contentToString(c.Literal, c.Content)
- }
- leaf := node.AsLeaf()
- return contentToString(leaf.Literal, leaf.Content)
- }
- func shortenString(s string, maxLen int) string {
- // for cleaner, one-line ouput, replace some white-space chars
- // with their escaped version
- s = strings.Replace(s, "\n", `\n`, -1)
- s = strings.Replace(s, "\r", `\r`, -1)
- s = strings.Replace(s, "\t", `\t`, -1)
- if maxLen < 0 {
- return s
- }
- if utf8.RuneCountInString(s) < maxLen {
- return s
- }
- // add "…" to indicate truncation
- return string(append([]rune(s)[:maxLen-3], '…'))
- }
- // get a short name of the type of v which excludes package name
- // and strips "()" from the end
- func getNodeType(node Node) string {
- s := fmt.Sprintf("%T", node)
- s = strings.TrimSuffix(s, "()")
- if idx := strings.Index(s, "."); idx != -1 {
- return s[idx+1:]
- }
- return s
- }
- func printDefault(w io.Writer, indent string, typeName string, content string) {
- content = strings.TrimSpace(content)
- if len(content) > 0 {
- fmt.Fprintf(w, "%s%s '%s'\n", indent, typeName, content)
- } else {
- fmt.Fprintf(w, "%s%s\n", indent, typeName)
- }
- }
- func getListFlags(f ListType) string {
- var s string
- if f&ListTypeOrdered != 0 {
- s += "ordered "
- }
- if f&ListTypeDefinition != 0 {
- s += "definition "
- }
- if f&ListTypeTerm != 0 {
- s += "term "
- }
- if f&ListItemContainsBlock != 0 {
- s += "has_block "
- }
- if f&ListItemBeginningOfList != 0 {
- s += "start "
- }
- if f&ListItemEndOfList != 0 {
- s += "end "
- }
- s = strings.TrimSpace(s)
- return s
- }
- func printRecur(w io.Writer, node Node, prefix string, depth int) {
- if node == nil {
- return
- }
- indent := strings.Repeat(prefix, depth)
- content := shortenString(getContent(node), 40)
- typeName := getNodeType(node)
- switch v := node.(type) {
- case *Link:
- content := "url=" + string(v.Destination)
- printDefault(w, indent, typeName, content)
- case *Image:
- content := "url=" + string(v.Destination)
- printDefault(w, indent, typeName, content)
- case *List:
- if v.Start > 1 {
- content += fmt.Sprintf("start=%d ", v.Start)
- }
- if v.Tight {
- content += "tight "
- }
- if v.IsFootnotesList {
- content += "footnotes "
- }
- flags := getListFlags(v.ListFlags)
- if len(flags) > 0 {
- content += "flags=" + flags + " "
- }
- printDefault(w, indent, typeName, content)
- case *ListItem:
- if v.Tight {
- content += "tight "
- }
- if v.IsFootnotesList {
- content += "footnotes "
- }
- flags := getListFlags(v.ListFlags)
- if len(flags) > 0 {
- content += "flags=" + flags + " "
- }
- printDefault(w, indent, typeName, content)
- case *CodeBlock:
- printDefault(w, indent, typeName + ":" + string(v.Info), content)
- default:
- printDefault(w, indent, typeName, content)
- }
- for _, child := range node.GetChildren() {
- printRecur(w, child, prefix, depth+1)
- }
- }
|