Skip to content

Commit 0281b85

Browse files
authored
Merge pull request kubernetes#91801 from liggitt/kubectl-explain
Improve kubectl explain formatting-preservation
2 parents 4e3dea8 + a22b640 commit 0281b85

File tree

3 files changed

+106
-9
lines changed

3 files changed

+106
-9
lines changed

staging/src/k8s.io/kubectl/pkg/explain/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ go_test(
4141
deps = [
4242
"//staging/src/k8s.io/apimachinery/pkg/api/meta/testrestmapper:go_default_library",
4343
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
44+
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
4445
"//staging/src/k8s.io/kubectl/pkg/scheme:go_default_library",
4546
"//staging/src/k8s.io/kubectl/pkg/util/openapi/testing:go_default_library",
4647
],

staging/src/k8s.io/kubectl/pkg/explain/formatter.go

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package explain
1919
import (
2020
"fmt"
2121
"io"
22+
"regexp"
2223
"strings"
2324
)
2425

@@ -103,22 +104,75 @@ func (l *line) Add(word string) bool {
103104
return false
104105
}
105106

107+
var bullet = regexp.MustCompile(`^(\d+\.?|-|\*)\s`)
108+
109+
func shouldStartNewLine(lastWord, str string) bool {
110+
// preserve line breaks ending in :
111+
if strings.HasSuffix(lastWord, ":") {
112+
return true
113+
}
114+
115+
// preserve code blocks
116+
if strings.HasPrefix(str, " ") {
117+
return true
118+
}
119+
str = strings.TrimSpace(str)
120+
// preserve empty lines
121+
if len(str) == 0 {
122+
return true
123+
}
124+
// preserve lines that look like they're starting lists
125+
if bullet.MatchString(str) == true {
126+
return true
127+
}
128+
// otherwise combine
129+
return false
130+
}
131+
106132
func wrapString(str string, wrap int) []string {
107-
words := strings.Fields(str)
108133
wrapped := []string{}
109134
l := line{wrap: wrap}
110-
111-
for _, word := range words {
112-
if !l.Add(word) {
135+
// track the last word added to the current line
136+
lastWord := ""
137+
flush := func() {
138+
if !l.Empty() {
139+
lastWord = ""
113140
wrapped = append(wrapped, l.String())
114141
l = line{wrap: wrap}
142+
}
143+
}
144+
145+
// iterate over the lines in the original description
146+
for _, str := range strings.Split(str, "\n") {
147+
// preserve code blocks and blockquotes as-is
148+
if strings.HasPrefix(str, " ") {
149+
flush()
150+
wrapped = append(wrapped, str)
151+
continue
152+
}
153+
154+
// preserve empty lines after the first line, since they can separate logical sections
155+
if len(wrapped) > 0 && len(strings.TrimSpace(str)) == 0 {
156+
flush()
157+
wrapped = append(wrapped, "")
158+
continue
159+
}
160+
161+
// flush if we should start a new line
162+
if shouldStartNewLine(lastWord, str) {
163+
flush()
164+
}
165+
words := strings.Fields(str)
166+
for _, word := range words {
167+
lastWord = word
115168
if !l.Add(word) {
116-
panic("Couldn't add to empty line.")
169+
flush()
170+
if !l.Add(word) {
171+
panic("Couldn't add to empty line.")
172+
}
117173
}
118174
}
119175
}
120-
if !l.Empty() {
121-
wrapped = append(wrapped, l.String())
122-
}
176+
flush()
123177
return wrapped
124178
}

staging/src/k8s.io/kubectl/pkg/explain/formatter_test.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package explain
1919
import (
2020
"bytes"
2121
"testing"
22+
23+
"k8s.io/apimachinery/pkg/util/diff"
2224
)
2325

2426
func TestFormatterWrite(t *testing.T) {
@@ -54,6 +56,30 @@ func TestFormatterWrappedWrite(t *testing.T) {
5456
f.Indent(10).WriteWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi at turpis faucibus, gravida dolor ut, fringilla velit.")
5557
// Test long words (especially urls) on their own line.
5658
f.Indent(20).WriteWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit. ThisIsAVeryLongWordThatDoesn'tFitOnALineOnItsOwn. Morbi at turpis faucibus, gravida dolor ut, fringilla velit.")
59+
// Test content that includes newlines, bullet points, and blockquotes/code blocks
60+
f.Indent(4).WriteWrapped(`
61+
This is an
62+
introductory paragraph
63+
that should end
64+
up on a continuous line.
65+
66+
Example:
67+
Example text on its own line
68+
69+
List:
70+
1. Item with
71+
wrapping text
72+
11. Another
73+
item with wrapping text
74+
* Bullet item
75+
with wrapping text
76+
- Dash item
77+
with wrapping text
78+
79+
base64(
80+
code goes here
81+
and here
82+
)`)
5783

5884
want := `Lorem ipsum dolor sit amet, consectetur adipiscing
5985
elit. Morbi at turpis faucibus, gravida dolor ut,
@@ -68,10 +94,26 @@ fringilla velit.
6894
Morbi at turpis faucibus,
6995
gravida dolor ut, fringilla
7096
velit.
97+
This is an introductory paragraph that should
98+
end up on a continuous line.
99+
100+
Example:
101+
Example text on its own line
102+
103+
List:
104+
1. Item with wrapping text
105+
11. Another item with wrapping text
106+
* Bullet item with wrapping text
107+
- Dash item with wrapping text
108+
109+
base64(
110+
code goes here
111+
and here
112+
)
71113
`
72114

73115
if buf.String() != want {
74-
t.Errorf("Got:\n%v\nWant:\n%v\n", buf.String(), want)
116+
t.Errorf("Diff:\n%s", diff.StringDiff(buf.String(), want))
75117
}
76118
}
77119

0 commit comments

Comments
 (0)