Skip to content

Commit 1a14f77

Browse files
authored
Merge pull request #3 from bbrks/customisable-breakpoints
Improve API. Bumping to v2.0. Resolves #1
2 parents 418c82e + 0f63161 commit 1a14f77

File tree

2 files changed

+121
-80
lines changed

2 files changed

+121
-80
lines changed

wrap.go

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,53 @@ package wrap
33
import "strings"
44

55
const (
6-
// breakpoints defines which characters should be able to break a line.
7-
breakpoints = " "
6+
defaultBreakpoints = " -"
87
)
98

10-
// Line will wrap a single line of text at the given length.
9+
// Wrapper contains settings for customisable word-wrapping.
10+
type Wrapper struct {
11+
// Breakpoints defines which characters should be able to break a line.
12+
// By default, this follows the usual English rules of spaces, and hyphens.
13+
// Default: " -"
14+
Breakpoints string
15+
}
16+
17+
// NewWrapper returns a new instance of a Wrapper initialised with defaults.
18+
func NewWrapper() Wrapper {
19+
return Wrapper{
20+
Breakpoints: defaultBreakpoints,
21+
}
22+
}
23+
24+
// line will wrap a single line of text at the given length.
1125
// If limit is less than 1, the string remains unchanged.
12-
//
13-
// If a word is longer than the given limit, it will not be broken to fit.
14-
// See the examples for this scenario.
15-
func Line(s string, limit int) string {
26+
func (w Wrapper) line(s string, limit int) string {
1627
if limit < 1 || len(s) < limit {
1728
return s
1829
}
1930

2031
// Find the index of the last breakpoint within the limit.
21-
i := strings.LastIndexAny(s[:limit], breakpoints)
32+
i := strings.LastIndexAny(s[:limit], w.Breakpoints)
2233

2334
// Can't wrap within the limit, wrap at the next breakpoint instead.
2435
if i < 0 {
25-
i = strings.IndexAny(s, breakpoints)
36+
i = strings.IndexAny(s, w.Breakpoints)
2637
// Nothing left to do!
2738
if i < 0 {
2839
return s
2940
}
3041
}
3142

3243
// Recurse until we have nothing left to do.
33-
return s[:i] + "\n" + Line(s[i+1:], limit)
44+
return s[:i] + "\n" + w.line(s[i+1:], limit)
3445
}
3546

36-
// LineWithPrefix will wrap a single line of text and prepend the given prefix,
37-
// whilst staying within given limits.
38-
func LineWithPrefix(s, prefix string, limit int) string {
47+
// Wrap will wrap one or more lines of text at the given length.
48+
// If limit is less than 1, the string remains unchanged.
49+
func (w Wrapper) Wrap(s string, limit int) string {
3950
var ret string
40-
for _, str := range strings.Split(Line(s, limit-len(prefix)), "\n") {
41-
ret += prefix + str + "\n"
51+
for _, str := range strings.Split(s, "\n") {
52+
ret += w.line(str, limit) + "\n"
4253
}
4354
return ret
4455
}

wrap_test.go

Lines changed: 95 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
package wrap
1+
package wrap_test
22

33
import (
44
"fmt"
55
"strings"
66
"testing"
77

8+
"github.com/bbrks/wrap"
89
"github.com/stretchr/testify/assert"
910
)
1011

12+
var w = wrap.NewWrapper()
13+
1114
// tests contains various line lengths to test our wrap functions.
1215
var tests = []int{-5, 0, 5, 10, 25, 80, 120, 500}
1316

@@ -20,13 +23,13 @@ var loremIpsums = []string{
2023
"Duis ac ornare erat. Nulla in odio eget ante tristique dignissim a non erat. Sed non nisi vitae arcu dapibus porta vitae dignissim ante. Cras et fringilla turpis. Maecenas arcu nibh, tempus euismod pretium eget, hendrerit vitae arcu. Sed vel dolor quam. Etiam consequat sed dolor ut elementum. Quisque dictum tempor pretium. Sed eu sollicitudin mi, in commodo ante.",
2124
}
2225

23-
func TestLine(t *testing.T) {
26+
func TestWrapper_Wrap(t *testing.T) {
2427
// Test multiple line lengths.
2528
for _, l := range tests {
2629

2730
// Test each input line individually.
2831
for _, s := range loremIpsums {
29-
wrapped := Line(s, l)
32+
wrapped := w.Wrap(s, l)
3033

3134
// Assert that each output line is no longer than the limit.
3235
for _, v := range strings.Split(wrapped, "\n") {
@@ -51,75 +54,26 @@ func TestLine(t *testing.T) {
5154
}
5255
}
5356

54-
func TestLineWithPrefix(t *testing.T) {
55-
var prefix = "// "
56-
// Test multiple line lengths.
57-
for _, l := range tests {
58-
59-
// Test each input line individually.
60-
for _, s := range loremIpsums {
61-
wrapped := LineWithPrefix(s, prefix, l)
62-
63-
// Assert that each output line is no longer than the limit.
64-
for _, v := range strings.Split(wrapped, "\n") {
65-
if !strings.HasPrefix(s, prefix) {
66-
continue
67-
}
68-
69-
// Only check lines which contain more than one word.
70-
if !strings.Contains(v, " ") {
71-
continue
72-
}
73-
74-
// If length < 1, the string remains unchaged.
75-
if l < 1 {
76-
assert.Equal(t, prefix+s, v)
77-
continue
78-
}
79-
80-
assert.True(t, len(v) <= l,
81-
fmt.Sprintf("Line length greater than %d: %s", l, v))
82-
}
83-
84-
}
85-
86-
}
87-
}
88-
89-
func benchmarkLine(b *testing.B, limit int) {
57+
func benchmarkWrap(b *testing.B, limit int) {
9058
b.ReportAllocs()
9159
for i := 0; i < b.N; i++ {
92-
Line(loremIpsums[0], limit)
60+
w.Wrap(loremIpsums[0], limit)
9361
}
9462
}
9563

96-
func BenchmarkLine0(b *testing.B) { benchmarkLine(b, 0) }
97-
func BenchmarkLine5(b *testing.B) { benchmarkLine(b, 5) }
98-
func BenchmarkLine10(b *testing.B) { benchmarkLine(b, 10) }
99-
func BenchmarkLine25(b *testing.B) { benchmarkLine(b, 25) }
100-
func BenchmarkLine80(b *testing.B) { benchmarkLine(b, 80) }
101-
func BenchmarkLine120(b *testing.B) { benchmarkLine(b, 120) }
102-
func BenchmarkLine500(b *testing.B) { benchmarkLine(b, 500) }
64+
func BenchmarkWrap0(b *testing.B) { benchmarkWrap(b, 0) }
65+
func BenchmarkWrap5(b *testing.B) { benchmarkWrap(b, 5) }
66+
func BenchmarkWrap10(b *testing.B) { benchmarkWrap(b, 10) }
67+
func BenchmarkWrap25(b *testing.B) { benchmarkWrap(b, 25) }
68+
func BenchmarkWrap80(b *testing.B) { benchmarkWrap(b, 80) }
69+
func BenchmarkWrap120(b *testing.B) { benchmarkWrap(b, 120) }
70+
func BenchmarkWrap500(b *testing.B) { benchmarkWrap(b, 500) }
10371

104-
func ExampleLineWithPrefix() {
105-
var loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vulputate quam nibh, et faucibus enim gravida vel. Integer bibendum lectus et erat semper fermentum quis a risus. Fusce dignissim tempus metus non pretium. Nunc sagittis magna nec purus porttitor mollis. Pellentesque feugiat quam eget laoreet aliquet. Donec gravida congue massa, et sollicitudin turpis lacinia a. Fusce non tortor magna. Cras vel finibus tellus."
106-
107-
// Wrap when lines exceed 80 chars and prepend a comment prefix.
108-
fmt.Println(LineWithPrefix(loremIpsum, "// ", 80))
109-
// Output:
110-
// // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vulputate quam
111-
// // nibh, et faucibus enim gravida vel. Integer bibendum lectus et erat semper
112-
// // fermentum quis a risus. Fusce dignissim tempus metus non pretium. Nunc
113-
// // sagittis magna nec purus porttitor mollis. Pellentesque feugiat quam eget
114-
// // laoreet aliquet. Donec gravida congue massa, et sollicitudin turpis lacinia
115-
// // a. Fusce non tortor magna. Cras vel finibus tellus.
116-
}
117-
118-
func ExampleLine() {
72+
func ExampleWrapper_Wrap() {
11973
var loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vulputate quam nibh, et faucibus enim gravida vel. Integer bibendum lectus et erat semper fermentum quis a risus. Fusce dignissim tempus metus non pretium. Nunc sagittis magna nec purus porttitor mollis. Pellentesque feugiat quam eget laoreet aliquet. Donec gravida congue massa, et sollicitudin turpis lacinia a. Fusce non tortor magna. Cras vel finibus tellus."
12074

12175
// Wrap when lines exceed 80 chars.
122-
fmt.Println(Line(loremIpsum, 80))
76+
fmt.Println(w.Wrap(loremIpsum, 80))
12377
// Output:
12478
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vulputate quam
12579
// nibh, et faucibus enim gravida vel. Integer bibendum lectus et erat semper
@@ -129,11 +83,71 @@ func ExampleLine() {
12983
// non tortor magna. Cras vel finibus tellus.
13084
}
13185

132-
func ExampleLine_short() {
86+
func ExampleWrapper_Wrap_paragraphs() {
87+
var loremIpsum = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. In pulvinar augue vel dui gravida, sed convallis ante aliquam. Morbi euismod felis in justo lobortis, eu egestas quam cursus. Ut ut tellus mattis, porttitor leo ut, porttitor ex. Nulla suscipit molestie ligula, quis porta nulla pellentesque ac. Cras ut vestibulum orci. Phasellus ante nisl, dignissim non nunc eget, dapibus convallis orci. Integer vel euismod mauris. Integer tortor elit, vestibulum eget augue vitae, vehicula commodo sapien. Integer iaculis maximus dui, vitae rutrum magna congue at. Praesent varius quam vitae rhoncus fringilla. Quisque ac ex sit amet enim aliquam rutrum in in tortor. Sed sit amet est finibus, congue purus et, ultrices quam. Aenean felis velit, ullamcorper at sagittis ut, aliquam eu mauris.
88+
89+
Phasellus vel lorem venenatis, condimentum risus quis, ultricies risus. Vivamus porttitor lorem sit amet bibendum congue. Mauris sem enim, rutrum in ipsum eget, porttitor placerat diam. Pellentesque ut pharetra augue. Maecenas in ante eget ex efficitur tincidunt. Cras ut ultrices nisl. Donec tristique tincidunt eros condimentum tempus. Morbi libero urna, pretium id turpis vel, cursus efficitur orci. Mauris ut elit felis. Duis ultrices nisl eget accumsan consectetur. Nullam blandit elit vel vulputate scelerisque. Nulla facilisi. Cras quis maximus odio. Nam orci est, tempor ac arcu eget, tincidunt consectetur risus. Donec quis faucibus velit.
90+
91+
Maecenas rhoncus semper nisi non luctus. Nam accumsan malesuada urna vel vehicula. Nullam quis dui in augue tristique sollicitudin. Praesent vulputate condimentum vestibulum. Morbi tincidunt consectetur velit non accumsan. Praesent sit amet vestibulum purus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla rhoncus urna ut aliquet congue. Sed ornare dignissim orci non imperdiet. Maecenas nec magna bibendum, cursus nisi ac, commodo arcu.
92+
93+
Sed auctor id leo at molestie. Donec sed cursus massa. Morbi semper lobortis dui. Sed mattis sem a molestie sodales. Cras consequat sapien semper, pretium nulla a, dignissim massa. Aliquam non ornare lacus. Cras gravida lorem tellus, et consectetur ante sodales ut.
94+
95+
Nunc mi enim, aliquam quis bibendum sed, commodo quis nulla. Aliquam vulputate arcu a volutpat semper. Donec nec mauris eros. Suspendisse velit ante, fermentum a odio non, porta dignissim nunc. Vestibulum condimentum at massa at malesuada. Etiam augue purus, interdum a est pretium, cursus interdum eros. Vestibulum et ligula dignissim, suscipit arcu et, congue sem. Integer posuere mauris id scelerisque sagittis. Proin cursus congue sem, nec pulvinar neque auctor eget. Suspendisse vitae mi ipsum. Nullam sed mauris posuere, accumsan ligula vitae, viverra tellus. Morbi quam turpis, sagittis vitae arcu vel, tempus sagittis neque. Vivamus dolor purus, blandit ac condimentum a, interdum in ipsum.`
96+
97+
fmt.Println(w.Wrap(loremIpsum, 80))
98+
// Output:
99+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. In pulvinar augue vel
100+
// dui gravida, sed convallis ante aliquam. Morbi euismod felis in justo lobortis,
101+
// eu egestas quam cursus. Ut ut tellus mattis, porttitor leo ut, porttitor ex.
102+
// Nulla suscipit molestie ligula, quis porta nulla pellentesque ac. Cras ut
103+
// vestibulum orci. Phasellus ante nisl, dignissim non nunc eget, dapibus
104+
// convallis orci. Integer vel euismod mauris. Integer tortor elit, vestibulum
105+
// eget augue vitae, vehicula commodo sapien. Integer iaculis maximus dui, vitae
106+
// rutrum magna congue at. Praesent varius quam vitae rhoncus fringilla. Quisque
107+
// ac ex sit amet enim aliquam rutrum in in tortor. Sed sit amet est finibus,
108+
// congue purus et, ultrices quam. Aenean felis velit, ullamcorper at sagittis ut,
109+
// aliquam eu mauris.
110+
//
111+
// Phasellus vel lorem venenatis, condimentum risus quis, ultricies risus. Vivamus
112+
// porttitor lorem sit amet bibendum congue. Mauris sem enim, rutrum in ipsum
113+
// eget, porttitor placerat diam. Pellentesque ut pharetra augue. Maecenas in ante
114+
// eget ex efficitur tincidunt. Cras ut ultrices nisl. Donec tristique tincidunt
115+
// eros condimentum tempus. Morbi libero urna, pretium id turpis vel, cursus
116+
// efficitur orci. Mauris ut elit felis. Duis ultrices nisl eget accumsan
117+
// consectetur. Nullam blandit elit vel vulputate scelerisque. Nulla facilisi.
118+
// Cras quis maximus odio. Nam orci est, tempor ac arcu eget, tincidunt
119+
// consectetur risus. Donec quis faucibus velit.
120+
//
121+
// Maecenas rhoncus semper nisi non luctus. Nam accumsan malesuada urna vel
122+
// vehicula. Nullam quis dui in augue tristique sollicitudin. Praesent vulputate
123+
// condimentum vestibulum. Morbi tincidunt consectetur velit non accumsan.
124+
// Praesent sit amet vestibulum purus. Orci varius natoque penatibus et magnis dis
125+
// parturient montes, nascetur ridiculus mus. Nulla rhoncus urna ut aliquet
126+
// congue. Sed ornare dignissim orci non imperdiet. Maecenas nec magna bibendum,
127+
// cursus nisi ac, commodo arcu.
128+
//
129+
// Sed auctor id leo at molestie. Donec sed cursus massa. Morbi semper lobortis
130+
// dui. Sed mattis sem a molestie sodales. Cras consequat sapien semper, pretium
131+
// nulla a, dignissim massa. Aliquam non ornare lacus. Cras gravida lorem tellus,
132+
// et consectetur ante sodales ut.
133+
//
134+
// Nunc mi enim, aliquam quis bibendum sed, commodo quis nulla. Aliquam vulputate
135+
// arcu a volutpat semper. Donec nec mauris eros. Suspendisse velit ante,
136+
// fermentum a odio non, porta dignissim nunc. Vestibulum condimentum at massa at
137+
// malesuada. Etiam augue purus, interdum a est pretium, cursus interdum eros.
138+
// Vestibulum et ligula dignissim, suscipit arcu et, congue sem. Integer posuere
139+
// mauris id scelerisque sagittis. Proin cursus congue sem, nec pulvinar neque
140+
// auctor eget. Suspendisse vitae mi ipsum. Nullam sed mauris posuere, accumsan
141+
// ligula vitae, viverra tellus. Morbi quam turpis, sagittis vitae arcu vel,
142+
// tempus sagittis neque. Vivamus dolor purus, blandit ac condimentum a, interdum
143+
// in ipsum.
144+
}
145+
146+
func ExampleWrapper_Wrap_short() {
133147
var loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vulputate quam nibh, et faucibus enim gravida vel. Integer bibendum lectus et erat semper fermentum quis a risus. Fusce dignissim tempus metus non pretium. Nunc sagittis magna nec purus porttitor mollis. Pellentesque feugiat quam eget laoreet aliquet. Donec gravida congue massa, et sollicitudin turpis lacinia a. Fusce non tortor magna. Cras vel finibus tellus."
134148

135149
// Wrap on every word.
136-
fmt.Println(Line(loremIpsum, 1))
150+
fmt.Println(w.Wrap(loremIpsum, 1))
137151
// Output:
138152
// Lorem
139153
// ipsum
@@ -199,3 +213,19 @@ func ExampleLine_short() {
199213
// finibus
200214
// tellus.
201215
}
216+
217+
func ExampleWrapper_Wrap_hyphens() {
218+
var loremIpsum = `
219+
In this particular example, I will spam a lot of hyphenated words, which should wrap at some point, and test the multi-breakpoint feature of this package.
220+
221+
The girl was accident-prone, good-looking, quick-thinking, carbon-neutral, bad-tempered, sport-mad, fair-haired, camera-ready, and finally open-mouthed.
222+
`
223+
224+
fmt.Println(w.Wrap(loremIpsum, 80))
225+
// Output:
226+
// In this particular example, I will spam a lot of hyphenated words, which should
227+
// wrap at some point, and test the multi-breakpoint feature of this package.
228+
//
229+
// The girl was accident-prone, good-looking, quick-thinking, carbon-neutral, bad
230+
// tempered, sport-mad, fair-haired, camera-ready, and finally open-mouthed.
231+
}

0 commit comments

Comments
 (0)