package util import ( "bytes" "regexp" "strings" ) func WordWrap(text string, lineWidth int) (string, int) { words := strings.Fields(strings.TrimSpace(text)) if len(words) == 0 { return "", 1 } lines := 1 wrapped := words[0] spaceLeft := lineWidth - len(wrapped) for _, word := range words[1:] { if len(word)+1 > spaceLeft { wrapped += "\n" + word spaceLeft = lineWidth - len(word) lines++ } else { wrapped += " " + word spaceLeft -= 1 + len(word) } } return wrapped, lines } func TextWrap(text string, lineWidth int) (string, int) { return TextWrapPadded(text, lineWidth, 0) } func TextWrapPadded(text string, lineWidth int, leftPad int) (string, int) { var textBuffer bytes.Buffer var lineBuffer bytes.Buffer nbLine := 1 firstLine := true pad := strings.Repeat(" ", leftPad) // tabs are formatted as 4 spaces text = strings.Replace(text, "\t", " ", 4) re := regexp.MustCompile(`(\x1b\[\d+m)?([^\x1b]*)(\x1b\[\d+m)?`) for _, line := range strings.Split(text, "\n") { spaceLeft := lineWidth - leftPad if !firstLine { textBuffer.WriteString("\n") nbLine++ } firstWord := true for _, word := range strings.Split(line, " ") { prefix := "" suffix := "" matches := re.FindStringSubmatch(word) if matches != nil && (matches[1] != "" || matches[3] != "") { // we have a color escape sequence prefix = matches[1] word = matches[2] suffix = matches[3] } if spaceLeft > len(word) { if !firstWord { lineBuffer.WriteString(" ") spaceLeft -= 1 } lineBuffer.WriteString(prefix + word + suffix) spaceLeft -= len(word) firstWord = false } else { if len(word) > lineWidth { for len(word) > 0 { l := minInt(spaceLeft, len(word)) part := prefix + word[:l] prefix = "" word = word[l:] lineBuffer.WriteString(part) textBuffer.WriteString(pad) textBuffer.Write(lineBuffer.Bytes()) lineBuffer.Reset() if len(word) > 0 { textBuffer.WriteString("\n") nbLine++ } spaceLeft = lineWidth - leftPad } } else { textBuffer.WriteString(pad + strings.TrimRight(lineBuffer.String(), " ")) textBuffer.WriteString("\n") lineBuffer.Reset() lineBuffer.WriteString(prefix + word + suffix) firstWord = false spaceLeft = lineWidth - len(word) nbLine++ } } } textBuffer.WriteString(pad + strings.TrimRight(lineBuffer.String(), " ")) lineBuffer.Reset() firstLine = false } return textBuffer.String(), nbLine } func minInt(a, b int) int { if a > b { return b } return a }