package html import ( "bufio" "bytes" "errors" "io" "unicode" ) type indentWriter struct { out *bufio.Writer indent string started, lineStarted bool err error } func indentLen(indent string) int { var l int r := []rune(indent) for _, ri := range r { if ri == '\t' { l += 8 continue } l++ } return l } func newIndentWriter(out io.Writer, indent string) *indentWriter { return &indentWriter{ out: bufio.NewWriter(out), indent: indent, } } func (w *indentWriter) write(r ...rune) { if w.err != nil { return } for _, ri := range r { if _, w.err = w.out.WriteRune(ri); w.err != nil { return } } } func (w *indentWriter) Write(p []byte) (int, error) { if w.err != nil { return 0, w.err } if len(p) == 0 { return 0, nil } 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 r == '\n' { w.write('\n') w.started = true w.lineStarted = false continue } if w.started && !w.lineStarted { w.write([]rune(w.indent)...) } w.write(r) w.started = true w.lineStarted = true } return len(p), w.err } func (w *indentWriter) Flush() error { if w.err != nil { return w.err } w.err = w.out.Flush() return w.err }