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 }