Skip to content

Commit 758aeaf

Browse files
committed
Add line wrap logic to box code
It can happen that the text to be displayed in the content box overflows the terminal width and therefore is difficult to read. Add line wrap logic to box code, which is enabled by default. Add option to disable line wrap.
1 parent 84cc42d commit 758aeaf

File tree

3 files changed

+98
-4
lines changed

3 files changed

+98
-4
lines changed

box.go

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"strings"
2929

3030
"github.com/gonvenience/bunt"
31+
"github.com/gonvenience/term"
3132
colorful "github.com/lucasb-eyer/go-colorful"
3233
)
3334

@@ -39,6 +40,7 @@ type boxOptions struct {
3940
contentColor *colorful.Color
4041
headlineStyles []bunt.StyleOption
4142
noClosingEndOfLine bool
43+
noLineWrap bool
4244
}
4345

4446
// HeadlineColor sets the color of the headline text
@@ -69,6 +71,13 @@ func NoFinalEndOfLine() BoxStyle {
6971
}
7072
}
7173

74+
// NoLineWrap disables line wrapping in the content box
75+
func NoLineWrap() BoxStyle {
76+
return func(options *boxOptions) {
77+
options.noLineWrap = true
78+
}
79+
}
80+
7281
// ContentBox creates a string for the terminal where content is printed inside
7382
// a simple box shape.
7483
func ContentBox(headline string, content string, opts ...BoxStyle) string {
@@ -107,20 +116,55 @@ func Box(out io.Writer, headline string, content io.Reader, opts ...BoxStyle) {
107116
headline = bunt.Style(headline, style)
108117
}
109118

119+
var processText = func(text string) []string {
120+
if options.noLineWrap {
121+
return []string{text}
122+
}
123+
124+
words := strings.Fields(strings.TrimSpace(text))
125+
if len(words) == 0 {
126+
return []string{text}
127+
}
128+
129+
var (
130+
buf bytes.Buffer
131+
lines = []string{}
132+
lineWidth = term.GetTerminalWidth() - len(prefix)
133+
)
134+
135+
buf.WriteString(words[0])
136+
for _, word := range words[1:] {
137+
if len(word)+1 > lineWidth-buf.Len() {
138+
lines = append(lines, buf.String())
139+
buf.Reset()
140+
buf.WriteString(word)
141+
142+
} else {
143+
fmt.Fprint(&buf, " ", word)
144+
}
145+
}
146+
147+
return append(lines, buf.String())
148+
}
149+
110150
// Process each line of the content and apply styles if necessary
111151
scanner := bufio.NewScanner(content)
112152
for scanner.Scan() {
113153
text := scanner.Text()
114-
if options.contentColor != nil {
115-
text = bunt.Style(text, bunt.Foreground(*options.contentColor))
116-
}
117154

118155
if !linewritten {
119156
// Write the headline string including the corner item
120157
fmt.Fprintf(out, "%s %s\n", beginning, headline)
121158
}
122159

123-
fmt.Fprintf(out, "%s %s\n", prefix, text)
160+
for _, line := range processText(text) {
161+
if options.contentColor != nil {
162+
line = bunt.Style(line, bunt.Foreground(*options.contentColor))
163+
}
164+
165+
fmt.Fprintf(out, "%s %s\n", prefix, line)
166+
}
167+
124168
linewritten = true
125169
}
126170

box_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929

3030
. "github.com/gonvenience/bunt"
3131
. "github.com/gonvenience/neat"
32+
. "github.com/gonvenience/term"
3233
)
3334

3435
var _ = Describe("content box", func() {
@@ -219,4 +220,52 @@ DimGray{╵}
219220
Expect(len(buf.String())).To(BeIdenticalTo(0))
220221
})
221222
})
223+
224+
Context("using line wrap", func() {
225+
var tmp int
226+
227+
BeforeEach(func() {
228+
tmp = FixedTerminalWidth
229+
FixedTerminalWidth = 80
230+
})
231+
232+
AfterEach(func() {
233+
FixedTerminalWidth = tmp
234+
})
235+
236+
It("should wrap lines that are too long", func() {
237+
Expect("\n" + ContentBox(
238+
"headline",
239+
"content with a very long first line, that is most likely an error message with a lot of context or similar",
240+
)).To(BeEquivalentTo(Sprintf(`
241+
╭ headline
242+
│ content with a very long first line, that is most likely an error message
243+
│ with a lot of context or similar
244+
245+
`)))
246+
})
247+
248+
It("should not wrap long lines if wrapping is disabled", func() {
249+
Expect("\n" + ContentBox(
250+
"headline",
251+
"content with a very long first line, that is most likely an error message with a lot of context or similar",
252+
NoLineWrap(),
253+
)).To(BeEquivalentTo(Sprintf(`
254+
╭ headline
255+
│ content with a very long first line, that is most likely an error message with a lot of context or similar
256+
257+
`)))
258+
})
259+
260+
It("should not fail with empty lines", func() {
261+
Expect("\n" + ContentBox(
262+
"headline",
263+
" ",
264+
)).To(BeEquivalentTo(Sprintf(`
265+
╭ headline
266+
267+
268+
`)))
269+
})
270+
})
222271
})

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.12
44

55
require (
66
github.com/gonvenience/bunt v1.1.1
7+
github.com/gonvenience/term v1.0.0
78
github.com/gonvenience/wrap v1.1.0
89
github.com/lucasb-eyer/go-colorful v1.0.3
910
github.com/onsi/ginkgo v1.12.0

0 commit comments

Comments
 (0)