| // 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. | |
| // TODO(bradfitz,adg): move to util | |
| package godoc | |
| import "io" | |
| var spaces = []byte(" ") // 32 spaces seems like a good number | |
| const ( | |
| indenting = iota | |
| collecting | |
| ) | |
| // 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 | |
| p *Presentation | |
| } | |
| func (p *tconv) writeIndent() (err error) { | |
| i := p.indent | |
| for i >= len(spaces) { | |
| i -= len(spaces) | |
| if _, err = p.output.Write(spaces); err != nil { | |
| return | |
| } | |
| } | |
| // i < len(spaces) | |
| if i > 0 { | |
| _, err = p.output.Write(spaces[0:i]) | |
| } | |
| return | |
| } | |
| func (p *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 p.state { | |
| case indenting: | |
| switch b { | |
| case '\t': | |
| p.indent += p.p.TabWidth | |
| case '\n': | |
| p.indent = 0 | |
| if _, err = p.output.Write(data[n : n+1]); err != nil { | |
| return | |
| } | |
| case ' ': | |
| p.indent++ | |
| default: | |
| p.state = collecting | |
| pos = n | |
| if err = p.writeIndent(); err != nil { | |
| return | |
| } | |
| } | |
| case collecting: | |
| if b == '\n' { | |
| p.state = indenting | |
| p.indent = 0 | |
| if _, err = p.output.Write(data[pos : n+1]); err != nil { | |
| return | |
| } | |
| } | |
| } | |
| } | |
| n = len(data) | |
| if pos < n && p.state == collecting { | |
| _, err = p.output.Write(data[pos:]) | |
| } | |
| return | |
| } |