Commit 6358e1fa authored by Robert Griesemer's avatar Robert Griesemer

godoc: don't convert multi-line functions into one-liners by default

- new heuristic: if both the opening { and closing } braces are on the
  same line, and the function body doesn't contain comments or is other-
  wise too long (e.g. signature too long), it is formatted as a one-line
  function

- related cleanups along the way

- gofmt -w src misc led to no additional changes as expected

R=rsc, rsc1
CC=golang-dev, ken2, r
https://golang.org/cl/758041
parent c93273c0
...@@ -74,7 +74,7 @@ func (p *printer) setComment(g *ast.CommentGroup) { ...@@ -74,7 +74,7 @@ func (p *printer) setComment(g *ast.CommentGroup) {
// for some reason there are pending comments; this // for some reason there are pending comments; this
// should never happen - handle gracefully and flush // should never happen - handle gracefully and flush
// all comments up to g, ignore anything after that // all comments up to g, ignore anything after that
p.flush(g.List[0].Pos(), false) p.flush(g.List[0].Pos(), token.ILLEGAL)
} }
p.comments[0] = g p.comments[0] = g
p.cindex = 0 p.cindex = 0
...@@ -112,7 +112,6 @@ func (p *printer) identList(list []*ast.Ident, indent bool, multiLine *bool) { ...@@ -112,7 +112,6 @@ func (p *printer) identList(list []*ast.Ident, indent bool, multiLine *bool) {
// Compute the key size of a key:value expression. // Compute the key size of a key:value expression.
// Returns 0 if the expression doesn't fit onto a single line. // Returns 0 if the expression doesn't fit onto a single line.
func (p *printer) keySize(pair *ast.KeyValueExpr) int { func (p *printer) keySize(pair *ast.KeyValueExpr) int {
const infinity = 1e6 // larger than any source line
if p.nodeSize(pair, infinity) <= infinity { if p.nodeSize(pair, infinity) <= infinity {
// entire expression fits on one line - return key size // entire expression fits on one line - return key size
return p.nodeSize(pair.Key, infinity) return p.nodeSize(pair.Key, infinity)
...@@ -431,7 +430,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC ...@@ -431,7 +430,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC
if len(list) > 0 { if len(list) > 0 {
p.print(formfeed) p.print(formfeed)
} }
p.flush(rbrace, false) // make sure we don't loose the last line comment p.flush(rbrace, token.RBRACE) // make sure we don't loose the last line comment
p.setLineComment("// contains unexported fields") p.setLineComment("// contains unexported fields")
} }
...@@ -458,7 +457,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC ...@@ -458,7 +457,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC
if len(list) > 0 { if len(list) > 0 {
p.print(formfeed) p.print(formfeed)
} }
p.flush(rbrace, false) // make sure we don't loose the last line comment p.flush(rbrace, token.RBRACE) // make sure we don't loose the last line comment
p.setLineComment("// contains unexported methods") p.setLineComment("// contains unexported methods")
} }
...@@ -1224,16 +1223,26 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) { ...@@ -1224,16 +1223,26 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool { func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
const maxSize = 90 // adjust as appropriate, this is an approximate value pos1 := b.Pos()
pos2 := b.Rbrace
if pos1.IsValid() && pos2.IsValid() && pos1.Line != pos2.Line {
// opening and closing brace are on different lines - don't make it a one-liner
return false
}
if len(b.List) > 5 || p.commentBefore(pos2) {
// too many statements or there is a comment inside - don't make it a one-liner
return false
}
// otherwise, estimate body size
const maxSize = 100
bodySize := 0 bodySize := 0
switch { for i, s := range b.List {
case len(b.List) > 1 || p.commentBefore(b.Rbrace): if i > 0 {
return false // too many statements or there is a comment - all bets are off bodySize += 2 // space for a semicolon and blank
case len(b.List) == 1: }
bodySize = p.nodeSize(b.List[0], maxSize) bodySize += p.nodeSize(s, maxSize)
} }
// require both headers and overall size to be not "too large" return headerSize+bodySize <= maxSize
return headerSize <= maxSize/2 && headerSize+bodySize <= maxSize
} }
...@@ -1248,13 +1257,18 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLi ...@@ -1248,13 +1257,18 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLi
if isLit { if isLit {
sep = blank sep = blank
} }
p.print(sep, b.Pos(), token.LBRACE)
if len(b.List) > 0 { if len(b.List) > 0 {
p.print(sep, b.Pos(), token.LBRACE, blank) p.print(blank)
p.stmt(b.List[0], ignoreMultiLine) for i, s := range b.List {
p.print(blank, b.Rbrace, token.RBRACE) if i > 0 {
} else { p.print(token.SEMICOLON, blank)
p.print(sep, b.Pos(), token.LBRACE, b.Rbrace, token.RBRACE) }
p.stmt(s, ignoreMultiLine)
}
p.print(blank)
} }
p.print(b.Rbrace, token.RBRACE)
return return
} }
...@@ -1266,12 +1280,12 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLi ...@@ -1266,12 +1280,12 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLi
// distance returns the column difference between from and to if both // distance returns the column difference between from and to if both
// are on the same line; if they are on different lines (or unknown) // are on the same line; if they are on different lines (or unknown)
// the result is infinity (1<<30). // the result is infinity.
func distance(from, to token.Position) int { func distance(from, to token.Position) int {
if from.IsValid() && to.IsValid() && from.Line == to.Line { if from.IsValid() && to.IsValid() && from.Line == to.Line {
return to.Column - from.Column return to.Column - from.Column
} }
return 1 << 30 return infinity
} }
......
...@@ -53,8 +53,8 @@ var ( ...@@ -53,8 +53,8 @@ var (
// Special positions // Special positions
var noPos token.Position // use noPos when a position is needed but not known var noPos token.Position // use noPos when a position is needed but not known
var infinity = token.Position{Offset: 1 << 30, Line: 1 << 30} // use infinity to indicate the end of the source var infinity = 1 << 30
// Use ignoreMultiLine if the multiLine information is not important. // Use ignoreMultiLine if the multiLine information is not important.
...@@ -640,17 +640,16 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) { ...@@ -640,17 +640,16 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
// intersperseComments consumes all comments that appear before the next token // intersperseComments consumes all comments that appear before the next token
// and prints it together with the buffered whitespace (i.e., the whitespace // tok and prints it together with the buffered whitespace (i.e., the whitespace
// that needs to be written before the next token). A heuristic is used to mix // that needs to be written before the next token). A heuristic is used to mix
// the comments and whitespace. The isKeyword parameter indicates if the next // the comments and whitespace. intersperseComments returns true if a pending
// token is a keyword or not. intersperseComments returns true if a pending
// formfeed was dropped from the whitespace buffer. // formfeed was dropped from the whitespace buffer.
// //
func (p *printer) intersperseComments(next token.Position, isKeyword bool) (droppedFF bool) { func (p *printer) intersperseComments(next token.Position, tok token.Token) (droppedFF bool) {
var last *ast.Comment var last *ast.Comment
for ; p.commentBefore(next); p.cindex++ { for ; p.commentBefore(next); p.cindex++ {
for _, c := range p.comments[p.cindex].List { for _, c := range p.comments[p.cindex].List {
p.writeCommentPrefix(c.Pos(), next, last == nil, isKeyword) p.writeCommentPrefix(c.Pos(), next, last == nil, tok.IsKeyword())
p.writeComment(c) p.writeComment(c)
last = c last = c
} }
...@@ -663,8 +662,8 @@ func (p *printer) intersperseComments(next token.Position, isKeyword bool) (drop ...@@ -663,8 +662,8 @@ func (p *printer) intersperseComments(next token.Position, isKeyword bool) (drop
p.write([]byte{' '}) p.write([]byte{' '})
} }
// ensure that there is a newline after a //-style comment // ensure that there is a newline after a //-style comment
// or if we are at the end of a file after a /*-style comment // or if we are before a closing '}' or at the end of a file
return p.writeCommentSuffix(last.Text[1] == '/' || next.Offset == infinity.Offset) return p.writeCommentSuffix(last.Text[1] == '/' || tok == token.RBRACE || tok == token.EOF)
} }
// no comment was written - we should never reach here since // no comment was written - we should never reach here since
...@@ -743,7 +742,7 @@ func (p *printer) print(args ...interface{}) { ...@@ -743,7 +742,7 @@ func (p *printer) print(args ...interface{}) {
next := p.pos // estimated position of next item next := p.pos // estimated position of next item
var data []byte var data []byte
var tag HTMLTag var tag HTMLTag
isKeyword := false var tok token.Token
switch x := f.(type) { switch x := f.(type) {
case whiteSpace: case whiteSpace:
if x == ignore { if x == ignore {
...@@ -785,7 +784,7 @@ func (p *printer) print(args ...interface{}) { ...@@ -785,7 +784,7 @@ func (p *printer) print(args ...interface{}) {
} else { } else {
data = []byte(x.String()) data = []byte(x.String())
} }
isKeyword = x.IsKeyword() tok = x
case token.Position: case token.Position:
if x.IsValid() { if x.IsValid() {
next = x // accurate position of next item next = x // accurate position of next item
...@@ -797,7 +796,7 @@ func (p *printer) print(args ...interface{}) { ...@@ -797,7 +796,7 @@ func (p *printer) print(args ...interface{}) {
p.pos = next p.pos = next
if data != nil { if data != nil {
droppedFF := p.flush(next, isKeyword) droppedFF := p.flush(next, tok)
// intersperse extra newlines if present in the source // intersperse extra newlines if present in the source
// (don't do this in flush as it will cause extra newlines // (don't do this in flush as it will cause extra newlines
...@@ -820,14 +819,15 @@ func (p *printer) commentBefore(next token.Position) bool { ...@@ -820,14 +819,15 @@ func (p *printer) commentBefore(next token.Position) bool {
// Flush prints any pending comments and whitespace occuring // Flush prints any pending comments and whitespace occuring
// textually before the position of the next item. Flush returns // textually before the position of the next token tok. Flush
// true if a pending formfeed character was dropped from the // returns true if a pending formfeed character was dropped
// whitespace buffer as a result of interspersing comments. // from the whitespace buffer as a result of interspersing
// comments.
// //
func (p *printer) flush(next token.Position, isKeyword bool) (droppedFF bool) { func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
if p.commentBefore(next) { if p.commentBefore(next) {
// if there are comments before the next item, intersperse them // if there are comments before the next item, intersperse them
droppedFF = p.intersperseComments(next, isKeyword) droppedFF = p.intersperseComments(next, tok)
} else { } else {
// otherwise, write any leftover whitespace // otherwise, write any leftover whitespace
p.writeWhitespace(len(p.buffer)) p.writeWhitespace(len(p.buffer))
...@@ -1026,7 +1026,7 @@ func (cfg *Config) Fprint(output io.Writer, node interface{}) (int, os.Error) { ...@@ -1026,7 +1026,7 @@ func (cfg *Config) Fprint(output io.Writer, node interface{}) (int, os.Error) {
p.errors <- os.NewError(fmt.Sprintf("printer.Fprint: unsupported node type %T", n)) p.errors <- os.NewError(fmt.Sprintf("printer.Fprint: unsupported node type %T", n))
runtime.Goexit() runtime.Goexit()
} }
p.flush(infinity, false) p.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF)
p.errors <- nil // no errors p.errors <- nil // no errors
}() }()
err := <-p.errors // wait for completion of goroutine err := <-p.errors // wait for completion of goroutine
......
...@@ -411,7 +411,8 @@ func _() { ...@@ -411,7 +411,8 @@ func _() {
// Some interesting interspersed comments // Some interesting interspersed comments
func _( /* this */ x /* is */ /* an */ int) {} func _( /* this */ x /* is */ /* an */ int) {
}
func _( /* no params */ ) {} func _( /* no params */ ) {}
...@@ -421,7 +422,13 @@ func _() { ...@@ -421,7 +422,13 @@ func _() {
func ( /* comment1 */ T /* comment2 */ ) _() {} func ( /* comment1 */ T /* comment2 */ ) _() {}
func _() { /* one-liner */ } func _() { /* one-liner */
}
func _() {
_ = 0
/* closing curly brace should be on new line */
}
// Line comments with tabs // Line comments with tabs
......
...@@ -424,6 +424,10 @@ func (/* comment1 */ T /* comment2 */) _() {} ...@@ -424,6 +424,10 @@ func (/* comment1 */ T /* comment2 */) _() {}
func _() { /* one-liner */ } func _() { /* one-liner */ }
func _() {
_ = 0
/* closing curly brace should be on new line */ }
// Line comments with tabs // Line comments with tabs
func _() { func _() {
......
...@@ -356,7 +356,8 @@ func _() { ...@@ -356,7 +356,8 @@ func _() {
// formatting of structs // formatting of structs
type _ struct{} type _ struct{}
type _ struct { /* this comment should be visible */ } type _ struct { /* this comment should be visible */
}
type _ struct { type _ struct {
// this comment should be visible and properly indented // this comment should be visible and properly indented
...@@ -588,15 +589,31 @@ func _() {} ...@@ -588,15 +589,31 @@ func _() {}
func _() {} func _() {}
func _() { f(1, 2, 3) } func _() { f(1, 2, 3) }
func _(x int) int { return x + 1 } func _(x int) int { y := x; return y + 1 }
func _() int { type T struct{} } func _() int { type T struct{}; var x T; return x }
// these must remain multi-line since they are multi-line in the source
func _() {
f(1, 2, 3)
}
func _(x int) int {
y := x
return y + 1
}
func _() int {
type T struct{}
var x T
return x
}
// making function declarations safe for new semicolon rules // making function declarations safe for new semicolon rules
func _() { /* one-line func */ } func _() { /* multi-line func because of comment */
}
func _() { func _() {
/* one-line func */ } /* multi-line func because block is on multiple lines */
}
// ellipsis parameters // ellipsis parameters
......
...@@ -581,22 +581,27 @@ func _() {} // an empty line before this function ...@@ -581,22 +581,27 @@ func _() {} // an empty line before this function
func _() {} func _() {}
func _() {} func _() {}
func _() { f(1, 2, 3) }
func _(x int) int { y := x; return y+1 }
func _() int { type T struct{}; var x T; return x }
// these must remain multi-line since they are multi-line in the source
func _() { func _() {
f(1, 2, 3) f(1, 2, 3)
} }
func _(x int) int { func _(x int) int {
return x+1 y := x; return y+1
} }
func _() int { func _() int {
type T struct{} type T struct{}; var x T; return x
} }
// making function declarations safe for new semicolon rules // making function declarations safe for new semicolon rules
func _() { /* one-line func */ } func _() { /* multi-line func because of comment */ }
func _() { func _() {
/* one-line func */ } /* multi-line func because block is on multiple lines */ }
// ellipsis parameters // ellipsis parameters
......
...@@ -210,14 +210,31 @@ func _() { ...@@ -210,14 +210,31 @@ func _() {
func _() { func _() {
// one-line function literals // one-line function literals (body is on a single line)
_ = func() {} _ = func() {}
_ = func() int { return 0 } _ = func() int { return 0 }
_ = func(x, y int) bool { return x < y } _ = func(x, y int) bool { m := (x + y) / 2; return m < 0 }
f(func() {}) // multi-line function literals (body is not on one line)
f(func() int { return 0 }) _ = func() {
f(func(x, y int) bool { return x < y }) }
_ = func() int {
return 0
}
_ = func(x, y int) bool {
m := (x + y) / 2
return x < y
}
f(func() {
})
f(func() int {
return 0
})
f(func(x, y int) bool {
m := (x + y) / 2
return x < y
})
} }
......
...@@ -206,22 +206,27 @@ _ = `foo ...@@ -206,22 +206,27 @@ _ = `foo
func _() { func _() {
// one-line function literals // one-line function literals (body is on a single line)
_ = func() {} _ = func() {}
_ = func() int { return 0 }
_ = func(x, y int) bool { m := (x+y)/2; return m < 0 }
// multi-line function literals (body is not on one line)
_ = func() {
}
_ = func() int { _ = func() int {
return 0 return 0
} }
_ = func(x, y int) bool { _ = func(x, y int) bool {
return x < y m := (x+y)/2; return x < y }
}
f(func() {}) f(func() {
})
f(func() int { f(func() int {
return 0 return 0
}) })
f(func(x, y int) bool { f(func(x, y int) bool {
return x < y m := (x+y)/2; return x < y })
})
} }
......
...@@ -210,14 +210,31 @@ func _() { ...@@ -210,14 +210,31 @@ func _() {
func _() { func _() {
// one-line function literals // one-line function literals (body is on a single line)
_ = func() {} _ = func() {}
_ = func() int { return 0 } _ = func() int { return 0 }
_ = func(x, y int) bool { return x < y } _ = func(x, y int) bool { m := (x + y) / 2; return m < 0 }
f(func() {}) // multi-line function literals (body is not on one line)
f(func() int { return 0 }) _ = func() {
f(func(x, y int) bool { return x < y }) }
_ = func() int {
return 0
}
_ = func(x, y int) bool {
m := (x + y) / 2
return x < y
}
f(func() {
})
f(func() int {
return 0
})
f(func(x, y int) bool {
m := (x + y) / 2
return x < y
})
} }
......
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