Merge pull request #112731 from pacoxu/blackfriday-v2

upgrade Blackfriday to v2 and re-implement render
This commit is contained in:
Kubernetes Prow Robot
2022-09-29 08:44:53 -07:00
committed by GitHub
21 changed files with 151 additions and 5876 deletions

View File

@@ -23,7 +23,7 @@ require (
github.com/onsi/ginkgo/v2 v2.2.0
github.com/onsi/gomega v1.20.1
github.com/pkg/errors v0.9.1
github.com/russross/blackfriday v1.5.2
github.com/russross/blackfriday/v2 v2.1.0
github.com/spf13/cobra v1.5.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.0

View File

@@ -226,8 +226,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=

View File

@@ -17,11 +17,11 @@ limitations under the License.
package templates
import (
"bytes"
"fmt"
"io"
"strings"
"github.com/russross/blackfriday"
"github.com/russross/blackfriday/v2"
)
const linebreak = "\n"
@@ -38,113 +38,77 @@ type ASCIIRenderer struct {
listLevel uint
}
// NormalText gets a text chunk *after* the markdown syntax was already
// processed and does a final cleanup on things we don't expect here, like
// removing linebreaks on things that are not a paragraph break (auto unwrap).
func (r *ASCIIRenderer) NormalText(out *bytes.Buffer, text []byte) {
raw := string(text)
lines := strings.Split(raw, linebreak)
for _, line := range lines {
trimmed := strings.Trim(line, " \n\t")
if len(trimmed) > 0 && trimmed[0] != '_' {
out.WriteString(" ")
// render markdown to text
func (r *ASCIIRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
switch node.Type {
case blackfriday.Text:
raw := string(node.Literal)
lines := strings.Split(raw, linebreak)
for _, line := range lines {
trimmed := strings.Trim(line, " \n\t")
if len(trimmed) > 0 && trimmed[0] != '_' {
w.Write([]byte(" "))
}
w.Write([]byte(trimmed))
}
out.WriteString(trimmed)
case blackfriday.HorizontalRule, blackfriday.Hardbreak:
w.Write([]byte(linebreak + "----------" + linebreak))
case blackfriday.Code, blackfriday.CodeBlock:
w.Write([]byte(linebreak))
lines := []string{}
for _, line := range strings.Split(string(node.Literal), linebreak) {
trimmed := strings.Trim(line, " \t")
indented := r.Indentation + trimmed
lines = append(lines, indented)
}
w.Write([]byte(strings.Join(lines, linebreak)))
case blackfriday.Image:
w.Write(node.LinkData.Destination)
case blackfriday.Link:
w.Write([]byte(" "))
w.Write(node.LinkData.Destination)
case blackfriday.Paragraph:
if r.listLevel == 0 {
w.Write([]byte(linebreak))
}
case blackfriday.List:
if entering {
w.Write([]byte(linebreak))
r.listLevel++
} else {
r.listLevel--
r.listItemCount = 0
}
case blackfriday.Item:
if entering {
r.listItemCount++
for i := 0; uint(i) < r.listLevel; i++ {
w.Write([]byte(r.Indentation))
}
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
w.Write([]byte(fmt.Sprintf("%d. ", r.listItemCount)))
} else {
w.Write([]byte("* "))
}
} else {
w.Write([]byte(linebreak))
}
default:
normalText(w, node.Literal)
}
return blackfriday.GoToNext
}
// List renders the start and end of a list.
func (r *ASCIIRenderer) List(out *bytes.Buffer, text func() bool, flags int) {
r.listLevel++
out.WriteString(linebreak)
text()
r.listLevel--
func normalText(w io.Writer, text []byte) {
w.Write([]byte(strings.Trim(string(text), " \n\t")))
}
// ListItem renders list items and supports both ordered and unordered lists.
func (r *ASCIIRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
if flags&blackfriday.LIST_ITEM_BEGINNING_OF_LIST != 0 {
r.listItemCount = 1
} else {
r.listItemCount++
}
indent := strings.Repeat(r.Indentation, int(r.listLevel))
var bullet string
if flags&blackfriday.LIST_TYPE_ORDERED != 0 {
bullet += fmt.Sprintf("%d.", r.listItemCount)
} else {
bullet += "*"
}
out.WriteString(indent + bullet + " ")
r.fw(out, text)
out.WriteString(linebreak)
// RenderHeader writes document preamble and TOC if requested.
func (r *ASCIIRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) {
}
// Paragraph renders the start and end of a paragraph.
func (r *ASCIIRenderer) Paragraph(out *bytes.Buffer, text func() bool) {
out.WriteString(linebreak)
text()
out.WriteString(linebreak)
}
// BlockCode renders a chunk of text that represents source code.
func (r *ASCIIRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
out.WriteString(linebreak)
lines := []string{}
for _, line := range strings.Split(string(text), linebreak) {
indented := r.Indentation + line
lines = append(lines, indented)
}
out.WriteString(strings.Join(lines, linebreak))
}
func (r *ASCIIRenderer) GetFlags() int { return 0 }
func (r *ASCIIRenderer) HRule(out *bytes.Buffer) {
out.WriteString(linebreak + "----------" + linebreak)
}
func (r *ASCIIRenderer) LineBreak(out *bytes.Buffer) { out.WriteString(linebreak) }
func (r *ASCIIRenderer) TitleBlock(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) Header(out *bytes.Buffer, text func() bool, level int, id string) { text() }
func (r *ASCIIRenderer) BlockHtml(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) BlockQuote(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) TableRow(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) TableHeaderCell(out *bytes.Buffer, text []byte, align int) { r.fw(out, text) }
func (r *ASCIIRenderer) TableCell(out *bytes.Buffer, text []byte, align int) { r.fw(out, text) }
func (r *ASCIIRenderer) Footnotes(out *bytes.Buffer, text func() bool) { text() }
func (r *ASCIIRenderer) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {
r.fw(out, text)
}
func (r *ASCIIRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) { r.fw(out, link) }
func (r *ASCIIRenderer) CodeSpan(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) DoubleEmphasis(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) Emphasis(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) RawHtmlTag(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) TripleEmphasis(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) StrikeThrough(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) FootnoteRef(out *bytes.Buffer, ref []byte, id int) { r.fw(out, ref) }
func (r *ASCIIRenderer) Entity(out *bytes.Buffer, entity []byte) { r.fw(out, entity) }
func (r *ASCIIRenderer) Smartypants(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) DocumentHeader(out *bytes.Buffer) {}
func (r *ASCIIRenderer) DocumentFooter(out *bytes.Buffer) {}
func (r *ASCIIRenderer) TocHeaderWithAnchor(text []byte, level int, anchor string) {}
func (r *ASCIIRenderer) TocHeader(text []byte, level int) {}
func (r *ASCIIRenderer) TocFinalize() {}
func (r *ASCIIRenderer) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {
r.fw(out, header, body)
}
func (r *ASCIIRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
out.WriteString(" ")
r.fw(out, link)
}
func (r *ASCIIRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
r.fw(out, link)
}
func (r *ASCIIRenderer) fw(out *bytes.Buffer, text ...[]byte) {
for _, t := range text {
out.Write(t)
}
// RenderFooter writes document footer.
func (r *ASCIIRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) {
io.WriteString(w, "\n")
}

View File

@@ -20,7 +20,7 @@ import (
"strings"
"github.com/MakeNowJust/heredoc"
"github.com/russross/blackfriday"
"github.com/russross/blackfriday/v2"
"github.com/spf13/cobra"
)
@@ -70,7 +70,7 @@ type normalizer struct {
func (s normalizer) markdown() normalizer {
bytes := []byte(s.string)
formatted := blackfriday.Markdown(bytes, &ASCIIRenderer{Indentation: Indentation}, blackfriday.EXTENSION_NO_INTRA_EMPHASIS)
formatted := blackfriday.Run(bytes, blackfriday.WithExtensions(blackfriday.NoIntraEmphasis), blackfriday.WithRenderer(&ASCIIRenderer{Indentation: Indentation}))
s.string = string(formatted)
return s
}

View File

@@ -0,0 +1,79 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package templates
import (
"testing"
)
func TestLongDescMarkdown(t *testing.T) {
tests := []struct {
desc string
in string
out string
}{
{
desc: "Empty input produces empty output",
in: "",
out: "",
},
{
desc: "Single line text is preserved as is",
in: "Some text",
out: "Some text",
},
{
desc: "Consecutive new lines are combined into a single paragraph",
in: "Line1\nLine2",
out: "Line1 Line2",
},
{
desc: "Two paragraphs",
in: "Line1\n\nLine2",
out: "Line1\n\n Line2",
},
{
desc: "Leading and trailing spaces are stripped (single line)",
in: "\t \nThe text line \n \t",
out: "The text line",
},
{
desc: "Leading and trailing spaces are stripped (multi line)",
in: "\t \nLine1\nLine2 \n \t",
out: "Line1 Line2",
},
{
desc: "List Items with order",
in: "Title\n\n1. First item\n2. Second item\n\nSome text",
out: "Title\n\n 1. First item\n 2. Second item\n\n Some text",
},
{
desc: "Multi lines without order",
in: "\t\t\t\t\tDescriptions.\n\n * Item.\n * Item2.",
out: "Descriptions.\n \n * Item.\n * Item2.",
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
got := LongDesc(test.in)
if got != test.out {
t.Errorf("expected(%d):\n%s\n=====\ngot(%d):\n%s\n", len(test.out), test.out, len(got), got)
}
})
}
}