blob: 17fe293654f0e516037b2e4c0e73fb3808539e58 [file] [log] [blame]
// 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 godoc
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
}