package tui import ( "strings" "unicode" ) // Split text into words or newlines. func splitText(s string) [][]rune { s = strings.TrimSpace(s) r := []rune(s) out := [][]rune{} nextWord := func() []rune { for i := range r { if unicode.IsSpace(r[i]) { ret := r[:i] r = r[i:] return ret } } ret := r r = r[:0] return ret } nextSpaces := func() []rune { for i := range r { if !unicode.IsSpace(r[i]) { ret := r[:i] r = r[i:] return ret } } // Code should never reach these lines. ret := r r = r[:0] return ret } for len(r) > 0 { word := nextWord() if len(word) != 0 { out = append(out, word) } if len(r) == 0 { break } spaces := nextSpaces() count := 0 for _, x := range spaces { if x == '\n' { count++ } if count > 1 { break } } if count > 1 { out = append(out, []rune{'\n'}) } } return out } func normalizeText(s string) string { out := make([]rune, 0, len(s)) parts := splitText(s) prevWasWord := false for _, p := range parts { if p[0] == '\n' { out = append(out, '\n', '\n') prevWasWord = false } else { if prevWasWord { out = append(out, ' ') } out = append(out, p...) prevWasWord = true } } return string(out) } func wrapText(s string, w int) ([]string, int) { maxLine := 0 parts := splitText(s) nextLine := func() (string, int) { if parts[0][0] == '\n' { parts = parts[1:] return "", 0 } words := append([]string{}, string(parts[0])) length := len(parts[0]) parts = parts[1:] for len(parts) > 0 { p := parts[0] if p[0] == '\n' { break } if length+len(p)+1 > w { break } length += len(p) + 1 words = append(words, string(p)) parts = parts[1:] } return strings.Join(words, " "), length } lines := make([]string, 0, 2) for len(parts) > 0 { line, lineLen := nextLine() if lineLen > maxLine { maxLine = lineLen } lines = append(lines, line) } return lines, maxLine }