Improve kubectl explain formatting-preservation

This commit is contained in:
Jordan Liggitt 2020-06-05 00:46:51 -04:00
parent eda662b2fd
commit a22b6400a1
3 changed files with 106 additions and 9 deletions

View File

@ -41,6 +41,7 @@ go_test(
deps = [
"//staging/src/k8s.io/apimachinery/pkg/api/meta/testrestmapper:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/kubectl/pkg/scheme:go_default_library",
"//staging/src/k8s.io/kubectl/pkg/util/openapi/testing:go_default_library",
],

View File

@ -19,6 +19,7 @@ package explain
import (
"fmt"
"io"
"regexp"
"strings"
)
@ -103,22 +104,75 @@ func (l *line) Add(word string) bool {
return false
}
var bullet = regexp.MustCompile(`^(\d+\.?|-|\*)\s`)
func shouldStartNewLine(lastWord, str string) bool {
// preserve line breaks ending in :
if strings.HasSuffix(lastWord, ":") {
return true
}
// preserve code blocks
if strings.HasPrefix(str, " ") {
return true
}
str = strings.TrimSpace(str)
// preserve empty lines
if len(str) == 0 {
return true
}
// preserve lines that look like they're starting lists
if bullet.MatchString(str) == true {
return true
}
// otherwise combine
return false
}
func wrapString(str string, wrap int) []string {
words := strings.Fields(str)
wrapped := []string{}
l := line{wrap: wrap}
for _, word := range words {
if !l.Add(word) {
// track the last word added to the current line
lastWord := ""
flush := func() {
if !l.Empty() {
lastWord = ""
wrapped = append(wrapped, l.String())
l = line{wrap: wrap}
}
}
// iterate over the lines in the original description
for _, str := range strings.Split(str, "\n") {
// preserve code blocks and blockquotes as-is
if strings.HasPrefix(str, " ") {
flush()
wrapped = append(wrapped, str)
continue
}
// preserve empty lines after the first line, since they can separate logical sections
if len(wrapped) > 0 && len(strings.TrimSpace(str)) == 0 {
flush()
wrapped = append(wrapped, "")
continue
}
// flush if we should start a new line
if shouldStartNewLine(lastWord, str) {
flush()
}
words := strings.Fields(str)
for _, word := range words {
lastWord = word
if !l.Add(word) {
panic("Couldn't add to empty line.")
flush()
if !l.Add(word) {
panic("Couldn't add to empty line.")
}
}
}
}
if !l.Empty() {
wrapped = append(wrapped, l.String())
}
flush()
return wrapped
}

View File

@ -19,6 +19,8 @@ package explain
import (
"bytes"
"testing"
"k8s.io/apimachinery/pkg/util/diff"
)
func TestFormatterWrite(t *testing.T) {
@ -54,6 +56,30 @@ func TestFormatterWrappedWrite(t *testing.T) {
f.Indent(10).WriteWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi at turpis faucibus, gravida dolor ut, fringilla velit.")
// Test long words (especially urls) on their own line.
f.Indent(20).WriteWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit. ThisIsAVeryLongWordThatDoesn'tFitOnALineOnItsOwn. Morbi at turpis faucibus, gravida dolor ut, fringilla velit.")
// Test content that includes newlines, bullet points, and blockquotes/code blocks
f.Indent(4).WriteWrapped(`
This is an
introductory paragraph
that should end
up on a continuous line.
Example:
Example text on its own line
List:
1. Item with
wrapping text
11. Another
item with wrapping text
* Bullet item
with wrapping text
- Dash item
with wrapping text
base64(
code goes here
and here
)`)
want := `Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Morbi at turpis faucibus, gravida dolor ut,
@ -68,10 +94,26 @@ fringilla velit.
Morbi at turpis faucibus,
gravida dolor ut, fringilla
velit.
This is an introductory paragraph that should
end up on a continuous line.
Example:
Example text on its own line
List:
1. Item with wrapping text
11. Another item with wrapping text
* Bullet item with wrapping text
- Dash item with wrapping text
base64(
code goes here
and here
)
`
if buf.String() != want {
t.Errorf("Got:\n%v\nWant:\n%v\n", buf.String(), want)
t.Errorf("Diff:\n%s", diff.StringDiff(buf.String(), want))
}
}