Commit 38e7fddc authored by Robert Griesemer's avatar Robert Griesemer

Show BUG comments in godoc:

ast.go:
- rename Comments -> CommentGroup (less confusion)
- change all comments/docs to be *CommentGroup

filter.go:
- do not remove unassociated comments from program as part
  of export filtering (they are needed by doc.go for BUG comments)

scanner.go:
- exclude '\n' from //-style comments

parser.go:
- rewrote collection of comments: now all collected comments
  are *ast.CommentGroups
- clarified distinction between leading and trailing comments
- fixed a bug in comment collection (parseGenDecl);
  corresponding test case is in printer/testdata
- extra documentation

doc.go:
- collect BUG comments
- corresponding fix for parser bug in comment collection

comment.go:
- simplified regex

printer.go:
- adjust comment printing to new representation

printer_test.go, testdata/*:
- enable printing of doc comments
- extended tests

package.html, package.txt:
- added Bugs section

gofmt:
- enable printing of doc comments

R=rsc
DELTA=339  (126 added, 38 deleted, 175 changed)
OCL=31403
CL=31416
parent ec23467e
...@@ -51,4 +51,11 @@ ...@@ -51,4 +51,11 @@
{.end} {.end}
{.end} {.end}
{.end} {.end}
{.section Bugs}
<hr />
<h2>Bugs</h2>
{.repeated section @}
{@|html-comment}
{.end}
{.end}
{.end} {.end}
...@@ -58,4 +58,12 @@ TYPES ...@@ -58,4 +58,12 @@ TYPES
{.end} {.end}
{.end} {.end}
{.end} {.end}
{.section Bugs}
BUGS
{.repeated section @}
{@}
{.end}
{.end}
{.end} {.end}
...@@ -48,7 +48,7 @@ func parserMode() uint { ...@@ -48,7 +48,7 @@ func parserMode() uint {
func printerMode() uint { func printerMode() uint {
mode := uint(0); mode := printer.DocComments;
if *optcommas { if *optcommas {
mode |= printer.OptCommas; mode |= printer.OptCommas;
} }
......
...@@ -85,15 +85,17 @@ type Decl interface { ...@@ -85,15 +85,17 @@ type Decl interface {
// A Comment node represents a single //-style or /*-style comment. // A Comment node represents a single //-style or /*-style comment.
type Comment struct { type Comment struct {
token.Position; // beginning position of the comment token.Position; // beginning position of the comment
Text []byte; // the comment text (without '\n' for //-style comments) Text []byte; // comment text (excluding '\n' for //-style comments)
EndLine int; // the line where the comment ends
} }
// A Comments node represents a sequence of single comments // A CommentGroup represents a sequence of single comments
// with no other tokens and no empty lines between. // with no other tokens and no empty lines between.
// //
type Comments []*Comment type CommentGroup struct {
List []*Comment;
EndLine int; // line where the last comment in the group ends
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
...@@ -110,11 +112,11 @@ type ( ...@@ -110,11 +112,11 @@ type (
// a method in an interface type, or a parameter/result declaration // a method in an interface type, or a parameter/result declaration
// in a signature. // in a signature.
Field struct { Field struct {
Doc Comments; // associated documentation; or nil Doc *CommentGroup; // associated documentation; or nil
Names []*Ident; // field/method/parameter names; nil if anonymous field Names []*Ident; // field/method/parameter names; nil if anonymous field
Type Expr; // field/method/parameter type Type Expr; // field/method/parameter type
Tag []*StringLit; // field tag; or nil Tag []*StringLit; // field tag; or nil
Comment *Comment; // trailing comment on same line; or nil Comment *CommentGroup; // trailing comments on same line; or nil
}; };
) )
...@@ -670,28 +672,28 @@ type ( ...@@ -670,28 +672,28 @@ type (
// An ImportSpec node represents a single package import. // An ImportSpec node represents a single package import.
ImportSpec struct { ImportSpec struct {
Doc Comments; // associated documentation; or nil Doc *CommentGroup; // associated documentation; or nil
Name *Ident; // local package name (including "."); or nil Name *Ident; // local package name (including "."); or nil
Path []*StringLit; // package path Path []*StringLit; // package path
Comment *Comment; // trailing comment on same line; or nil Comment *CommentGroup; // trailing comments on same line; or nil
}; };
// A ValueSpec node represents a constant or variable declaration // A ValueSpec node represents a constant or variable declaration
// (ConstSpec or VarSpec production). // (ConstSpec or VarSpec production).
ValueSpec struct { ValueSpec struct {
Doc Comments; // associated documentation; or nil Doc *CommentGroup; // associated documentation; or nil
Names []*Ident; // value names Names []*Ident; // value names
Type Expr; // value type; or nil Type Expr; // value type; or nil
Values []Expr; // initial values; or nil Values []Expr; // initial values; or nil
Comment *Comment; // trailing comment on same line; or nil Comment *CommentGroup; // trailing comments on same line; or nil
}; };
// A TypeSpec node represents a type declaration (TypeSpec production). // A TypeSpec node represents a type declaration (TypeSpec production).
TypeSpec struct { TypeSpec struct {
Doc Comments; // associated documentation; or nil Doc *CommentGroup; // associated documentation; or nil
Name *Ident; // type name Name *Ident; // type name
Type Expr; Type Expr;
Comment *Comment; // trailing comment on same line; or nil Comment *CommentGroup; // trailing comments on same line; or nil
}; };
) )
...@@ -719,7 +721,7 @@ type ( ...@@ -719,7 +721,7 @@ type (
// token.VAR *ValueSpec // token.VAR *ValueSpec
// //
GenDecl struct { GenDecl struct {
Doc Comments; // associated documentation; or nil Doc *CommentGroup; // associated documentation; or nil
token.Position; // position of Tok token.Position; // position of Tok
Tok token.Token; // IMPORT, CONST, TYPE, VAR Tok token.Token; // IMPORT, CONST, TYPE, VAR
Lparen token.Position; // position of '(', if any Lparen token.Position; // position of '(', if any
...@@ -729,7 +731,7 @@ type ( ...@@ -729,7 +731,7 @@ type (
// A FuncDecl node represents a function declaration. // A FuncDecl node represents a function declaration.
FuncDecl struct { FuncDecl struct {
Doc Comments; // associated documentation; or nil Doc *CommentGroup; // associated documentation; or nil
Recv *Field; // receiver (methods); or nil (functions) Recv *Field; // receiver (methods); or nil (functions)
Name *Ident; // function/method name Name *Ident; // function/method name
Type *FuncType; // position of Func keyword, parameters and results Type *FuncType; // position of Func keyword, parameters and results
...@@ -768,9 +770,9 @@ func (d *FuncDecl) Visit(v DeclVisitor) { v.DoFuncDecl(d); } ...@@ -768,9 +770,9 @@ func (d *FuncDecl) Visit(v DeclVisitor) { v.DoFuncDecl(d); }
// for an entire source file. // for an entire source file.
// //
type Program struct { type Program struct {
Doc Comments; // associated documentation; or nil Doc *CommentGroup; // associated documentation; or nil
token.Position; // position of "package" keyword token.Position; // position of "package" keyword
Name *Ident; // package name Name *Ident; // package name
Decls []Decl; // top-level declarations Decls []Decl; // top-level declarations
Comments []*Comment; // list of unassociated comments Comments []*CommentGroup; // list of unassociated comments
} }
...@@ -186,6 +186,5 @@ func FilterExports(prog *Program) bool { ...@@ -186,6 +186,5 @@ func FilterExports(prog *Program) bool {
} }
} }
prog.Decls = prog.Decls[0 : j]; prog.Decls = prog.Decls[0 : j];
prog.Comments = nil; // remove unassociated comments
return j > 0; return j > 0;
} }
...@@ -34,7 +34,7 @@ func makeRex(s string) *regexp.Regexp { ...@@ -34,7 +34,7 @@ func makeRex(s string) *regexp.Regexp {
// TODO(rsc): Cannot use var initialization for regexps, // TODO(rsc): Cannot use var initialization for regexps,
// because Regexp constructor needs threads. // because Regexp constructor needs threads.
func setupRegexps() { func setupRegexps() {
comment_markers = makeRex("^/(/|\\*) ?"); comment_markers = makeRex("^/[/*] ?");
trailing_whitespace = makeRex("[ \t\r]+$"); trailing_whitespace = makeRex("[ \t\r]+$");
comment_junk = makeRex("^[ \t]*(/\\*|\\*/)[ \t]*$"); comment_junk = makeRex("^[ \t]*(/\\*|\\*/)[ \t]*$");
} }
......
...@@ -31,11 +31,12 @@ type typeDoc struct { ...@@ -31,11 +31,12 @@ type typeDoc struct {
type DocReader struct { type DocReader struct {
name string; // package name name string; // package name
path string; // import path path string; // import path
doc ast.Comments; // package documentation, if any doc *ast.CommentGroup; // package documentation, if any
consts *vector.Vector; // list of *ast.GenDecl consts *vector.Vector; // list of *ast.GenDecl
types map[string] *typeDoc; types map[string] *typeDoc;
vars *vector.Vector; // list of *ast.GenDecl vars *vector.Vector; // list of *ast.GenDecl
funcs map[string] *ast.FuncDecl; funcs map[string] *ast.FuncDecl;
bugs *vector.Vector; // list of *ast.CommentGroup
} }
...@@ -49,6 +50,7 @@ func (doc *DocReader) Init(pkg, imp string) { ...@@ -49,6 +50,7 @@ func (doc *DocReader) Init(pkg, imp string) {
doc.types = make(map[string] *typeDoc); doc.types = make(map[string] *typeDoc);
doc.vars = vector.New(0); doc.vars = vector.New(0);
doc.funcs = make(map[string] *ast.FuncDecl); doc.funcs = make(map[string] *ast.FuncDecl);
doc.bugs = vector.New(0);
} }
...@@ -131,11 +133,18 @@ func (doc *DocReader) addDecl(decl ast.Decl) { ...@@ -131,11 +133,18 @@ func (doc *DocReader) addDecl(decl ast.Decl) {
case token.TYPE: case token.TYPE:
// types are handled individually // types are handled individually
var noPos token.Position; var noPos token.Position;
for i, spec := range d.Specs { for _, spec := range d.Specs {
// make a (fake) GenDecl node for this TypeSpec // make a (fake) GenDecl node for this TypeSpec
// (we need to do this here - as opposed to just // (we need to do this here - as opposed to just
// for printing - so we don't lose the GenDecl // for printing - so we don't lose the GenDecl
// documentation) // documentation)
//
// TODO(gri): Consider just collecting the TypeSpec
// node (and copy in the GenDecl.doc if there is no
// doc in the TypeSpec - this is currently done in
// makeTypeDocs below). Simpler data structures, but
// would lose GenDecl documentation if the TypeSpec
// has documentation as well.
s := spec.(*ast.TypeSpec); s := spec.(*ast.TypeSpec);
doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, noPos, []ast.Spec{s}, noPos}); doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, noPos, []ast.Spec{s}, noPos});
} }
...@@ -150,10 +159,25 @@ func (doc *DocReader) addDecl(decl ast.Decl) { ...@@ -150,10 +159,25 @@ func (doc *DocReader) addDecl(decl ast.Decl) {
} }
func copyCommentList(list []*ast.Comment) []*ast.Comment {
copy := make([]*ast.Comment, len(list));
for i, c := range list {
copy[i] = c;
}
return copy;
}
var bug_markers *regexp.Regexp; // Regexp constructor needs threads - cannot use init expression
// AddProgram adds the AST for a source file to the DocReader. // AddProgram adds the AST for a source file to the DocReader.
// Adding the same AST multiple times is a no-op. // Adding the same AST multiple times is a no-op.
// //
func (doc *DocReader) AddProgram(prog *ast.Program) { func (doc *DocReader) AddProgram(prog *ast.Program) {
if bug_markers == nil {
bug_markers = makeRex("^/[/*][ \t]*BUG(\\([^)]*\\))?:?[ \t]*");
}
if doc.name != prog.Name.Value { if doc.name != prog.Name.Value {
panic("package names don't match"); panic("package names don't match");
} }
...@@ -165,22 +189,39 @@ func (doc *DocReader) AddProgram(prog *ast.Program) { ...@@ -165,22 +189,39 @@ func (doc *DocReader) AddProgram(prog *ast.Program) {
} }
// add all declarations // add all declarations
for i, decl := range prog.Decls { for _, decl := range prog.Decls {
doc.addDecl(decl); doc.addDecl(decl);
} }
// collect BUG(...) comments
for _, c := range prog.Comments {
text := c.List[0].Text;
m := bug_markers.Execute(string(text));
if len(m) > 0 {
// found a BUG comment;
// push a copy of the comment w/o the BUG prefix
list := copyCommentList(c.List);
list[0].Text = text[m[1] : len(text)];
doc.bugs.Push(&ast.CommentGroup{list, c.EndLine});
}
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Conversion to external representation // Conversion to external representation
func astComment(comments ast.Comments) string { func astComment(comment *ast.CommentGroup) string {
text := make([]string, len(comments)); if comment != nil {
for i, c := range comments { text := make([]string, len(comment.List));
text[i] = string(c.Text); for i, c := range comment.List {
text[i] = string(c.Text);
}
return commentText(text);
} }
return commentText(text); return "";
} }
// ValueDoc is the documentation for a group of declared // ValueDoc is the documentation for a group of declared
// values, either vars or consts. // values, either vars or consts.
// //
...@@ -252,7 +293,7 @@ func (p sortFuncDoc) Less(i, j int) bool { return p[i].Name < p[j].Name; } ...@@ -252,7 +293,7 @@ func (p sortFuncDoc) Less(i, j int) bool { return p[i].Name < p[j].Name; }
func makeFuncDocs(m map[string] *ast.FuncDecl) []*FuncDoc { func makeFuncDocs(m map[string] *ast.FuncDecl) []*FuncDoc {
d := make([]*FuncDoc, len(m)); d := make([]*FuncDoc, len(m));
i := 0; i := 0;
for name, f := range m { for _, f := range m {
doc := new(FuncDoc); doc := new(FuncDoc);
doc.Doc = astComment(f.Doc); doc.Doc = astComment(f.Doc);
if f.Recv != nil { if f.Recv != nil {
...@@ -296,14 +337,19 @@ func (p sortTypeDoc) Less(i, j int) bool { ...@@ -296,14 +337,19 @@ func (p sortTypeDoc) Less(i, j int) bool {
// NOTE(rsc): This would appear not to be correct for type ( ) // NOTE(rsc): This would appear not to be correct for type ( )
// blocks, but the doc extractor above has split them into // blocks, but the doc extractor above has split them into
// individual statements. // individual declarations.
func makeTypeDocs(m map[string] *typeDoc) []*TypeDoc { func makeTypeDocs(m map[string] *typeDoc) []*TypeDoc {
d := make([]*TypeDoc, len(m)); d := make([]*TypeDoc, len(m));
i := 0; i := 0;
for name, old := range m { for _, old := range m {
typespec := old.decl.Specs[0].(*ast.TypeSpec); typespec := old.decl.Specs[0].(*ast.TypeSpec);
t := new(TypeDoc); t := new(TypeDoc);
t.Doc = astComment(typespec.Doc); doc := typespec.Doc;
if doc == nil {
// no doc associated with the spec, use the declaration doc, if any
doc = old.decl.Doc;
}
t.Doc = astComment(doc);
t.Type = typespec; t.Type = typespec;
t.Factories = makeFuncDocs(old.factories); t.Factories = makeFuncDocs(old.factories);
t.Methods = makeFuncDocs(old.methods); t.Methods = makeFuncDocs(old.methods);
...@@ -317,6 +363,15 @@ func makeTypeDocs(m map[string] *typeDoc) []*TypeDoc { ...@@ -317,6 +363,15 @@ func makeTypeDocs(m map[string] *typeDoc) []*TypeDoc {
} }
func makeBugDocs(v *vector.Vector) []string {
d := make([]string, v.Len());
for i := 0; i < v.Len(); i++ {
d[i] = astComment(v.At(i).(*ast.CommentGroup));
}
return d;
}
// PackageDoc is the documentation for an entire package. // PackageDoc is the documentation for an entire package.
// //
type PackageDoc struct { type PackageDoc struct {
...@@ -327,6 +382,7 @@ type PackageDoc struct { ...@@ -327,6 +382,7 @@ type PackageDoc struct {
Types []*TypeDoc; Types []*TypeDoc;
Vars []*ValueDoc; Vars []*ValueDoc;
Funcs []*FuncDoc; Funcs []*FuncDoc;
Bugs []string;
} }
...@@ -341,6 +397,7 @@ func (doc *DocReader) Doc() *PackageDoc { ...@@ -341,6 +397,7 @@ func (doc *DocReader) Doc() *PackageDoc {
p.Vars = makeValueDocs(doc.vars); p.Vars = makeValueDocs(doc.vars);
p.Types = makeTypeDocs(doc.types); p.Types = makeTypeDocs(doc.types);
p.Funcs = makeFuncDocs(doc.funcs); p.Funcs = makeFuncDocs(doc.funcs);
p.Bugs = makeBugDocs(doc.bugs);
return p; return p;
} }
...@@ -351,8 +408,8 @@ func (doc *DocReader) Doc() *PackageDoc { ...@@ -351,8 +408,8 @@ func (doc *DocReader) Doc() *PackageDoc {
// Does s look like a regular expression? // Does s look like a regular expression?
func isRegexp(s string) bool { func isRegexp(s string) bool {
metachars := ".(|)*+?^$[]"; metachars := ".(|)*+?^$[]";
for i, c := range s { for _, c := range s {
for j, m := range metachars { for _, m := range metachars {
if c == m { if c == m {
return true return true
} }
...@@ -363,7 +420,7 @@ func isRegexp(s string) bool { ...@@ -363,7 +420,7 @@ func isRegexp(s string) bool {
func match(s string, a []string) bool { func match(s string, a []string) bool {
for i, t := range a { for _, t := range a {
if isRegexp(t) { if isRegexp(t) {
if matched, err := regexp.Match(t, s); matched { if matched, err := regexp.Match(t, s); matched {
return true; return true;
...@@ -378,10 +435,10 @@ func match(s string, a []string) bool { ...@@ -378,10 +435,10 @@ func match(s string, a []string) bool {
func matchDecl(d *ast.GenDecl, names []string) bool { func matchDecl(d *ast.GenDecl, names []string) bool {
for i, d := range d.Specs { for _, d := range d.Specs {
switch v := d.(type) { switch v := d.(type) {
case *ast.ValueSpec: case *ast.ValueSpec:
for j, name := range v.Names { for _, name := range v.Names {
if match(name.Value, names) { if match(name.Value, names) {
return true; return true;
} }
...@@ -398,7 +455,7 @@ func matchDecl(d *ast.GenDecl, names []string) bool { ...@@ -398,7 +455,7 @@ func matchDecl(d *ast.GenDecl, names []string) bool {
func filterValueDocs(a []*ValueDoc, names []string) []*ValueDoc { func filterValueDocs(a []*ValueDoc, names []string) []*ValueDoc {
w := 0; w := 0;
for i, vd := range a { for _, vd := range a {
if matchDecl(vd.Decl, names) { if matchDecl(vd.Decl, names) {
a[w] = vd; a[w] = vd;
w++; w++;
...@@ -410,7 +467,7 @@ func filterValueDocs(a []*ValueDoc, names []string) []*ValueDoc { ...@@ -410,7 +467,7 @@ func filterValueDocs(a []*ValueDoc, names []string) []*ValueDoc {
func filterFuncDocs(a []*FuncDoc, names []string) []*FuncDoc { func filterFuncDocs(a []*FuncDoc, names []string) []*FuncDoc {
w := 0; w := 0;
for i, fd := range a { for _, fd := range a {
if match(fd.Name, names) { if match(fd.Name, names) {
a[w] = fd; a[w] = fd;
w++; w++;
...@@ -422,7 +479,7 @@ func filterFuncDocs(a []*FuncDoc, names []string) []*FuncDoc { ...@@ -422,7 +479,7 @@ func filterFuncDocs(a []*FuncDoc, names []string) []*FuncDoc {
func filterTypeDocs(a []*TypeDoc, names []string) []*TypeDoc { func filterTypeDocs(a []*TypeDoc, names []string) []*TypeDoc {
w := 0; w := 0;
for i, td := range a { for _, td := range a {
match := false; match := false;
if matchDecl(td.Decl, names) { if matchDecl(td.Decl, names) {
match = true; match = true;
......
This diff is collapsed.
...@@ -31,7 +31,7 @@ type printer struct { ...@@ -31,7 +31,7 @@ type printer struct {
output io.Writer; output io.Writer;
mode uint; mode uint;
errors chan os.Error; errors chan os.Error;
comments ast.Comments; // list of unassociated comments; or nil comments []*ast.CommentGroup; // list of unassociated comments; or nil
// current state (changes during printing) // current state (changes during printing)
written int; // number of bytes written written int; // number of bytes written
...@@ -40,8 +40,8 @@ type printer struct { ...@@ -40,8 +40,8 @@ type printer struct {
pos token.Position; // output position (possibly estimated) in "AST space" pos token.Position; // output position (possibly estimated) in "AST space"
// comments // comments
cindex int; // the current comment index cindex int; // the current comment group index
cpos token.Position; // the position of the next comment cpos token.Position; // the position of the next comment group
} }
...@@ -53,14 +53,14 @@ func (p *printer) hasComment(pos token.Position) bool { ...@@ -53,14 +53,14 @@ func (p *printer) hasComment(pos token.Position) bool {
func (p *printer) nextComment() { func (p *printer) nextComment() {
p.cindex++; p.cindex++;
if p.comments != nil && p.cindex < len(p.comments) && p.comments[p.cindex] != nil { if p.comments != nil && p.cindex < len(p.comments) && p.comments[p.cindex] != nil {
p.cpos = p.comments[p.cindex].Pos(); p.cpos = p.comments[p.cindex].List[0].Pos();
} else { } else {
p.cpos = token.Position{1<<30, 1<<30, 1}; // infinite p.cpos = token.Position{1<<30, 1<<30, 1}; // infinite
} }
} }
func (p *printer) setComments(comments ast.Comments) { func (p *printer) setComments(comments []*ast.CommentGroup) {
p.comments = comments; p.comments = comments;
p.cindex = -1; p.cindex = -1;
p.nextComment(); p.nextComment();
...@@ -125,6 +125,8 @@ func (p *printer) write(data []byte) { ...@@ -125,6 +125,8 @@ func (p *printer) write(data []byte) {
} }
// TODO(gri) Enable this code to intersperse comments
/*
// Reduce contiguous sequences of '\t' in a []byte to a single '\t'. // Reduce contiguous sequences of '\t' in a []byte to a single '\t'.
func untabify(src []byte) []byte { func untabify(src []byte) []byte {
dst := make([]byte, len(src)); dst := make([]byte, len(src));
...@@ -149,12 +151,13 @@ func (p *printer) adjustSpacingAndMergeComments() { ...@@ -149,12 +151,13 @@ func (p *printer) adjustSpacingAndMergeComments() {
// - add extra newlines if so indicated by source positions // - add extra newlines if so indicated by source positions
} }
} }
*/
func (p *printer) print(args ...) { func (p *printer) print(args ...) {
v := reflect.NewValue(args).(*reflect.StructValue); v := reflect.NewValue(args).(*reflect.StructValue);
for i := 0; i < v.NumField(); i++ { for i := 0; i < v.NumField(); i++ {
p.adjustSpacingAndMergeComments(); //p.adjustSpacingAndMergeComments(); // TODO(gri) enable to intersperse comments
f := v.Field(i); f := v.Field(i);
switch x := f.Interface().(type) { switch x := f.Interface().(type) {
case int: case int:
...@@ -187,24 +190,35 @@ func (p *printer) optSemis() bool { ...@@ -187,24 +190,35 @@ func (p *printer) optSemis() bool {
} }
func (p *printer) comment(c *ast.Comment) { // Print a list of individual comments.
if c != nil { func (p *printer) commentList(list []*ast.Comment) {
text := c.Text; for i, c := range list {
if text[1] == '/' { t := c.Text;
// //-style comment - dont print the '\n' p.print(c.Pos(), t);
// TODO scanner should probably not include the '\n' in this case if t[1] == '/' && i+1 < len(list) {
text = text[0 : len(text)-1]; //-style comment which is not at the end; print a newline
p.print(newline);
} }
p.print(tab, c.Pos(), text); // tab-separated trailing comment
} }
} }
func (p *printer) doc(d ast.Comments) { // Print a leading comment followed by a newline.
if p.mode & DocComments != 0 { func (p *printer) leadingComment(d *ast.CommentGroup) {
for _, c := range d { if p.mode & DocComments != 0 && d != nil {
p.print(c.Pos(), c.Text); p.commentList(d.List);
} p.print(newline);
}
}
// Print a tab followed by a trailing comment.
// A newline must be printed afterwards since
// the comment may be a //-style comment.
func (p *printer) trailingComment(d *ast.CommentGroup) {
if d != nil {
p.print(tab);
p.commentList(d.List);
} }
} }
...@@ -286,13 +300,13 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok ...@@ -286,13 +300,13 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
p.print(blank, lbrace, token.LBRACE, +1, newline); p.print(blank, lbrace, token.LBRACE, +1, newline);
var lastWasAnon bool; // true if the previous line was an anonymous field var lastWasAnon bool; // true if the previous line was an anonymous field
var lastComment *ast.Comment; // the comment from the previous line var lastComment *ast.CommentGroup; // the comment from the previous line
for i, f := range list { for i, f := range list {
// at least one visible identifier or anonymous field // at least one visible identifier or anonymous field
isAnon := len(f.Names) == 0; isAnon := len(f.Names) == 0;
if i > 0 { if i > 0 {
p.print(token.SEMICOLON); p.print(token.SEMICOLON);
p.comment(lastComment); p.trailingComment(lastComment);
if lastWasAnon == isAnon { if lastWasAnon == isAnon {
// previous and current line have same structure; // previous and current line have same structure;
// continue with existing columns // continue with existing columns
...@@ -307,7 +321,7 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok ...@@ -307,7 +321,7 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
} }
} }
p.doc(f.Doc); p.leadingComment(f.Doc);
if !isAnon { if !isAnon {
p.identList(f.Names); p.identList(f.Names);
p.print(tab); p.print(tab);
...@@ -336,7 +350,7 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok ...@@ -336,7 +350,7 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
if p.optSemis() { if p.optSemis() {
p.print(token.SEMICOLON); p.print(token.SEMICOLON);
} }
p.comment(lastComment); p.trailingComment(lastComment);
p.print(-1, newline, rbrace, token.RBRACE); p.print(-1, newline, rbrace, token.RBRACE);
...@@ -521,7 +535,7 @@ func (p *printer) expr(x ast.Expr) bool { ...@@ -521,7 +535,7 @@ func (p *printer) expr(x ast.Expr) bool {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Statements // Statements
func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) func (p *printer) decl(decl ast.Decl) (comment *ast.CommentGroup, optSemi bool)
// Print the statement list indented, but without a newline after the last statement. // Print the statement list indented, but without a newline after the last statement.
func (p *printer) stmtList(list []ast.Stmt) { func (p *printer) stmtList(list []ast.Stmt) {
...@@ -607,14 +621,14 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) { ...@@ -607,14 +621,14 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) {
p.print("BadStmt"); p.print("BadStmt");
case *ast.DeclStmt: case *ast.DeclStmt:
var comment *ast.Comment; var comment *ast.CommentGroup;
comment, optSemi = p.decl(s.Decl); comment, optSemi = p.decl(s.Decl);
if comment != nil { if comment != nil {
// Trailing comments of declarations in statement lists // Trailing comments of declarations in statement lists
// are not associated with the declaration in the parser; // are not associated with the declaration in the parser;
// this case should never happen. Print anyway to continue // this case should never happen. Print anyway to continue
// gracefully. // gracefully.
p.comment(comment); p.trailingComment(comment);
p.print(newline); p.print(newline);
} }
...@@ -768,10 +782,10 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) { ...@@ -768,10 +782,10 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) {
// Returns trailing comment, if any, and whether a separating semicolon is optional. // Returns trailing comment, if any, and whether a separating semicolon is optional.
// //
func (p *printer) spec(spec ast.Spec) (comment *ast.Comment, optSemi bool) { func (p *printer) spec(spec ast.Spec) (comment *ast.CommentGroup, optSemi bool) {
switch s := spec.(type) { switch s := spec.(type) {
case *ast.ImportSpec: case *ast.ImportSpec:
p.doc(s.Doc); p.leadingComment(s.Doc);
if s.Name != nil { if s.Name != nil {
p.expr(s.Name); p.expr(s.Name);
} }
...@@ -780,7 +794,7 @@ func (p *printer) spec(spec ast.Spec) (comment *ast.Comment, optSemi bool) { ...@@ -780,7 +794,7 @@ func (p *printer) spec(spec ast.Spec) (comment *ast.Comment, optSemi bool) {
comment = s.Comment; comment = s.Comment;
case *ast.ValueSpec: case *ast.ValueSpec:
p.doc(s.Doc); p.leadingComment(s.Doc);
p.identList(s.Names); p.identList(s.Names);
if s.Type != nil { if s.Type != nil {
p.print(blank); // TODO switch to tab? (indent problem with structs) p.print(blank); // TODO switch to tab? (indent problem with structs)
...@@ -794,7 +808,7 @@ func (p *printer) spec(spec ast.Spec) (comment *ast.Comment, optSemi bool) { ...@@ -794,7 +808,7 @@ func (p *printer) spec(spec ast.Spec) (comment *ast.Comment, optSemi bool) {
comment = s.Comment; comment = s.Comment;
case *ast.TypeSpec: case *ast.TypeSpec:
p.doc(s.Doc); p.leadingComment(s.Doc);
p.expr(s.Name); p.expr(s.Name);
p.print(blank); // TODO switch to tab? (but indent problem with structs) p.print(blank); // TODO switch to tab? (but indent problem with structs)
optSemi = p.expr(s.Type); optSemi = p.expr(s.Type);
...@@ -809,13 +823,13 @@ func (p *printer) spec(spec ast.Spec) (comment *ast.Comment, optSemi bool) { ...@@ -809,13 +823,13 @@ func (p *printer) spec(spec ast.Spec) (comment *ast.Comment, optSemi bool) {
// Returns true if a separating semicolon is optional. // Returns true if a separating semicolon is optional.
func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) { func (p *printer) decl(decl ast.Decl) (comment *ast.CommentGroup, optSemi bool) {
switch d := decl.(type) { switch d := decl.(type) {
case *ast.BadDecl: case *ast.BadDecl:
p.print(d.Pos(), "BadDecl"); p.print(d.Pos(), "BadDecl");
case *ast.GenDecl: case *ast.GenDecl:
p.doc(d.Doc); p.leadingComment(d.Doc);
p.print(d.Pos(), d.Tok, blank); p.print(d.Pos(), d.Tok, blank);
if d.Lparen.IsValid() { if d.Lparen.IsValid() {
...@@ -826,7 +840,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) { ...@@ -826,7 +840,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) {
for i, s := range d.Specs { for i, s := range d.Specs {
if i > 0 { if i > 0 {
p.print(token.SEMICOLON); p.print(token.SEMICOLON);
p.comment(comment); p.trailingComment(comment);
p.print(newline); p.print(newline);
} }
comment, optSemi = p.spec(s); comment, optSemi = p.spec(s);
...@@ -834,7 +848,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) { ...@@ -834,7 +848,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) {
if p.optSemis() { if p.optSemis() {
p.print(token.SEMICOLON); p.print(token.SEMICOLON);
} }
p.comment(comment); p.trailingComment(comment);
p.print(-1, newline); p.print(-1, newline);
} }
p.print(d.Rparen, token.RPAREN); p.print(d.Rparen, token.RPAREN);
...@@ -847,7 +861,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) { ...@@ -847,7 +861,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) {
} }
case *ast.FuncDecl: case *ast.FuncDecl:
p.doc(d.Doc); p.leadingComment(d.Doc);
p.print(d.Pos(), token.FUNC, blank); p.print(d.Pos(), token.FUNC, blank);
if recv := d.Recv; recv != nil { if recv := d.Recv; recv != nil {
// method: print receiver // method: print receiver
...@@ -880,11 +894,9 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) { ...@@ -880,11 +894,9 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) {
// Programs // Programs
func (p *printer) program(prog *ast.Program) { func (p *printer) program(prog *ast.Program) {
// set unassociated comments p.setComments(prog.Comments); // unassociated comments
// TODO enable this once comments are properly interspersed
// p.setComments(prog.Comments);
p.doc(prog.Doc); p.leadingComment(prog.Doc);
p.print(prog.Pos(), token.PACKAGE, blank); p.print(prog.Pos(), token.PACKAGE, blank);
p.expr(prog.Name); p.expr(prog.Name);
...@@ -894,7 +906,7 @@ func (p *printer) program(prog *ast.Program) { ...@@ -894,7 +906,7 @@ func (p *printer) program(prog *ast.Program) {
if p.optSemis() { if p.optSemis() {
p.print(token.SEMICOLON); p.print(token.SEMICOLON);
} }
p.comment(comment); p.trailingComment(comment);
} }
p.print(newline); p.print(newline);
...@@ -922,7 +934,7 @@ func Fprint(output io.Writer, node interface{}, mode uint) (int, os.Error) { ...@@ -922,7 +934,7 @@ func Fprint(output io.Writer, node interface{}, mode uint) (int, os.Error) {
p.stmt(n); p.stmt(n);
case ast.Decl: case ast.Decl:
comment, _ := p.decl(n); comment, _ := p.decl(n);
p.comment(comment); p.trailingComment(comment); // no newline at end
case *ast.Program: case *ast.Program:
p.program(n); p.program(n);
default: default:
......
...@@ -61,7 +61,7 @@ func check(t *testing.T, source, golden string, exports bool) { ...@@ -61,7 +61,7 @@ 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); w := tabwriter.NewWriter(&buf, tabwidth, padding, tabchar, 0);
Fprint(w, prog, 0); Fprint(w, prog, DocComments);
w.Flush(); w.Flush();
res := buf.Data(); res := buf.Data();
......
...@@ -9,6 +9,7 @@ const ( ...@@ -9,6 +9,7 @@ const (
c2 // c2 c2 // c2
) )
// The T type.
type T struct { type T struct {
a, b, c int // 3 fields a, b, c int // 3 fields
} }
......
package main package main
// The T type.
type T struct type T struct
...@@ -9,6 +9,7 @@ const ( ...@@ -9,6 +9,7 @@ const (
) )
// The T type.
type T struct { type T struct {
a, b, c int // 3 fields a, b, c int // 3 fields
} }
......
...@@ -141,7 +141,8 @@ func (S *Scanner) scanComment(pos token.Position) { ...@@ -141,7 +141,8 @@ func (S *Scanner) scanComment(pos token.Position) {
for S.ch >= 0 { for S.ch >= 0 {
S.next(); S.next();
if S.ch == '\n' { if S.ch == '\n' {
S.next(); // '\n' belongs to the comment // '\n' is not part of the comment
// (the comment ends on the same line where it started)
return; return;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment