1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package templates
18
19import (
20	"bytes"
21	"fmt"
22	"strings"
23
24	"github.com/russross/blackfriday"
25)
26
27const linebreak = "\n"
28
29// ASCIIRenderer implements blackfriday.Renderer
30var _ blackfriday.Renderer = &ASCIIRenderer{}
31
32// ASCIIRenderer is a blackfriday.Renderer intended for rendering markdown
33// documents as plain text, well suited for human reading on terminals.
34type ASCIIRenderer struct {
35	Indentation string
36
37	listItemCount uint
38	listLevel     uint
39}
40
41// NormalText gets a text chunk *after* the markdown syntax was already
42// processed and does a final cleanup on things we don't expect here, like
43// removing linebreaks on things that are not a paragraph break (auto unwrap).
44func (r *ASCIIRenderer) NormalText(out *bytes.Buffer, text []byte) {
45	raw := string(text)
46	lines := strings.Split(raw, linebreak)
47	for _, line := range lines {
48		trimmed := strings.Trim(line, " \n\t")
49		if len(trimmed) > 0 && trimmed[0] != '_' {
50			out.WriteString(" ")
51		}
52		out.WriteString(trimmed)
53	}
54}
55
56// List renders the start and end of a list.
57func (r *ASCIIRenderer) List(out *bytes.Buffer, text func() bool, flags int) {
58	r.listLevel++
59	out.WriteString(linebreak)
60	text()
61	r.listLevel--
62}
63
64// ListItem renders list items and supports both ordered and unordered lists.
65func (r *ASCIIRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
66	if flags&blackfriday.LIST_ITEM_BEGINNING_OF_LIST != 0 {
67		r.listItemCount = 1
68	} else {
69		r.listItemCount++
70	}
71	indent := strings.Repeat(r.Indentation, int(r.listLevel))
72	var bullet string
73	if flags&blackfriday.LIST_TYPE_ORDERED != 0 {
74		bullet += fmt.Sprintf("%d.", r.listItemCount)
75	} else {
76		bullet += "*"
77	}
78	out.WriteString(indent + bullet + " ")
79	r.fw(out, text)
80	out.WriteString(linebreak)
81}
82
83// Paragraph renders the start and end of a paragraph.
84func (r *ASCIIRenderer) Paragraph(out *bytes.Buffer, text func() bool) {
85	out.WriteString(linebreak)
86	text()
87	out.WriteString(linebreak)
88}
89
90// BlockCode renders a chunk of text that represents source code.
91func (r *ASCIIRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
92	out.WriteString(linebreak)
93	lines := []string{}
94	for _, line := range strings.Split(string(text), linebreak) {
95		indented := r.Indentation + line
96		lines = append(lines, indented)
97	}
98	out.WriteString(strings.Join(lines, linebreak))
99}
100
101func (r *ASCIIRenderer) GetFlags() int { return 0 }
102func (r *ASCIIRenderer) HRule(out *bytes.Buffer) {
103	out.WriteString(linebreak + "----------" + linebreak)
104}
105func (r *ASCIIRenderer) LineBreak(out *bytes.Buffer)                                      { out.WriteString(linebreak) }
106func (r *ASCIIRenderer) TitleBlock(out *bytes.Buffer, text []byte)                        { r.fw(out, text) }
107func (r *ASCIIRenderer) Header(out *bytes.Buffer, text func() bool, level int, id string) { text() }
108func (r *ASCIIRenderer) BlockHtml(out *bytes.Buffer, text []byte)                         { r.fw(out, text) }
109func (r *ASCIIRenderer) BlockQuote(out *bytes.Buffer, text []byte)                        { r.fw(out, text) }
110func (r *ASCIIRenderer) TableRow(out *bytes.Buffer, text []byte)                          { r.fw(out, text) }
111func (r *ASCIIRenderer) TableHeaderCell(out *bytes.Buffer, text []byte, align int)        { r.fw(out, text) }
112func (r *ASCIIRenderer) TableCell(out *bytes.Buffer, text []byte, align int)              { r.fw(out, text) }
113func (r *ASCIIRenderer) Footnotes(out *bytes.Buffer, text func() bool)                    { text() }
114func (r *ASCIIRenderer) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {
115	r.fw(out, text)
116}
117func (r *ASCIIRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int)         { r.fw(out, link) }
118func (r *ASCIIRenderer) CodeSpan(out *bytes.Buffer, text []byte)                   { r.fw(out, text) }
119func (r *ASCIIRenderer) DoubleEmphasis(out *bytes.Buffer, text []byte)             { r.fw(out, text) }
120func (r *ASCIIRenderer) Emphasis(out *bytes.Buffer, text []byte)                   { r.fw(out, text) }
121func (r *ASCIIRenderer) RawHtmlTag(out *bytes.Buffer, text []byte)                 { r.fw(out, text) }
122func (r *ASCIIRenderer) TripleEmphasis(out *bytes.Buffer, text []byte)             { r.fw(out, text) }
123func (r *ASCIIRenderer) StrikeThrough(out *bytes.Buffer, text []byte)              { r.fw(out, text) }
124func (r *ASCIIRenderer) FootnoteRef(out *bytes.Buffer, ref []byte, id int)         { r.fw(out, ref) }
125func (r *ASCIIRenderer) Entity(out *bytes.Buffer, entity []byte)                   { r.fw(out, entity) }
126func (r *ASCIIRenderer) Smartypants(out *bytes.Buffer, text []byte)                { r.fw(out, text) }
127func (r *ASCIIRenderer) DocumentHeader(out *bytes.Buffer)                          {}
128func (r *ASCIIRenderer) DocumentFooter(out *bytes.Buffer)                          {}
129func (r *ASCIIRenderer) TocHeaderWithAnchor(text []byte, level int, anchor string) {}
130func (r *ASCIIRenderer) TocHeader(text []byte, level int)                          {}
131func (r *ASCIIRenderer) TocFinalize()                                              {}
132
133func (r *ASCIIRenderer) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {
134	r.fw(out, header, body)
135}
136
137func (r *ASCIIRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
138	out.WriteString(" ")
139	r.fw(out, link)
140}
141
142func (r *ASCIIRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
143	r.fw(out, link)
144}
145
146func (r *ASCIIRenderer) fw(out *bytes.Buffer, text ...[]byte) {
147	for _, t := range text {
148		out.Write(t)
149	}
150}
151