package html
import (
"bytes"
"errors"
"io"
"unicode"
)
type wrapper struct {
out *indentWriter
width int
line, word *bytes.Buffer
inWord, inTag, inSingleQuote, inQuote, lastSpace, started bool
err error
}
func newWrapper(out io.Writer, width int, indent string) *wrapper {
return &wrapper{
out: newIndentWriter(out, indent),
width: width,
line: bytes.NewBuffer(nil),
word: bytes.NewBuffer(nil),
}
}
func (w *wrapper) feed() error {
withSpace := w.lastSpace && w.line.Len() > 0
l := w.line.Len() + w.word.Len()
if withSpace && w.word.Len() > 0 {
l++
}
feedLine := l > w.width && w.line.Len() > 0
if feedLine {
if w.started {
if _, err := w.out.Write([]byte{'\n'}); err != nil {
return err
}
}
if _, err := io.Copy(w.out, w.line); err != nil {
return err
}
w.line.Reset()
w.started = true
}
if !feedLine && withSpace {
w.line.WriteRune(' ')
}
io.Copy(w.line, w.word)
w.word.Reset()
return nil
}
func (w *wrapper) Write(p []byte) (int, error) {
if w.err != nil {
return 0, w.err
}
runes := bytes.NewBuffer(nil)
if n, err := runes.Write(p); err != nil {
w.err = err
return n, w.err
}
for {
r, _, err := runes.ReadRune()
if errors.Is(err, io.EOF) {
return len(p), nil
}
if err != nil {
w.err = err
return len(p), w.err
}
if r == unicode.ReplacementChar {
continue
}
if w.inSingleQuote {
w.inSingleQuote = r != '\''
w.word.WriteRune(r)
continue
}
if w.inQuote {
w.inQuote = r != '"'
w.word.WriteRune(r)
continue
}
if w.inTag {
w.inSingleQuote = r == '\''
w.inQuote = r == '"'
w.inTag = r != '>'
if w.inTag || !unicode.IsSpace(r) {
w.word.WriteRune(r)
}
if !w.inTag {
if err := w.feed(); err != nil {
w.err = err
return len(p), w.err
}
w.lastSpace = unicode.IsSpace(r)
}
continue
}
if w.inWord {
w.inTag = r == '<'
w.inWord = !w.inTag && !unicode.IsSpace(r)
if !w.inWord {
if err := w.feed(); err != nil {
w.err = err
return len(p), w.err
}
w.lastSpace = unicode.IsSpace(r)
}
if w.inWord || w.inTag {
w.word.WriteRune(r)
}
continue
}
if unicode.IsSpace(r) {
w.lastSpace = true
continue
}
w.word.WriteRune(r)
w.inTag = r == '<'
w.inWord = !w.inTag
}
return len(p), w.err
}
func (w *wrapper) Flush() error {
if w.err != nil {
return w.err
}
if w.inTag || w.inWord {
if err := w.feed(); err != nil {
w.err = err
return err
}
}
w.width = 0
if err := w.feed(); err != nil {
w.err = err
return err
}
w.err = w.out.Flush()
return w.err
}