| // Copyright 2013 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build go1.16 |
| // +build go1.16 |
| |
| package web |
| |
| import "io" |
| |
| var spaces = []byte(" ") // 32 spaces seems like a good number |
| |
| const ( |
| indenting = iota |
| collecting |
| ) |
| |
| // tabSpacer returns a writer that passes writes through to w, |
| // expanding tabs to one or more spaces ending at a width-spaces-aligned boundary. |
| func tabSpacer(w io.Writer, width int) io.Writer { |
| return &tconv{output: w, tabWidth: width} |
| } |
| |
| // A tconv is an io.Writer filter for converting leading tabs into spaces. |
| type tconv struct { |
| output io.Writer |
| state int // indenting or collecting |
| indent int // valid if state == indenting |
| tabWidth int |
| } |
| |
| func (t *tconv) writeIndent() (err error) { |
| i := t.indent |
| for i >= len(spaces) { |
| i -= len(spaces) |
| if _, err = t.output.Write(spaces); err != nil { |
| return |
| } |
| } |
| // i < len(spaces) |
| if i > 0 { |
| _, err = t.output.Write(spaces[0:i]) |
| } |
| return |
| } |
| |
| func (t *tconv) Write(data []byte) (n int, err error) { |
| if len(data) == 0 { |
| return |
| } |
| pos := 0 // valid if p.state == collecting |
| var b byte |
| for n, b = range data { |
| switch t.state { |
| case indenting: |
| switch b { |
| case '\t': |
| t.indent += t.tabWidth |
| case '\n': |
| t.indent = 0 |
| if _, err = t.output.Write(data[n : n+1]); err != nil { |
| return |
| } |
| case ' ': |
| t.indent++ |
| default: |
| t.state = collecting |
| pos = n |
| if err = t.writeIndent(); err != nil { |
| return |
| } |
| } |
| case collecting: |
| if b == '\n' { |
| t.state = indenting |
| t.indent = 0 |
| if _, err = t.output.Write(data[pos : n+1]); err != nil { |
| return |
| } |
| } |
| } |
| } |
| n = len(data) |
| if pos < n && t.state == collecting { |
| _, err = t.output.Write(data[pos:]) |
| } |
| return |
| } |