1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// TODO(bradfitz,adg): move to util
6
7package godoc
8
9import "io"
10
11var spaces = []byte("                                ") // 32 spaces seems like a good number
12
13const (
14	indenting = iota
15	collecting
16)
17
18// A tconv is an io.Writer filter for converting leading tabs into spaces.
19type tconv struct {
20	output io.Writer
21	state  int // indenting or collecting
22	indent int // valid if state == indenting
23	p      *Presentation
24}
25
26func (p *tconv) writeIndent() (err error) {
27	i := p.indent
28	for i >= len(spaces) {
29		i -= len(spaces)
30		if _, err = p.output.Write(spaces); err != nil {
31			return
32		}
33	}
34	// i < len(spaces)
35	if i > 0 {
36		_, err = p.output.Write(spaces[0:i])
37	}
38	return
39}
40
41func (p *tconv) Write(data []byte) (n int, err error) {
42	if len(data) == 0 {
43		return
44	}
45	pos := 0 // valid if p.state == collecting
46	var b byte
47	for n, b = range data {
48		switch p.state {
49		case indenting:
50			switch b {
51			case '\t':
52				p.indent += p.p.TabWidth
53			case '\n':
54				p.indent = 0
55				if _, err = p.output.Write(data[n : n+1]); err != nil {
56					return
57				}
58			case ' ':
59				p.indent++
60			default:
61				p.state = collecting
62				pos = n
63				if err = p.writeIndent(); err != nil {
64					return
65				}
66			}
67		case collecting:
68			if b == '\n' {
69				p.state = indenting
70				p.indent = 0
71				if _, err = p.output.Write(data[pos : n+1]); err != nil {
72					return
73				}
74			}
75		}
76	}
77	n = len(data)
78	if pos < n && p.state == collecting {
79		_, err = p.output.Write(data[pos:])
80	}
81	return
82}
83