text.go

  1package util
  2
  3import (
  4	"bytes"
  5	"regexp"
  6	"strings"
  7)
  8
  9func WordWrap(text string, lineWidth int) (string, int) {
 10	words := strings.Fields(strings.TrimSpace(text))
 11	if len(words) == 0 {
 12		return "", 1
 13	}
 14	lines := 1
 15	wrapped := words[0]
 16	spaceLeft := lineWidth - len(wrapped)
 17	for _, word := range words[1:] {
 18		if len(word)+1 > spaceLeft {
 19			wrapped += "\n" + word
 20			spaceLeft = lineWidth - len(word)
 21			lines++
 22		} else {
 23			wrapped += " " + word
 24			spaceLeft -= 1 + len(word)
 25		}
 26	}
 27
 28	return wrapped, lines
 29}
 30
 31func TextWrap(text string, lineWidth int) (string, int) {
 32	return TextWrapPadded(text, lineWidth, 0)
 33}
 34
 35func TextWrapPadded(text string, lineWidth int, leftPad int) (string, int) {
 36	var textBuffer bytes.Buffer
 37	var lineBuffer bytes.Buffer
 38	nbLine := 1
 39	firstLine := true
 40	pad := strings.Repeat(" ", leftPad)
 41
 42	// tabs are formatted as 4 spaces
 43	text = strings.Replace(text, "\t", "    ", 4)
 44
 45	re := regexp.MustCompile(`(\x1b\[\d+m)?([^\x1b]*)(\x1b\[\d+m)?`)
 46
 47	for _, line := range strings.Split(text, "\n") {
 48		spaceLeft := lineWidth - leftPad
 49
 50		if !firstLine {
 51			textBuffer.WriteString("\n")
 52			nbLine++
 53		}
 54
 55		firstWord := true
 56
 57		for _, word := range strings.Split(line, " ") {
 58			prefix := ""
 59			suffix := ""
 60
 61			matches := re.FindStringSubmatch(word)
 62			if matches != nil && (matches[1] != "" || matches[3] != "") {
 63				// we have a color escape sequence
 64				prefix = matches[1]
 65				word = matches[2]
 66				suffix = matches[3]
 67			}
 68
 69			if spaceLeft > len(word) {
 70				if !firstWord {
 71					lineBuffer.WriteString(" ")
 72					spaceLeft -= 1
 73				}
 74				lineBuffer.WriteString(prefix + word + suffix)
 75				spaceLeft -= len(word)
 76				firstWord = false
 77			} else {
 78				if len(word) > lineWidth {
 79					for len(word) > 0 {
 80						l := minInt(spaceLeft, len(word))
 81						part := prefix + word[:l]
 82						prefix = ""
 83						word = word[l:]
 84
 85						lineBuffer.WriteString(part)
 86						textBuffer.WriteString(pad)
 87						textBuffer.Write(lineBuffer.Bytes())
 88						lineBuffer.Reset()
 89
 90						if len(word) > 0 {
 91							textBuffer.WriteString("\n")
 92							nbLine++
 93						}
 94
 95						spaceLeft = lineWidth - leftPad
 96					}
 97				} else {
 98					textBuffer.WriteString(pad + strings.TrimRight(lineBuffer.String(), " "))
 99					textBuffer.WriteString("\n")
100					lineBuffer.Reset()
101					lineBuffer.WriteString(prefix + word + suffix)
102					firstWord = false
103					spaceLeft = lineWidth - len(word)
104					nbLine++
105				}
106			}
107		}
108		textBuffer.WriteString(pad + strings.TrimRight(lineBuffer.String(), " "))
109		lineBuffer.Reset()
110		firstLine = false
111	}
112
113	return textBuffer.String(), nbLine
114}
115
116func minInt(a, b int) int {
117	if a > b {
118		return b
119	}
120	return a
121}