diff --git a/ansi/wrap.go b/ansi/wrap.go index 530be6ca..d2b2ace2 100644 --- a/ansi/wrap.go +++ b/ansi/wrap.go @@ -196,6 +196,12 @@ func wordwrap(m Method, s string, limit int, breakpoints string) string { addWord() space.WriteRune(r) } else if bytes.ContainsAny(cluster, breakpoints) { + // Treat the current word + breakpoint as one unit: + // if appending it would overflow the line, break first + // so the word doesn't trail past the limit. + if curWidth+space.Len()+wordLen+1 > limit && wordLen+1 <= limit { + addNewline() + } addSpace() addWord() buf.Write(cluster) @@ -235,6 +241,9 @@ func wordwrap(m Method, s string, limit int, breakpoints string) string { case r == '-': fallthrough case runeContainsAny(r, breakpoints): + if curWidth+space.Len()+wordLen+1 > limit && wordLen+1 <= limit { + addNewline() + } addSpace() addWord() buf.WriteByte(b[i]) diff --git a/ansi/wrap_test.go b/ansi/wrap_test.go index 65c12ebc..711b471d 100644 --- a/ansi/wrap_test.go +++ b/ansi/wrap_test.go @@ -63,6 +63,7 @@ var wwCases = []struct { {"emoji_breakpoint", "foo😃 foobar", 4, "😃", "foo😃\nfoobar"}, {"wide_emoji_breakpoint", "foo🫧 foobar", 4, "🫧", "foo🫧\nfoobar"}, {"space_breakpoint", "foo --bar", 9, "-", "foo --bar"}, + {"breakpoint_overflow", "foo bar, fo", 7, ",", "foo\nbar, fo"}, {"simple", "foo bars foobars", 4, "", "foo\nbars\nfoobars"}, {"limit", "foo bar", 5, "", "foo\nbar"}, {"remove white spaces", "foo \nb ar ", 4, "", "foo\nb\nar"},