| // 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 |
| } |