}
p.print(x.Lbrace, token.LBRACE)
p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace)
- // do not insert extra line breaks because of comments before
- // the closing '}' as it might break the code if there is no
- // trailing ','
- p.print(noExtraLinebreak, x.Rbrace, token.RBRACE, noExtraLinebreak)
+ // do not insert extra line break following a /*-style comment
+ // before the closing '}' as it might break the code if there
+ // is no trailing ','
+ mode := noExtraLinebreak
+ // do not insert extra blank following a /*-style comment
+ // before the closing '}' unless the literal is empty
+ if len(x.Elts) > 0 {
+ mode |= noExtraBlank
+ }
+ p.print(mode, x.Rbrace, token.RBRACE, mode)
case *ast.Ellipsis:
p.print(token.ELLIPSIS)
// opening and closing brace are on different lines - don't make it a one-liner
return maxSize + 1
}
- if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) {
- // too many statements or there is a comment inside - don't make it a one-liner
+ if len(b.List) > 5 {
+ // too many statements - don't make it a one-liner
return maxSize + 1
}
// otherwise, estimate body size
- bodySize := 0
+ bodySize := p.commentSizeBefore(p.posFor(pos2))
for i, s := range b.List {
+ if bodySize > maxSize {
+ break // no need to continue
+ }
if i > 0 {
bodySize += 2 // space for a semicolon and blank
}
}
p.print(blank)
}
- p.print(b.Rbrace, token.RBRACE)
+ p.print(noExtraLinebreak, b.Rbrace, token.RBRACE, noExtraLinebreak)
return
}
type pmode int
const (
- noExtraLinebreak pmode = 1 << iota
+ noExtraBlank pmode = 1 << iota // disables extra blank after /*-style comment
+ noExtraLinebreak // disables extra line break after /*-style comment
)
+type commentInfo struct {
+ cindex int // current comment index
+ comment *ast.CommentGroup // = printer.comments[cindex]; or nil
+ commentOffset int // = printer.posFor(printer.comments[cindex].List[0].Pos()).Offset; or infinity
+ commentNewline bool // true if the comment group contains newlines
+}
+
type printer struct {
// Configuration (does not change after initialization)
Config
indent int // current indentation
mode pmode // current printer mode
impliedSemi bool // if set, a linebreak implies a semicolon
- lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace)
+ lastTok token.Token // last token printed (token.ILLEGAL if it's whitespace)
+ prevOpen token.Token // previous non-brace "open" token (, [, or token.ILLEGAL
wsbuf []whiteSpace // delayed white space
// Positions
// The list of all source comments, in order of appearance.
comments []*ast.CommentGroup // may be nil
- cindex int // current comment index
useNodeComments bool // if not set, ignore lead and line comments of nodes
// Information about p.comments[p.cindex]; set up by nextComment.
- comment *ast.CommentGroup // = p.comments[p.cindex]; or nil
- commentOffset int // = p.posFor(p.comments[p.cindex].List[0].Pos()).Offset; or infinity
- commentNewline bool // true if the comment group contains newlines
+ commentInfo
// Cache of already computed node sizes.
nodeSizes map[ast.Node]int
p.commentOffset = infinity
}
+// commentBefore returns true iff the current comment group occurs
+// before the next position in the source code and printing it does
+// not introduce implicit semicolons.
+//
+func (p *printer) commentBefore(next token.Position) bool {
+ return p.commentOffset < next.Offset && (!p.impliedSemi || !p.commentNewline)
+}
+
+// commentSizeBefore returns the estimated size of the
+// comments on the same line before the next position.
+//
+func (p *printer) commentSizeBefore(next token.Position) int {
+ // save/restore current p.commentInfo (p.nextComment() modifies it)
+ defer func(info commentInfo) {
+ p.commentInfo = info
+ }(p.commentInfo)
+
+ size := 0
+ for p.commentBefore(next) {
+ for _, c := range p.comment.List {
+ size += len(c.Text)
+ }
+ p.nextComment()
+ }
+ return size
+}
+
func (p *printer) internalError(msg ...interface{}) {
if debug {
fmt.Print(p.pos.String() + ": ")
if last != nil {
// if the last comment is a /*-style comment and the next item
- // follows on the same line but is not a comma or a "closing"
- // token, add an extra blank for separation
- if last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line && tok != token.COMMA &&
- tok != token.RPAREN && tok != token.RBRACK && tok != token.RBRACE {
+ // follows on the same line but is not a comma, and not a "closing"
+ // token immediately following its corresponding "opening" token,
+ // add an extra blank for separation unless explicitly disabled
+ if p.mode&noExtraBlank == 0 &&
+ last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line &&
+ tok != token.COMMA &&
+ (tok != token.RPAREN || p.prevOpen == token.LPAREN) &&
+ (tok != token.RBRACK || p.prevOpen == token.LBRACK) {
p.writeByte(' ', 1)
}
// ensure that there is a line break after a //-style comment,
}
// shift remaining entries down
- i := 0
- for ; n < len(p.wsbuf); n++ {
- p.wsbuf[i] = p.wsbuf[n]
- i++
- }
- p.wsbuf = p.wsbuf[0:i]
+ l := copy(p.wsbuf, p.wsbuf[n:])
+ p.wsbuf = p.wsbuf[:l]
}
// ----------------------------------------------------------------------------
var isLit bool
var impliedSemi bool // value for p.impliedSemi after this arg
+ // record previous opening token, if any
+ switch p.lastTok {
+ case token.ILLEGAL:
+ // ignore (white space)
+ case token.LPAREN, token.LBRACK:
+ p.prevOpen = p.lastTok
+ default:
+ // other tokens followed any opening token
+ p.prevOpen = token.ILLEGAL
+ }
+
switch x := arg.(type) {
case pmode:
// toggle printer mode
}
}
-// commentBefore returns true iff the current comment group occurs
-// before the next position in the source code and printing it does
-// not introduce implicit semicolons.
-//
-func (p *printer) commentBefore(next token.Position) (result bool) {
- return p.commentOffset < next.Offset && (!p.impliedSemi || !p.commentNewline)
-}
-
// flush prints any pending comments and whitespace occurring textually
// before the position of the next token tok. The flush result indicates
// if a newline was written or if a formfeed was dropped from the whitespace