initial commit
This commit is contained in:
126
wrap.go
Normal file
126
wrap.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package wrap
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type measuredString struct {
|
||||
text string
|
||||
unicodeSize int
|
||||
}
|
||||
|
||||
func (str *measuredString) pushRune(text string, size int) string {
|
||||
str.text += text[:size]
|
||||
str.unicodeSize++
|
||||
return text[size:]
|
||||
}
|
||||
|
||||
func concat(a, b measuredString) measuredString {
|
||||
return measuredString{
|
||||
text: a.text + b.text,
|
||||
unicodeSize: a.unicodeSize + b.unicodeSize,
|
||||
}
|
||||
}
|
||||
|
||||
// Given the space remaining on the current line as minWidth and the maximum line size as maxWidth,
|
||||
// this function will read the first word and return three values:
|
||||
// 1) the text that should go at the end of the current line,
|
||||
// 2) the text that should go on the next line,
|
||||
// 3) the remaining text that has not been consumed.
|
||||
func wrapWord(text string, minWidth int, maxWidth int) (measuredString, measuredString, string) {
|
||||
var space, prefix, suffix measuredString
|
||||
reachedMin := false
|
||||
reachedEnd := false
|
||||
// read the first word, stopping at the end of the current line
|
||||
for !reachedMin && len(text) > 0 {
|
||||
rune, size := utf8.DecodeRuneInString(text)
|
||||
if unicode.IsSpace(rune) {
|
||||
if prefix.unicodeSize == 0 {
|
||||
text = space.pushRune(text, size)
|
||||
} else {
|
||||
reachedMin = true
|
||||
reachedEnd = true
|
||||
}
|
||||
} else {
|
||||
if space.unicodeSize+prefix.unicodeSize < minWidth {
|
||||
text = prefix.pushRune(text, size)
|
||||
} else {
|
||||
reachedMin = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// does the entire word fit on the current line ?
|
||||
if reachedEnd || len(text) == 0 {
|
||||
return concat(space, prefix), measuredString{}, text
|
||||
}
|
||||
|
||||
// otherwise finish reading the first word
|
||||
for !reachedEnd && len(text) > 0 {
|
||||
rune, size := utf8.DecodeRuneInString(text)
|
||||
if unicode.IsSpace(rune) {
|
||||
reachedEnd = true
|
||||
} else {
|
||||
if prefix.unicodeSize+suffix.unicodeSize < maxWidth {
|
||||
text = suffix.pushRune(text, size)
|
||||
} else {
|
||||
// special case: if the word cannot fit on a single line,
|
||||
// we put half the word on the current line and the other half on the next line
|
||||
return concat(space, prefix), suffix, text
|
||||
}
|
||||
}
|
||||
}
|
||||
// and put the word on the next line
|
||||
return measuredString{}, concat(prefix, suffix), text
|
||||
}
|
||||
|
||||
// Break a string into lines of at most [width] runes.
|
||||
func Wrap(text string, width int) (result []string) {
|
||||
var line measuredString
|
||||
for len(text) > 0 {
|
||||
var cont, next measuredString
|
||||
cont, next, text = wrapWord(text, width-line.unicodeSize, width)
|
||||
|
||||
line = concat(line, cont)
|
||||
if next.unicodeSize > 0 {
|
||||
result = append(result, line.text)
|
||||
line = next
|
||||
}
|
||||
}
|
||||
// add any text remaining in the buffer
|
||||
return append(result, line.text)
|
||||
}
|
||||
|
||||
// Prefix multiple lines of text with a string.
|
||||
func Indent(prefix string, lines []string) []string {
|
||||
result := make([]string, len(lines))
|
||||
for i, line := range lines {
|
||||
result[i] = prefix + line
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Prefix multiple lines of text using a different string for the first line.
|
||||
func Indentf(first string, prefix string, lines []string) []string {
|
||||
result := make([]string, len(lines))
|
||||
for i, line := range lines {
|
||||
if i == 0 {
|
||||
result[i] = first + line
|
||||
} else {
|
||||
result[i] = prefix + line
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Prefix multiple lines of text with a number of spaces.
|
||||
func Indents(size int, lines []string) []string {
|
||||
return Indent(strings.Repeat(" ", size), lines)
|
||||
}
|
||||
|
||||
// Prefix multiple lines of text with a number of spaces,
|
||||
// but using a different string for the first line.
|
||||
func Indentfs(first string, size int, lines []string) []string {
|
||||
return Indentf(first, strings.Repeat(" ", size), lines)
|
||||
}
|
||||
Reference in New Issue
Block a user