"bytes"
"go/ast"
"go/token"
+ "strings"
)
}
-// TODO(gri): The code for printing lead and line comments
-// should be eliminated in favor of reusing the
-// comment intersperse mechanism above somehow.
-
-// Print a list of individual comments.
-func (p *printer) commentList(list []*ast.Comment) {
- for i, c := range list {
- t := c.Text
- // TODO(gri): this needs to be styled like normal comments
- p.print(c.Pos(), t)
- if t[1] == '/' && i+1 < len(list) {
- //-style comment which is not at the end; print a newline
- p.print(newline)
- }
+// setComment sets g as the next comment if g != nil and if node comments
+// are enabled - this mode is used when printing source code fragments such
+// as exports only. It assumes that there are no other pending comments to
+// intersperse.
+func (p *printer) setComment(g *ast.CommentGroup) {
+ if g == nil || !p.useNodeComments {
+ return
}
-}
-
-
-// Print a lead comment followed by a newline.
-func (p *printer) leadComment(d *ast.CommentGroup) {
- // Ignore the comment if we have comments interspersed (p.comment != nil).
- if p.comments == nil && d != nil {
- p.commentList(d.List)
- p.print(newline)
- }
-}
-
-
-// Print a tab followed by a line comment.
-// A newline must be printed afterwards since
-// the comment may be a //-style comment.
-func (p *printer) lineComment(d *ast.CommentGroup) {
- // Ignore the comment if we have comments interspersed (p.comment != nil).
- if p.comments == nil && d != nil {
- p.print(vtab)
- p.commentList(d.List)
+ if p.comments == nil {
+ // initialize p.comments lazily
+ p.comments = make([]*ast.CommentGroup, 1)
+ } else if p.cindex < len(p.comments) {
+ // for some reason there are pending comments; this
+ // should never happen - handle gracefully and flush
+ // all comments up to g, ignore anything after that
+ p.flush(g.List[0].Pos(), false)
}
+ p.comments[0] = g
+ p.cindex = 0
}
}
+func (p *printer) setLineComment(text string) {
+ p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{noPos, strings.Bytes(text)}}})
+}
+
+
func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace token.Position, isIncomplete bool, ctxt exprContext) {
if !isIncomplete && !p.commentBefore(rbrace) {
// possibly a one-line struct/interface
}
ml = false
extraTabs := 0
- p.leadComment(f.Doc)
+ p.setComment(f.Doc)
if len(f.Names) > 0 {
// named fields
p.identList(f.Names, &ml)
}
if f.Comment != nil {
for ; extraTabs > 0; extraTabs-- {
- p.print(vtab)
+ p.print(sep)
}
- p.lineComment(f.Comment)
+ p.setComment(f.Comment)
}
}
if isIncomplete {
if len(list) > 0 {
p.print(formfeed)
}
- // TODO(gri): this needs to be styled like normal comments
- p.print("// contains unexported fields")
+ p.flush(rbrace, false) // make sure we don't loose the last line comment
+ p.setLineComment("// contains unexported fields")
}
} else { // interface
p.linebreak(f.Pos().Line, 1, 2, ignore, ml)
}
ml = false
- p.leadComment(f.Doc)
+ p.setComment(f.Doc)
if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
// method
p.expr(f.Names[0], &ml)
// embedded interface
p.expr(f.Type, &ml)
}
- p.lineComment(f.Comment)
+ p.setComment(f.Comment)
}
if isIncomplete {
if len(list) > 0 {
p.print(formfeed)
}
- // TODO(gri): this needs to be styled like normal comments
- p.print("// contains unexported methods")
+ p.flush(rbrace, false) // make sure we don't loose the last line comment
+ p.setLineComment("// contains unexported methods")
}
}
switch s := spec.(type) {
case *ast.ImportSpec:
- p.leadComment(s.Doc)
+ p.setComment(s.Doc)
if s.Name != nil {
p.expr(s.Name, multiLine)
p.print(blank)
comment = s.Comment
case *ast.ValueSpec:
- p.leadComment(s.Doc)
+ p.setComment(s.Doc)
p.identList(s.Names, multiLine) // always present
if n == 1 {
if s.Type != nil {
comment = s.Comment
case *ast.TypeSpec:
- p.leadComment(s.Doc)
+ p.setComment(s.Doc)
p.expr(s.Name, multiLine)
if n == 1 {
p.print(blank)
for ; extraTabs > 0; extraTabs-- {
p.print(vtab)
}
- p.lineComment(comment)
+ p.setComment(comment)
}
}
// Sets multiLine to true if the declaration spans multiple lines.
func (p *printer) genDecl(d *ast.GenDecl, context declContext, multiLine *bool) {
- p.leadComment(d.Doc)
+ p.setComment(d.Doc)
p.print(d.Pos(), d.Tok, blank)
if d.Lparen.IsValid() {
// Sets multiLine to true if the declaration spans multiple lines.
func (p *printer) funcDecl(d *ast.FuncDecl, multiLine *bool) {
- p.leadComment(d.Doc)
+ p.setComment(d.Doc)
p.print(d.Pos(), token.FUNC, blank)
if recv := d.Recv; recv != nil {
// method: print receiver
func (p *printer) file(src *ast.File) {
- p.leadComment(src.Doc)
+ p.setComment(src.Doc)
p.print(src.Pos(), token.PACKAGE, blank)
p.expr(src.Name, ignoreMultiLine)
lastTaggedLine int // last line for which a line tag was written
// The list of all source comments, in order of appearance.
- comments []*ast.CommentGroup // may be nil
- cindex int // current comment index
+ comments []*ast.CommentGroup // may be nil
+ cindex int // current comment index
+ useNodeComments bool // if not set, ignore lead and line comments of nodes
}
// immediately following the data.
//
func (p *printer) writeItem(pos token.Position, data []byte, tag HTMLTag) {
- p.pos = pos
+ if pos.IsValid() {
+ // continue with previous position if we don't have a valid pos
+ p.pos = pos
+ }
if debug {
// do not update p.pos - use write0
p.write0(strings.Bytes(fmt.Sprintf("[%d:%d]", pos.Line, pos.Column)))
}
p.buffer = p.buffer[0 : i+1]
p.buffer[i] = x
- case []byte:
- // TODO(gri): remove this case once commentList
- // handles comments correctly
- data = x
- case string:
- // TODO(gri): remove this case once fieldList
- // handles comments correctly
- data = strings.Bytes(x)
case *ast.Ident:
if p.Styler != nil {
data, tag = p.Styler.Ident(x)
go func() {
switch n := node.(type) {
case ast.Expr:
+ p.useNodeComments = true
p.expr(n, ignoreMultiLine)
case ast.Stmt:
+ p.useNodeComments = true
p.stmt(n, ignoreMultiLine)
case ast.Decl:
+ p.useNodeComments = true
p.decl(n, atTop, ignoreMultiLine)
case *ast.File:
p.comments = n.Comments
+ p.useNodeComments = n.Comments == nil
p.file(n)
default:
p.errors <- os.NewError(fmt.Sprintf("printer.Fprint: unsupported node type %T", n))
// The SZ struct; it is empty.
type SZ struct{}
-
// The S0 struct; no field is exported.
type S0 struct {
// contains unexported fields
}
-
// The S1 struct; some fields are not exported.
type S1 struct {
S0
// contains unexported fields
}
-
// The S2 struct; all fields are exported.
type S2 struct {
S1
A, B, C float // 3 exported fields
}
-
// The IZ interface; it is empty.
type SZ interface{}
-
// The I0 interface; no method is exported.
type I0 interface {
// contains unexported methods
}
-
// The I1 interface; some methods are not exported.
type I1 interface {
I0
// contains unexported methods
}
-
// The I2 interface; all methods are exported.
type I2 interface {
I0
F(x float) float // exported method
G(x float) float // exported method
}
+
+// The S3 struct; all comments except for the last one must appear in the export.
+type S3 struct {
+ // lead comment for F1
+ F1 int // line comment for F1
+ // lead comment for F2
+ F2 int // line comment for F2
+ // contains unexported fields
+}