From 11ff934fd36d8b93431468f3967ddb420886ada3 Mon Sep 17 00:00:00 2001 From: Ben Brooks Date: Sat, 2 Jan 2021 17:40:33 +0000 Subject: [PATCH] Add CutLongWords option to Wrapper (#13) --- wrapper.go | 26 +++++++++++++++++++------- wrapper_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/wrapper.go b/wrapper.go index d319924..2d2e421 100644 --- a/wrapper.go +++ b/wrapper.go @@ -46,9 +46,12 @@ type Wrapper struct { TrimInputSuffix string // StripTrailingNewline can be set to true if you want the trailing - // newline to be removed from the return vailue. + // newline to be removed from the return value. // Default: false StripTrailingNewline bool + + // CutLongWords will cause a hard-wrap in the middle of a word if the word's length exceeds the given limit. + CutLongWords bool } // NewWrapper returns a new instance of a Wrapper initialised with defaults. @@ -98,15 +101,24 @@ func (w Wrapper) line(s string, limit int) string { // Find the index of the last breakpoint within the limit. i := strings.LastIndexAny(s[:limit+1], w.Breakpoints) - // Can't wrap within the limit, wrap at the next breakpoint instead. + breakpointWidth := 1 + + // Can't wrap within the limit if i < 0 { - i = strings.IndexAny(s, w.Breakpoints) - // Nothing left to do! - if i < 0 { - return w.OutputLinePrefix + s + w.OutputLineSuffix + if w.CutLongWords { + // wrap at the limit + i = limit + breakpointWidth = 0 + } else { + // wrap at the next breakpoint instead + i = strings.IndexAny(s, w.Breakpoints) + // Nothing left to do! + if i < 0 { + return w.OutputLinePrefix + s + w.OutputLineSuffix + } } } // Recurse until we have nothing left to do. - return w.OutputLinePrefix + s[:i] + w.OutputLineSuffix + w.Newline + w.line(s[i+1:], limit) + return w.OutputLinePrefix + s[:i] + w.OutputLineSuffix + w.Newline + w.line(s[i+breakpointWidth:], limit) } diff --git a/wrapper_test.go b/wrapper_test.go index 829f2b8..7d115bf 100644 --- a/wrapper_test.go +++ b/wrapper_test.go @@ -1,6 +1,7 @@ package wrap_test import ( + "strconv" "strings" "testing" "unicode/utf8" @@ -63,3 +64,44 @@ func TestWrapper_Wrap(t *testing.T) { } } + +func TestWrapper_Wrap_CutLongWords(t *testing.T) { + const limit = 8 + + tests := []struct { + input string + expected string + }{ + { + input: "A short woooord", + expected: `A short +woooord`, + }, + { + input: "A perfect wooooord", + expected: `A +perfect +wooooord`, + }, + { + input: "A long wooooooooooooord", + expected: `A long +wooooooo +oooooord`, + }, + } + + for i, test := range tests { + t.Run(strconv.FormatInt(int64(i), 10), func(t *testing.T) { + w := wrap.NewWrapper() + w.CutLongWords = true + w.StripTrailingNewline = true + + actual := w.Wrap(test.input, limit) + + if actual != test.expected { + t.Errorf("expected %q but got %q", test.expected, actual) + } + }) + } +}