135 lines
2.0 KiB
Go
135 lines
2.0 KiB
Go
|
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
|
||
|
}
|