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 }