func WrapSoft(str string, wrapLen int) string {
	if wrapLen <= 0 {
		return ""
	}
	str = strings.Replace(str, "\t", "    ", -1)
	sLen := utf8.RuneCountInString(str)
	if sLen <= wrapLen {
		return str
	}
	out := &strings.Builder{}
	out.Grow(sLen + (sLen / wrapLen))
	for idx, paragraph := range strings.Split(str, "\n\n") {
		if idx > 0 {
			out.WriteString("\n\n")
		}
		wrapSoft(paragraph, wrapLen, out)
	}
	return out.String()
}
WrapSoft wraps a string to the given length using a newline. Handles strings
with ANSI escape sequences (such as text color) without breaking the text
formatting. Tries to move words that go beyond the line boundary to the next
line.

For examples, refer to the unit-tests or GoDoc examples.
package text
import (
	"strings"
	"unicode/utf8"
)
func WrapHard(str string, wrapLen int) string {
	if wrapLen <= 0 {
		return ""
	}
	str = strings.Replace(str, "\t", "    ", -1)
	sLen := utf8.RuneCountInString(str)
	if sLen <= wrapLen {
		return str
	}
	out := &strings.Builder{}
	out.Grow(sLen + (sLen / wrapLen))
	for idx, paragraph := range strings.Split(str, "\n\n") {
		if idx > 0 {
			out.WriteString("\n\n")
		}
		wrapHard(paragraph, wrapLen, out)
	}
	return out.String()
}