1
0
html/wrap.go
2025-11-01 21:23:56 +01:00

148 lines
2.6 KiB
Go

package html
import (
"code.squareroundforest.org/arpio/textedit"
"io"
"unicode"
)
type wrapState struct {
line, word []rune
inWord, inTag, inSingleQuote, inQuote bool
lastSpace, started bool
}
type wrapper struct {
indent *textedit.Writer
wrap *textedit.Writer
}
func wrapFeed(width int, s wrapState) ([]rune, wrapState) {
var ret []rune
withSpace := s.lastSpace && len(s.line) > 0
l := len(s.line) + len(s.word)
if withSpace && len(s.word) > 0 {
l++
}
feedLine := l > width && len(s.line) > 0
if feedLine {
if s.started {
ret = append(ret, '\n')
}
ret = append(ret, s.line...)
s.line = nil
s.started = true
}
if !feedLine && withSpace {
s.line = append(s.line, ' ')
}
s.line = append(s.line, s.word...)
s.word = nil
return ret, s
}
func wrapEdit(width int) func(rune, wrapState) ([]rune, wrapState) {
return func(r rune, s wrapState) ([]rune, wrapState) {
var ret []rune
if s.inSingleQuote {
s.inSingleQuote = r != '\''
s.word = append(s.word, r)
return ret, s
}
if s.inQuote {
s.inQuote = r != '"'
s.word = append(s.word, r)
return ret, s
}
if s.inTag {
s.inSingleQuote = r == '\''
s.inQuote = r == '"'
s.inTag = r != '>'
s.inWord = !s.inTag && !unicode.IsSpace(r)
if s.inTag || !unicode.IsSpace(r) {
s.word = append(s.word, r)
}
if !s.inTag {
ret, s = wrapFeed(width, s)
s.lastSpace = unicode.IsSpace(r)
}
return ret, s
}
if s.inWord {
s.inTag = r == '<'
s.inWord = !s.inTag && !unicode.IsSpace(r)
if !s.inWord {
ret, s = wrapFeed(width, s)
s.lastSpace = unicode.IsSpace(r)
}
if s.inWord || s.inTag {
s.word = append(s.word, r)
}
return ret, s
}
if unicode.IsSpace(r) {
s.lastSpace = true
return ret, s
}
s.word = append(s.word, r)
s.inTag = r == '<'
s.inWord = !s.inTag
return ret, s
}
}
func wrapReleaseState(width int) func(wrapState) []rune {
return func(s wrapState) []rune {
var ret []rune
if s.inTag || s.inWord {
ret, s = wrapFeed(width, s)
}
ret1, _ := wrapFeed(0, s)
return append(ret, ret1...)
}
}
func newWrapper(out io.Writer, width int, indent string) *wrapper {
indentWriter := newIndentWriter(out, indent)
return &wrapper{
indent: indentWriter,
wrap: textedit.New(
indentWriter,
textedit.Func(
wrapEdit(width),
wrapReleaseState(width),
),
),
}
}
func (w *wrapper) Write(p []byte) (int, error) {
return w.wrap.Write(p)
}
func (w *wrapper) Flush() error {
if err := w.wrap.Flush(); err != nil {
return err
}
if err := w.indent.Flush(); err != nil {
return err
}
return nil
}