test teletype formatting
This commit is contained in:
parent
dda5dc6884
commit
57ef6b1267
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.cover
|
||||||
16
Makefile
16
Makefile
@ -7,3 +7,19 @@ build: $(SOURCES)
|
|||||||
|
|
||||||
fmt: $(SOURCES)
|
fmt: $(SOURCES)
|
||||||
go fmt
|
go fmt
|
||||||
|
|
||||||
|
check: $(SOURCES)
|
||||||
|
go test -count 1
|
||||||
|
|
||||||
|
.cover: $(SOURCES)
|
||||||
|
go test -count 1 -coverprofile .cover
|
||||||
|
|
||||||
|
cover: .cover
|
||||||
|
go tool cover -func .cover
|
||||||
|
|
||||||
|
showcover: .cover
|
||||||
|
go tool cover -html .cover
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean
|
||||||
|
rm .cover
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -1,3 +1,5 @@
|
|||||||
module code.squareroundforest.org/arpio/textfmt
|
module code.squareroundforest.org/arpio/textfmt
|
||||||
|
|
||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
|
require code.squareroundforest.org/arpio/notation v0.0.0-20250826181910-5140794b16b2
|
||||||
|
|||||||
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
code.squareroundforest.org/arpio/notation v0.0.0-20250826181910-5140794b16b2 h1:S4mjQHL70CuzFg1AGkr0o0d+4M+ZWM0sbnlYq6f0b3I=
|
||||||
|
code.squareroundforest.org/arpio/notation v0.0.0-20250826181910-5140794b16b2/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY=
|
||||||
12
lib.go
12
lib.go
@ -59,7 +59,7 @@ type Entry struct {
|
|||||||
rows []TableRow
|
rows []TableRow
|
||||||
syntax SyntaxItem
|
syntax SyntaxItem
|
||||||
indentFirst int
|
indentFirst int
|
||||||
indentRest int
|
indent int
|
||||||
wrapWidth int
|
wrapWidth int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,8 +71,8 @@ func Text(text string) Txt {
|
|||||||
return Txt{text: text}
|
return Txt{text: text}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Link(title, uri string) Txt {
|
func Link(label, uri string) Txt {
|
||||||
return Txt{text: title, link: uri}
|
return Txt{text: label, link: uri}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Bold(t Txt) Txt {
|
func Bold(t Txt) Txt {
|
||||||
@ -184,11 +184,15 @@ func Choice(items ...SyntaxItem) SyntaxItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Syntax(items ...SyntaxItem) Entry {
|
func Syntax(items ...SyntaxItem) Entry {
|
||||||
|
if len(items) == 1 {
|
||||||
|
return Entry{typ: syntax, syntax: items[0]}
|
||||||
|
}
|
||||||
|
|
||||||
return Entry{typ: syntax, syntax: Sequence(items...)}
|
return Entry{typ: syntax, syntax: Sequence(items...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Indent(e Entry, first, rest int) Entry {
|
func Indent(e Entry, first, rest int) Entry {
|
||||||
e.indentFirst, e.indentRest = first, rest
|
e.indentFirst, e.indent = first, rest
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
print_test.go
Normal file
10
print_test.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package textfmt_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.squareroundforest.org/arpio/notation"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func logBytes(t *testing.T, s string) {
|
||||||
|
t.Log(notation.Sprint([]byte(s)))
|
||||||
|
}
|
||||||
7
table.go
7
table.go
@ -31,7 +31,12 @@ func columnWeights(cells [][]string) []int {
|
|||||||
w := make([]int, len(cells[0]))
|
w := make([]int, len(cells[0]))
|
||||||
for _, row := range cells {
|
for _, row := range cells {
|
||||||
for i, cell := range row {
|
for i, cell := range row {
|
||||||
w[i] += len([]rune(cell))
|
weight := len([]rune(cell))
|
||||||
|
if weight == 0 {
|
||||||
|
weight = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
w[i] += weight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
206
teletype.go
206
teletype.go
@ -23,21 +23,35 @@ func escapeTeletype(s string) string {
|
|||||||
return string(r)
|
return string(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeLines(w *writer, txt string, indentFirst, indentRest int) {
|
func definitionNamesValues(d []DefinitionItem) ([]string, []string, error) {
|
||||||
lines := strings.Split(txt, "\n")
|
var n, v []string
|
||||||
for i, l := range lines {
|
for _, di := range d {
|
||||||
if i > 0 {
|
name, err := ttyTextToString(di.name)
|
||||||
w.write("\n")
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
indent := indentFirst
|
value, err := ttyTextToString(di.value)
|
||||||
if i > 0 {
|
if err != nil {
|
||||||
indent = indentRest
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
w.write(timesn(" ", indent))
|
n = append(n, name)
|
||||||
w.write(l)
|
v = append(v, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return n, v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ttyTextToString(text Txt) (string, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
w := writer{w: &b}
|
||||||
|
renderTTYText(&w, text)
|
||||||
|
if w.err != nil {
|
||||||
|
return "", w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderTTYText(w *writer, text Txt) {
|
func renderTTYText(w *writer, text Txt) {
|
||||||
@ -71,31 +85,6 @@ func renderTTYText(w *writer, text Txt) {
|
|||||||
w.write(text.text)
|
w.write(text.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ttyTextToString(text Txt) (string, error) {
|
|
||||||
var b bytes.Buffer
|
|
||||||
w := writer{w: &b}
|
|
||||||
renderTTYText(&w, text)
|
|
||||||
if w.err != nil {
|
|
||||||
return "", w.err
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func definitionNames(d []DefinitionItem) ([]string, error) {
|
|
||||||
var n []string
|
|
||||||
for _, di := range d {
|
|
||||||
name, err := ttyTextToString(di.name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
n = append(n, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderTTYTitle(w *writer, e Entry) {
|
func renderTTYTitle(w *writer, e Entry) {
|
||||||
w.write(timesn(" ", e.indentFirst))
|
w.write(timesn(" ", e.indentFirst))
|
||||||
renderTTYText(w, e.text)
|
renderTTYText(w, e.text)
|
||||||
@ -105,16 +94,15 @@ func renderTTYParagraph(w *writer, e Entry) {
|
|||||||
var txt string
|
var txt string
|
||||||
txt, w.err = ttyTextToString(e.text)
|
txt, w.err = ttyTextToString(e.text)
|
||||||
if e.wrapWidth > 0 {
|
if e.wrapWidth > 0 {
|
||||||
txt = wrap(txt, e.wrapWidth, e.indentFirst, e.indentRest)
|
txt = wrap(txt, e.wrapWidth, e.indentFirst, e.indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
writeLines(w, txt, e.indentFirst, e.indentRest)
|
writeLines(w, txt, e.indentFirst, e.indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderTTYList(w *writer, e Entry) {
|
func renderTTYList(w *writer, e Entry) {
|
||||||
const bullet = "- "
|
const bullet = "- "
|
||||||
indentFirst := e.indentFirst
|
indent := e.indent + len(bullet)
|
||||||
indentRest := e.indentRest + len(bullet)
|
|
||||||
for i, item := range e.items {
|
for i, item := range e.items {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
w.write("\n")
|
w.write("\n")
|
||||||
@ -123,19 +111,18 @@ func renderTTYList(w *writer, e Entry) {
|
|||||||
var txt string
|
var txt string
|
||||||
txt, w.err = ttyTextToString(item.text)
|
txt, w.err = ttyTextToString(item.text)
|
||||||
if e.wrapWidth > 0 {
|
if e.wrapWidth > 0 {
|
||||||
txt = wrap(txt, e.wrapWidth-len(bullet), indentFirst, indentRest)
|
txt = wrap(txt, e.wrapWidth-len(bullet), e.indentFirst, indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.write(timesn(" ", indentFirst))
|
w.write(timesn(" ", e.indentFirst))
|
||||||
w.write(bullet)
|
w.write(bullet)
|
||||||
writeLines(w, txt, 0, indentRest)
|
writeLines(w, txt, 0, indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderTTYNumberedList(w *writer, e Entry) {
|
func renderTTYNumberedList(w *writer, e Entry) {
|
||||||
maxDigits := maxDigits(len(e.items))
|
maxDigits := numDigits(len(e.items))
|
||||||
indentFirst := e.indentFirst
|
indent := e.indent + maxDigits + 2
|
||||||
indentRest := e.indentRest + maxDigits + 2
|
|
||||||
for i, item := range e.items {
|
for i, item := range e.items {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
w.write("\n")
|
w.write("\n")
|
||||||
@ -144,71 +131,89 @@ func renderTTYNumberedList(w *writer, e Entry) {
|
|||||||
var txt string
|
var txt string
|
||||||
txt, w.err = ttyTextToString(item.text)
|
txt, w.err = ttyTextToString(item.text)
|
||||||
if e.wrapWidth > 0 {
|
if e.wrapWidth > 0 {
|
||||||
txt = wrap(txt, e.wrapWidth-maxDigits-2, indentFirst, indentRest)
|
txt = wrap(txt, e.wrapWidth-maxDigits-2, e.indentFirst, indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.write(timesn(" ", indentFirst))
|
w.write(timesn(" ", e.indentFirst))
|
||||||
w.write(fmt.Sprintf("%d. ", i))
|
w.write(padRight(fmt.Sprintf("%d.", i+1), maxDigits+2))
|
||||||
writeLines(w, txt, 0, indentRest)
|
writeLines(w, txt, 0, indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderTTYDefinitions(w *writer, e Entry) {
|
func renderTTYDefinitions(w *writer, e Entry) {
|
||||||
names, err := definitionNames(e.definitions)
|
const (
|
||||||
|
bullet = "- "
|
||||||
|
sep = ": "
|
||||||
|
)
|
||||||
|
|
||||||
|
names, values, err := definitionNamesValues(e.definitions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.err = err
|
w.err = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
maxLength := maxLength(names)
|
maxNameLength := maxLength(names)
|
||||||
indentFirst := e.indentFirst
|
nameColWidth := maxNameLength + e.indentFirst + len(bullet) + len(sep)
|
||||||
indentRest := e.indentRest + maxLength + 4
|
valueWidth := e.wrapWidth
|
||||||
for i, def := range e.definitions {
|
if valueWidth > 0 {
|
||||||
|
valueWidth -= nameColWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range names {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
w.write("\n")
|
w.write("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
var value string
|
w.write(timesn(" ", e.indentFirst), bullet, names[i], sep)
|
||||||
value, w.err = ttyTextToString(def.value)
|
if valueWidth > 0 {
|
||||||
if e.wrapWidth > 0 {
|
values[i] = wrap(values[i], valueWidth, 0, e.indent)
|
||||||
value = wrap(value, e.wrapWidth-maxLength-4, indentFirst, indentRest)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
name := names[i]
|
writeLines(
|
||||||
w.write("- ")
|
w,
|
||||||
w.write(name)
|
values[i],
|
||||||
w.write(": ")
|
maxNameLength-len([]rune(names[i])),
|
||||||
writeLines(w, value, 0, indentRest)
|
nameColWidth+e.indent,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderTTYNumberedDefinitions(w *writer, e Entry) {
|
func renderTTYNumberedDefinitions(w *writer, e Entry) {
|
||||||
maxDigits := maxDigits(len(e.definitions))
|
const (
|
||||||
names, err := definitionNames(e.definitions)
|
dot = ". "
|
||||||
|
sep = ": "
|
||||||
|
)
|
||||||
|
|
||||||
|
names, values, err := definitionNamesValues(e.definitions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.err = err
|
w.err = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
maxLength := maxLength(names)
|
maxDigits := numDigits(len(e.definitions))
|
||||||
indentFirst := e.indentFirst
|
maxNameLength := maxLength(names)
|
||||||
indentRest := e.indentRest + maxLength + maxDigits + 4
|
nameColWidth := maxNameLength + e.indentFirst + maxDigits + len(dot) + len(sep)
|
||||||
for i, def := range e.definitions {
|
valueWidth := e.wrapWidth
|
||||||
|
if valueWidth > 0 {
|
||||||
|
valueWidth -= nameColWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range names {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
w.write("\n")
|
w.write("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
var value string
|
w.write(timesn(" ", e.indentFirst), padRight(fmt.Sprintf("%d.", i+1), maxDigits+2), names[i], sep)
|
||||||
value, w.err = ttyTextToString(def.value)
|
if valueWidth > 0 {
|
||||||
if e.wrapWidth > 0 {
|
values[i] = wrap(values[i], valueWidth, 0, e.indent)
|
||||||
value = wrap(value, e.wrapWidth-maxLength-4, indentFirst, indentRest)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
name := names[i]
|
writeLines(
|
||||||
w.write(fmt.Sprintf("%d. ", i))
|
w,
|
||||||
w.write(name)
|
values[i],
|
||||||
w.write(": ")
|
maxNameLength-len([]rune(names[i])),
|
||||||
writeLines(w, value, 0, indentRest)
|
nameColWidth+e.indent,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +253,7 @@ func renderTTYTable(w *writer, e Entry) {
|
|||||||
|
|
||||||
totalSeparatorWidth := (len(cellTexts[0]) - 1) * 3
|
totalSeparatorWidth := (len(cellTexts[0]) - 1) * 3
|
||||||
if e.wrapWidth > 0 {
|
if e.wrapWidth > 0 {
|
||||||
allocatedWidth := e.wrapWidth - e.indentFirst - totalSeparatorWidth
|
allocatedWidth := e.wrapWidth - e.indent - totalSeparatorWidth
|
||||||
columnWeights := columnWeights(cellTexts)
|
columnWeights := columnWeights(cellTexts)
|
||||||
targetColumnWidths := targetColumnWidths(allocatedWidth, columnWeights)
|
targetColumnWidths := targetColumnWidths(allocatedWidth, columnWeights)
|
||||||
for i := range cellTexts {
|
for i := range cellTexts {
|
||||||
@ -273,28 +278,52 @@ func renderTTYTable(w *writer, e Entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
w.write("\n")
|
w.write("\n")
|
||||||
w.write(timesn(sep, totalWidth))
|
w.write(timesn(" ", e.indent), timesn(sep, totalWidth))
|
||||||
w.write("\n")
|
w.write("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lines := make([][]string, len(cellTexts[i]))
|
||||||
for j := range cellTexts[i] {
|
for j := range cellTexts[i] {
|
||||||
if j > 0 {
|
lines[j] = strings.Split(cellTexts[i][j], "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxLines int
|
||||||
|
for j := range lines {
|
||||||
|
if len(lines[j]) > maxLines {
|
||||||
|
maxLines = len(lines[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := 0; k < maxLines; k++ {
|
||||||
|
if k > 0 {
|
||||||
|
w.write("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := range lines {
|
||||||
|
if j == 0 {
|
||||||
|
w.write(timesn(" ", e.indent))
|
||||||
|
} else {
|
||||||
w.write(" | ")
|
w.write(" | ")
|
||||||
}
|
}
|
||||||
|
|
||||||
w.write(padRight(cellTexts[i][j], columnWidths[j]))
|
var l string
|
||||||
|
if k < len(lines[j]) {
|
||||||
|
l = lines[j][k]
|
||||||
|
}
|
||||||
|
|
||||||
|
w.write(padRight(l, columnWidths[j]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasHeader && len(cellTexts) == 1 {
|
if hasHeader && len(cellTexts) == 1 {
|
||||||
w.write(timesn("=", totalWidth))
|
w.write("\n", timesn("=", totalWidth))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderTTYCode(w *writer, e Entry) {
|
func renderTTYCode(w *writer, e Entry) {
|
||||||
var txt string
|
e.text.text = escapeTeletype(e.text.text)
|
||||||
txt, w.err = ttyTextToString(e.text)
|
writeLines(w, e.text.text, e.indent, e.indent)
|
||||||
writeLines(w, txt, e.indentFirst, e.indentFirst)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderTTYMultiple(w *writer, s SyntaxItem) {
|
func renderTTYMultiple(w *writer, s SyntaxItem) {
|
||||||
@ -346,7 +375,7 @@ func renderTTYChoice(w *writer, s SyntaxItem) {
|
|||||||
w.write("(")
|
w.write("(")
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, item := range s.sequence {
|
for i, item := range s.choice {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
separator := "|"
|
separator := "|"
|
||||||
if s.topLevel {
|
if s.topLevel {
|
||||||
@ -435,6 +464,9 @@ func renderTeletype(out io.Writer, d Document) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(d.entries) > 0 {
|
||||||
w.write("\n")
|
w.write("\n")
|
||||||
|
}
|
||||||
|
|
||||||
return w.err
|
return w.err
|
||||||
}
|
}
|
||||||
|
|||||||
1901
teletype_test.go
Normal file
1901
teletype_test.go
Normal file
File diff suppressed because it is too large
Load Diff
38
text.go
38
text.go
@ -7,7 +7,7 @@ func timesn(s string, n int) string {
|
|||||||
return strings.Join(ss, s)
|
return strings.Join(ss, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func maxDigits(n int) int {
|
func numDigits(n int) int {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@ -33,6 +33,42 @@ func maxLength(names []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func padRight(s string, n int) string {
|
func padRight(s string, n int) string {
|
||||||
|
if len(s) >= n {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
n -= len([]rune(s))
|
n -= len([]rune(s))
|
||||||
return s + timesn(" ", n)
|
return s + timesn(" ", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func singleLine(text string) string {
|
||||||
|
var l []string
|
||||||
|
p := strings.Split(text, "\n")
|
||||||
|
for _, part := range p {
|
||||||
|
part = strings.TrimSpace(part)
|
||||||
|
if part == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
l = append(l, part)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(l, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeLines(w *writer, txt string, indentFirst, indent int) {
|
||||||
|
lines := strings.Split(txt, "\n")
|
||||||
|
for i, l := range lines {
|
||||||
|
if i > 0 {
|
||||||
|
w.write("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
ind := indentFirst
|
||||||
|
if i > 0 {
|
||||||
|
ind = indent
|
||||||
|
}
|
||||||
|
|
||||||
|
w.write(timesn(" ", ind))
|
||||||
|
w.write(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
51
wrap.go
51
wrap.go
@ -16,60 +16,37 @@ func getWords(text string) []string {
|
|||||||
return words
|
return words
|
||||||
}
|
}
|
||||||
|
|
||||||
func lineLength(words []string) int {
|
|
||||||
if len(words) == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
var l int
|
|
||||||
for _, w := range words {
|
|
||||||
r := []rune(w)
|
|
||||||
l += len(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return l + len(words) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func singleLine(text string) string {
|
|
||||||
var l []string
|
|
||||||
p := strings.Split(text, "\n")
|
|
||||||
for _, part := range p {
|
|
||||||
part = strings.TrimSpace(part)
|
|
||||||
if part == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
l = append(l, part)
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(l, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func wrap(text string, width, firstIndent, restIndent int) string {
|
func wrap(text string, width, firstIndent, restIndent int) string {
|
||||||
var (
|
var (
|
||||||
lines []string
|
lines []string
|
||||||
currentLine []string
|
currentLine []string
|
||||||
currentLen int
|
lineLen int
|
||||||
)
|
)
|
||||||
|
|
||||||
words := getWords(text)
|
words := getWords(text)
|
||||||
for _, w := range words {
|
for _, w := range words {
|
||||||
|
if len(currentLine) == 0 {
|
||||||
|
currentLine = []string{w}
|
||||||
|
lineLen = len(w)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
maxw := width - restIndent
|
maxw := width - restIndent
|
||||||
if len(lines) == 0 {
|
if len(lines) == 0 {
|
||||||
maxw = width - firstIndent
|
maxw = width - firstIndent
|
||||||
}
|
}
|
||||||
|
|
||||||
currentLine = append(currentLine, w)
|
if lineLen+1+len(w) > maxw {
|
||||||
if lineLength(currentLine) > maxw {
|
|
||||||
currentLine = currentLine[:len(currentLine)-1]
|
|
||||||
lines = append(lines, strings.Join(currentLine, " "))
|
lines = append(lines, strings.Join(currentLine, " "))
|
||||||
currentLine = []string{w}
|
currentLine = []string{w}
|
||||||
}
|
lineLen = len(w)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
currentLine = append(currentLine, w)
|
||||||
|
lineLen += 1 + len(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(currentLine) > 0 {
|
|
||||||
lines = append(lines, strings.Join(currentLine, " "))
|
lines = append(lines, strings.Join(currentLine, " "))
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(lines, "\n")
|
return strings.Join(lines, "\n")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,12 +7,14 @@ type writer struct {
|
|||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *writer) write(s string) {
|
func (w *writer) write(s ...string) {
|
||||||
|
for _, si := range s {
|
||||||
if w.err != nil {
|
if w.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := w.w.Write([]byte(s)); err != nil {
|
if _, err := w.w.Write([]byte(si)); err != nil {
|
||||||
w.err = err
|
w.err = err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
writer_test.go
Normal file
36
writer_test.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package textfmt_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type failingWriter struct {
|
||||||
|
out io.Writer
|
||||||
|
failAfter int
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *failingWriter) Write(p []byte) (int, error) {
|
||||||
|
if w.err != nil {
|
||||||
|
return 0, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.failAfter <= len(p) {
|
||||||
|
p = p[:w.failAfter]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) > 0 && w.out != nil {
|
||||||
|
if n, err := w.out.Write(p); err != nil {
|
||||||
|
w.err = err
|
||||||
|
return n, w.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.failAfter -= len(p)
|
||||||
|
if w.failAfter == 0 {
|
||||||
|
w.err = errors.New("test write error")
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(p), w.err
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user