diff --git a/internal/godoc/godoc.go b/internal/godoc/godoc.go index 25b30632..47c2b0fb 100644 --- a/internal/godoc/godoc.go +++ b/internal/godoc/godoc.go @@ -605,7 +605,7 @@ func (p *Presentation) writeNode(w io.Writer, pageInfo *PageInfo, fset *token.Fi } mode := printer.TabIndent | printer.UseSpaces - err := (&printer.Config{Mode: mode, Tabwidth: p.TabWidth}).Fprint(&tconv{p: p, output: out}, fset, x) + err := (&printer.Config{Mode: mode, Tabwidth: p.TabWidth}).Fprint(TabSpacer(out, p.TabWidth), fset, x) if err != nil { log.Print(err) } diff --git a/internal/godoc/tab.go b/internal/godoc/tab.go index 944074e6..17fe2936 100644 --- a/internal/godoc/tab.go +++ b/internal/godoc/tab.go @@ -5,8 +5,6 @@ //go:build go1.16 // +build go1.16 -// TODO(bradfitz,adg): move to util - package godoc import "io" @@ -18,68 +16,74 @@ const ( 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 +// 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} } -func (p *tconv) writeIndent() (err error) { - i := p.indent +// 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 = p.output.Write(spaces); err != nil { + if _, err = t.output.Write(spaces); err != nil { return } } // i < len(spaces) if i > 0 { - _, err = p.output.Write(spaces[0:i]) + _, err = t.output.Write(spaces[0:i]) } return } -func (p *tconv) Write(data []byte) (n int, err error) { +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 p.state { + switch t.state { case indenting: switch b { case '\t': - p.indent += p.p.TabWidth + t.indent += t.tabWidth case '\n': - p.indent = 0 - if _, err = p.output.Write(data[n : n+1]); err != nil { + t.indent = 0 + if _, err = t.output.Write(data[n : n+1]); err != nil { return } case ' ': - p.indent++ + t.indent++ default: - p.state = collecting + t.state = collecting pos = n - if err = p.writeIndent(); err != nil { + if err = t.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 { + t.state = indenting + t.indent = 0 + if _, err = t.output.Write(data[pos : n+1]); err != nil { return } } } } n = len(data) - if pos < n && p.state == collecting { - _, err = p.output.Write(data[pos:]) + if pos < n && t.state == collecting { + _, err = t.output.Write(data[pos:]) } return }