1
0
mirror of https://github.com/golang/go synced 2024-11-26 00:38:00 -07:00
- prepare for generation of HTML id tags and links
- do HTML-escaping in central print routine
- move tabwriter setup into printer
- fixed various TODOs

godoc:
- removed tabwriter setup, need for various HTML-escaping

R=rsc
DELTA=210  (107 added, 36 deleted, 67 changed)
OCL=32612
CL=32616
This commit is contained in:
Robert Griesemer 2009-07-31 18:04:53 -07:00
parent d951ce4e45
commit 62718fb5d4
5 changed files with 166 additions and 95 deletions

View File

@ -47,7 +47,6 @@ import (
"strings"; "strings";
"sync"; "sync";
"syscall"; "syscall";
"tabwriter";
"template"; "template";
"time"; "time";
) )
@ -125,11 +124,6 @@ func isPkgDir(dir *os.Dir) bool {
} }
func makeTabwriter(writer io.Writer) *tabwriter.Writer {
return tabwriter.NewWriter(writer, *tabwidth, 1, byte(' '), 0);
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Parsing // Parsing
@ -201,55 +195,66 @@ func parse(path string, mode uint) (*ast.File, *parseErrors) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Templates // Templates
// Return text for an AST node. // Write an AST-node to w; optionally html-escaped.
func nodeText(node interface{}) []byte { func writeNode(w io.Writer, node interface{}, html bool) {
var buf bytes.Buffer; mode := printer.UseSpaces;
tw := makeTabwriter(&buf); if html {
printer.Fprint(tw, node, 0); mode |= printer.GenHTML;
tw.Flush(); }
return buf.Data(); printer.Fprint(w, node, mode, *tabwidth);
} }
// Convert x, whatever it is, to text form. // Write text to w; optionally html-escaped.
func toText(x interface{}) []byte { func writeText(w io.Writer, text []byte, html bool) {
type Stringer interface { String() string } if html {
template.HtmlEscape(w, text);
return;
}
w.Write(text);
}
// Write anything to w; optionally html-escaped.
func writeAny(w io.Writer, x interface{}, html bool) {
switch v := x.(type) { switch v := x.(type) {
case []byte: case []byte:
return v; writeText(w, v, html);
case string: case string:
return strings.Bytes(v); writeText(w, strings.Bytes(v), html);
case ast.Decl: case ast.Decl:
return nodeText(v); writeNode(w, v, html);
case ast.Expr: case ast.Expr:
return nodeText(v); writeNode(w, v, html);
case Stringer: default:
// last resort (AST nodes get a String method if html {
// from token.Position - don't call that one)
return strings.Bytes(v.String());
}
var buf bytes.Buffer; var buf bytes.Buffer;
fmt.Fprint(&buf, x); fmt.Fprint(&buf, x);
return buf.Data(); writeText(w, buf.Data(), true);
} else {
fmt.Fprint(w, x);
}
}
} }
// Template formatter for "html" format. // Template formatter for "html" format.
func htmlFmt(w io.Writer, x interface{}, format string) { func htmlFmt(w io.Writer, x interface{}, format string) {
template.HtmlEscape(w, toText(x)); writeAny(w, x, true);
} }
// Template formatter for "html-comment" format. // Template formatter for "html-comment" format.
func htmlCommentFmt(w io.Writer, x interface{}, format string) { func htmlCommentFmt(w io.Writer, x interface{}, format string) {
doc.ToHtml(w, toText(x)); var buf bytes.Buffer;
writeAny(&buf, x, false);
doc.ToHtml(w, buf.Data());
} }
// Template formatter for "" (default) format. // Template formatter for "" (default) format.
func textFmt(w io.Writer, x interface{}, format string) { func textFmt(w io.Writer, x interface{}, format string) {
w.Write(toText(x)); writeAny(w, x, false);
} }
@ -337,7 +342,7 @@ func serveGoSource(c *http.Conn, name string) {
var buf bytes.Buffer; var buf bytes.Buffer;
fmt.Fprintln(&buf, "<pre>"); fmt.Fprintln(&buf, "<pre>");
template.HtmlEscape(&buf, nodeText(prog)); writeNode(&buf, prog, true);
fmt.Fprintln(&buf, "</pre>"); fmt.Fprintln(&buf, "</pre>");
servePage(c, name + " - Go source", buf.Data()); servePage(c, name + " - Go source", buf.Data());

View File

@ -16,7 +16,6 @@ import (
pathutil "path"; pathutil "path";
"sort"; "sort";
"strings"; "strings";
"tabwriter";
) )
@ -34,8 +33,9 @@ var (
exports = flag.Bool("x", false, "show exports only"); exports = flag.Bool("x", false, "show exports only");
// layout control // layout control
tabwidth = flag.Int("tabwidth", 4, "tab width"); tabwidth = flag.Int("tabwidth", 8, "tab width");
usetabs = flag.Bool("tabs", false, "align with tabs instead of blanks"); rawformat = flag.Bool("rawformat", false, "do not use a tabwriter");
usespaces = flag.Bool("spaces", false, "align with blanks instead of tabs");
optcommas = flag.Bool("optcommas", false, "print optional commas"); optcommas = flag.Bool("optcommas", false, "print optional commas");
optsemis = flag.Bool("optsemis", false, "print optional semicolons"); optsemis = flag.Bool("optsemis", false, "print optional semicolons");
) )
@ -104,6 +104,12 @@ func getPackage(path string) (*ast.Package, os.Error) {
func printerMode() uint { func printerMode() uint {
mode := uint(0); mode := uint(0);
if *rawformat {
mode |= printer.RawFormat;
}
if *usespaces {
mode |= printer.UseSpaces;
}
if *optcommas { if *optcommas {
mode |= printer.OptCommas; mode |= printer.OptCommas;
} }
@ -114,15 +120,6 @@ func printerMode() uint {
} }
func makeTabwriter(writer io.Writer) *tabwriter.Writer {
padchar := byte(' ');
if *usetabs {
padchar = '\t';
}
return tabwriter.NewWriter(writer, *tabwidth, 1, padchar, 0);
}
func main() { func main() {
flag.Usage = usage; flag.Usage = usage;
flag.Parse(); flag.Parse();
@ -144,15 +141,21 @@ func main() {
} }
if !*silent { if !*silent {
w := makeTabwriter(os.Stdout);
if *exports { if *exports {
ast.PackageExports(pkg); ast.PackageExports(pkg);
printer.Fprint(w, ast.MergePackageFiles(pkg), printerMode()); // ignore errors _, err := printer.Fprint(os.Stdout, ast.MergePackageFiles(pkg), printerMode(), *tabwidth);
if err != nil {
fmt.Fprint(os.Stderr, err);
os.Exit(2);
}
} else { } else {
for _, src := range pkg.Files { for _, src := range pkg.Files {
printer.Fprint(w, src, printerMode()); // ignore errors _, err := printer.Fprint(os.Stdout, src, printerMode(), *tabwidth);
if err != nil {
fmt.Fprint(os.Stderr, err);
os.Exit(2);
}
} }
} }
w.Flush();
} }
} }

View File

@ -22,7 +22,7 @@ fmt.install: io.install os.install reflect.install strconv.install utf8.install
go/ast.install: go/token.install unicode.install utf8.install go/ast.install: go/token.install unicode.install utf8.install
go/doc.install: container/vector.install fmt.install go/ast.install go/token.install io.install once.install regexp.install sort.install strings.install template.install go/doc.install: container/vector.install fmt.install go/ast.install go/token.install io.install once.install regexp.install sort.install strings.install template.install
go/parser.install: bytes.install container/vector.install fmt.install go/ast.install go/scanner.install go/token.install io.install os.install path.install strings.install go/parser.install: bytes.install container/vector.install fmt.install go/ast.install go/scanner.install go/token.install io.install os.install path.install strings.install
go/printer.install: fmt.install go/ast.install go/token.install io.install os.install reflect.install strings.install go/printer.install: fmt.install go/ast.install go/token.install io.install os.install reflect.install strings.install tabwriter.install
go/scanner.install: bytes.install container/vector.install fmt.install go/token.install io.install os.install sort.install strconv.install unicode.install utf8.install go/scanner.install: bytes.install container/vector.install fmt.install go/token.install io.install os.install sort.install strconv.install unicode.install utf8.install
go/token.install: fmt.install strconv.install go/token.install: fmt.install strconv.install
gob.install: bytes.install fmt.install io.install math.install os.install reflect.install strings.install sync.install unicode.install gob.install: bytes.install fmt.install io.install math.install os.install reflect.install strings.install sync.install unicode.install

View File

@ -13,6 +13,7 @@ import (
"os"; "os";
"reflect"; "reflect";
"strings"; "strings";
"tabwriter";
) )
@ -26,7 +27,10 @@ const (
// to Fprint via the mode parameter. // to Fprint via the mode parameter.
// //
const ( const (
OptCommas = 1 << iota; // print optional commas GenHTML uint = 1 << iota; // generate HTML
RawFormat; // do not use a tabwriter; if set, UseSpaces is ignored
UseSpaces; // use spaces instead of tabs for indentation and alignment
OptCommas; // print optional commas
OptSemis; // print optional semicolons OptSemis; // print optional semicolons
) )
@ -41,6 +45,15 @@ const (
) )
var (
tabs = [...]byte{'\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t'};
newlines = [...]byte{'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'}; // more than maxNewlines
ampersand = strings.Bytes("&amp;");
lessthan = strings.Bytes("&lt;");
greaterthan = strings.Bytes("&gt;");
)
type printer struct { type printer struct {
// configuration (does not change after initialization) // configuration (does not change after initialization)
output io.Writer; output io.Writer;
@ -70,8 +83,7 @@ func (p *printer) init(output io.Writer, mode uint) {
// Writing to p.output is done with write0 which also handles errors. // Writing to p.output is done with write0 which also handles errors.
// It should only be called by write and debug routines which are not // Does not indent after newlines, or HTML-escape, or update p.pos.
// supposed to update the p.pos estimation.
// //
func (p *printer) write0(data []byte) { func (p *printer) write0(data []byte) {
n, err := p.output.Write(data); n, err := p.output.Write(data);
@ -85,22 +97,55 @@ func (p *printer) write0(data []byte) {
func (p *printer) write(data []byte) { func (p *printer) write(data []byte) {
i0 := 0; i0 := 0;
for i, b := range data { for i, b := range data {
if b == '\n' || b == '\f' { switch b {
// write segment ending in a newline/formfeed followed by indentation case '\n', '\f':
// TODO(gri) should convert '\f' into '\n' if the output is not going // write segment ending in b followed by indentation
// through tabwriter if p.mode & RawFormat != 0 && b == '\f' {
// no tabwriter - convert last byte into a newline
p.write0(data[i0 : i]);
p.write0(newlines[0 : 1]);
} else {
p.write0(data[i0 : i+1]); p.write0(data[i0 : i+1]);
// TODO(gri) should not write indentation is there is nothing else
// on the line
for j := p.indent; j > 0; j-- {
p.write0([]byte{'\t'}); // TODO(gri) don't do allocation in every iteration
} }
i0 = i+1;
// write indentation
// TODO(gri) should not write indentation if there is nothing else
// on the line
j := p.indent;
for ; j > len(tabs); j -= len(tabs) {
p.write0(&tabs);
}
p.write0(tabs[0 : j]);
// update p.pos // update p.pos
p.pos.Offset += i+1 - i0 + p.indent; p.pos.Offset += i+1 - i0 + p.indent;
p.pos.Line++; p.pos.Line++;
p.pos.Column = p.indent + 1; p.pos.Column = p.indent + 1;
// next segment start
i0 = i+1;
case '&', '<', '>':
if p.mode & GenHTML != 0 {
// write segment ending in b
p.write0(data[i0 : i]);
// write HTML-escaped b
var esc []byte;
switch b {
case '&': esc = ampersand;
case '<': esc = lessthan;
case '>': esc = greaterthan;
}
p.write0(esc);
// update p.pos
p.pos.Offset += i+1 - i0;
p.pos.Column += i+1 - i0;
// next segment start
i0 = i+1;
}
} }
} }
@ -114,33 +159,29 @@ func (p *printer) write(data []byte) {
} }
// TODO(gri) Don't go through write and make this more efficient.
func (p *printer) writeByte(b byte) {
p.write([]byte{b});
}
func (p *printer) writeNewlines(n int) { func (p *printer) writeNewlines(n int) {
if n > 0 {
if n > maxNewlines { if n > maxNewlines {
n = maxNewlines; n = maxNewlines;
} }
for ; n > 0; n-- { p.write(newlines[0 : n]);
p.writeByte('\n');
} }
} }
func (p *printer) writePos(pos token.Position) {
// use write0 so not to disturb the p.pos update by write
p.write0(strings.Bytes(fmt.Sprintf("[%d:%d]", pos.Line, pos.Column)));
}
func (p *printer) writeItem(pos token.Position, data []byte) { func (p *printer) writeItem(pos token.Position, data []byte) {
p.pos = pos; p.pos = pos;
if debug { if debug {
p.writePos(pos); // do not update p.pos - use write0
p.write0(strings.Bytes(fmt.Sprintf("[%d:%d]", pos.Line, pos.Column)));
} }
// TODO(gri) Enable once links are generated.
/*
if p.mode & GenHTML != 0 {
// do not HTML-escape or update p.pos - use write0
p.write0(strings.Bytes(fmt.Sprintf("<a id=%x></a>", pos.Offset)));
}
*/
p.write(data); p.write(data);
p.prev = p.pos; p.prev = p.pos;
} }
@ -172,7 +213,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
n := comment.Pos().Line - p.prev.Line; n := comment.Pos().Line - p.prev.Line;
if n == 0 { if n == 0 {
// comment on the same line as previous item; separate with tab // comment on the same line as previous item; separate with tab
p.writeByte('\t'); p.write(tabs[0 : 1]);
} else { } else {
// comment on a different line; separate with newlines // comment on a different line; separate with newlines
p.writeNewlines(n); p.writeNewlines(n);
@ -239,10 +280,16 @@ func (p *printer) intersperseComments(next token.Position) {
func (p *printer) writeWhitespace() { func (p *printer) writeWhitespace() {
var a [len(p.buffer)]byte;
for i := 0; i < p.buflen; i++ { for i := 0; i < p.buflen; i++ {
p.writeByte(byte(p.buffer[i])); a[i] = byte(p.buffer[i]);
} }
var b []byte = &a;
b = b[0 : p.buflen];
p.buflen = 0; p.buflen = 0;
p.write(b);
} }
@ -254,7 +301,7 @@ func (p *printer) writeWhitespace() {
// Whitespace is accumulated until a non-whitespace token appears. Any // Whitespace is accumulated until a non-whitespace token appears. Any
// comments that need to appear before that token are printed first, // comments that need to appear before that token are printed first,
// taking into account the amount and structure of any pending white- // taking into account the amount and structure of any pending white-
// space for best commemnt placement. Then, any leftover whitespace is // space for best comment placement. Then, any leftover whitespace is
// printed, followed by the actual token. // printed, followed by the actual token.
// //
func (p *printer) print(args ...) { func (p *printer) print(args ...) {
@ -930,8 +977,8 @@ func (p *printer) spec(spec ast.Spec) (comment *ast.CommentGroup, optSemi bool)
if s.Name != nil { if s.Name != nil {
p.expr(s.Name); p.expr(s.Name);
} }
// TODO fix for longer package names p.print(tab);
p.print(tab, s.Path[0].Pos(), s.Path[0].Value); p.expr(&ast.StringList{s.Path});
comment = s.Comment; comment = s.Comment;
case *ast.ValueSpec: case *ast.ValueSpec:
@ -1057,14 +1104,28 @@ func (p *printer) file(src *ast.File) {
// Fprint "pretty-prints" an AST node to output and returns the number of // Fprint "pretty-prints" an AST node to output and returns the number of
// bytes written, and an error, if any. The node type must be *ast.File, // bytes written, and an error, if any. The node type must be *ast.File,
// or assignment-compatible to ast.Expr, ast.Decl, or ast.Stmt. Printing is // or assignment-compatible to ast.Expr, ast.Decl, or ast.Stmt. Printing
// controlled by the mode parameter. For best results, the output should be // is controlled by the mode and tabwidth parameters.
// a tabwriter.Writer.
// //
func Fprint(output io.Writer, node interface{}, mode uint) (int, os.Error) { func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int) (int, os.Error) {
// setup tabwriter if needed and redirect output
var tw *tabwriter.Writer;
if mode & RawFormat == 0 {
padchar := byte('\t');
if mode & UseSpaces != 0 {
padchar = ' ';
}
var twmode uint;
if mode & GenHTML != 0 {
twmode = tabwriter.FilterHTML;
}
tw = tabwriter.NewWriter(output, tabwidth, 1, padchar, twmode);
output = tw;
}
// setup printer and print node
var p printer; var p printer;
p.init(output, mode); p.init(output, mode);
go func() { go func() {
switch n := node.(type) { switch n := node.(type) {
case ast.Expr: case ast.Expr:
@ -1085,5 +1146,10 @@ func Fprint(output io.Writer, node interface{}, mode uint) (int, os.Error) {
}(); }();
err := <-p.errors; // wait for completion of goroutine err := <-p.errors; // wait for completion of goroutine
// flush tabwriter, if any
if tw != nil {
tw.Flush(); // ignore errors
}
return p.written, err; return p.written, err;
} }

View File

@ -13,7 +13,6 @@ import (
"go/printer"; "go/printer";
"os"; "os";
"path"; "path";
"tabwriter";
"testing"; "testing";
) )
@ -21,8 +20,6 @@ import (
const ( const (
dataDir = "testdata"; dataDir = "testdata";
tabwidth = 4; tabwidth = 4;
padding = 1;
tabchar = '\t';
) )
@ -54,9 +51,9 @@ func check(t *testing.T, source, golden string, exports bool) {
// format source // format source
var buf bytes.Buffer; var buf bytes.Buffer;
w := tabwriter.NewWriter(&buf, tabwidth, padding, tabchar, 0); if _, err := Fprint(&buf, prog, 0, tabwidth); err != nil {
Fprint(w, prog, 0); t.Error(err);
w.Flush(); }
res := buf.Data(); res := buf.Data();
// update golden files if necessary // update golden files if necessary