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