Commit 9f65e99a authored by Robert Griesemer's avatar Robert Griesemer

go/printer, gofmt: don't write too many newlines

In some rare cases, gofmt would accept more than the maximum
number of empty lines (1) between source code snippets.

The actual change is in printer.go, lines 773-775; the rest
is some minor restructuring.

Applied gofmt -w src misc .

Fixes #2387.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5496047
parent 52c8107a
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package main package main
import ( import (
......
...@@ -16,7 +16,6 @@ const N = 10000 // make this bigger for a larger (and slower) test ...@@ -16,7 +16,6 @@ const N = 10000 // make this bigger for a larger (and slower) test
var data string // test data for write tests var data string // test data for write tests
var bytes []byte // test data; same as data but as a slice. var bytes []byte // test data; same as data but as a slice.
func init() { func init() {
bytes = make([]byte, N) bytes = make([]byte, N)
for i := 0; i < N; i++ { for i := 0; i < N; i++ {
......
...@@ -39,7 +39,10 @@ import ( ...@@ -39,7 +39,10 @@ import (
// future (not yet interspersed) comments in this function. // future (not yet interspersed) comments in this function.
// //
func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) { func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) {
n := p.nlines(line-p.pos.Line, min) n := nlimit(line - p.pos.Line)
if n < min {
n = min
}
if n > 0 { if n > 0 {
p.print(ws) p.print(ws)
if newSection { if newSection {
......
...@@ -18,8 +18,11 @@ import ( ...@@ -18,8 +18,11 @@ import (
"text/tabwriter" "text/tabwriter"
) )
const debug = false // enable for debugging const (
const infinity = 1 << 30 maxNewlines = 2 // max. number of newlines between source text
debug = false // enable for debugging
infinity = 1 << 30
)
type whiteSpace byte type whiteSpace byte
...@@ -89,21 +92,7 @@ func (p *printer) internalError(msg ...interface{}) { ...@@ -89,21 +92,7 @@ func (p *printer) internalError(msg ...interface{}) {
} }
} }
// nlines returns the adjusted number of linebreaks given the desired number // writeByte writes ch to p.output and updates p.pos.
// of breaks n such that min <= result <= max.
//
func (p *printer) nlines(n, min int) int {
const max = 2 // max. number of newlines
switch {
case n < min:
return min
case n > max:
return max
}
return n
}
// writeByte writes a single byte to p.output and updates p.pos.
func (p *printer) writeByte(ch byte) { func (p *printer) writeByte(ch byte) {
p.output.WriteByte(ch) p.output.WriteByte(ch)
p.pos.Offset++ p.pos.Offset++
...@@ -128,13 +117,11 @@ func (p *printer) writeByte(ch byte) { ...@@ -128,13 +117,11 @@ func (p *printer) writeByte(ch byte) {
} }
} }
// writeNewlines writes up to n newlines to p.output and updates p.pos. // writeByteN writes ch n times to p.output and updates p.pos.
// The actual number of newlines written is limited by nlines. func (p *printer) writeByteN(ch byte, n int) {
// nl must be one of '\n' or '\f'. for n > 0 {
// p.writeByte(ch)
func (p *printer) writeNewlines(n int, nl byte) { n--
for n = p.nlines(n, 0); n > 0; n-- {
p.writeByte(nl)
} }
} }
...@@ -223,8 +210,8 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as ...@@ -223,8 +210,8 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
} }
if pos.IsValid() && pos.Filename != p.last.Filename { if pos.IsValid() && pos.Filename != p.last.Filename {
// comment in a different file - separate with newlines (writeNewlines will limit the number) // comment in a different file - separate with newlines
p.writeNewlines(10, '\f') p.writeByteN('\f', maxNewlines)
return return
} }
...@@ -318,7 +305,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as ...@@ -318,7 +305,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
n = 1 n = 1
} }
if n > 0 { if n > 0 {
p.writeNewlines(n, '\f') p.writeByteN('\f', nlimit(n))
} }
p.indent = indent p.indent = indent
} }
...@@ -550,10 +537,11 @@ func (p *printer) writeComment(comment *ast.Comment) { ...@@ -550,10 +537,11 @@ func (p *printer) writeComment(comment *ast.Comment) {
// writeCommentSuffix writes a line break after a comment if indicated // writeCommentSuffix writes a line break after a comment if indicated
// and processes any leftover indentation information. If a line break // and processes any leftover indentation information. If a line break
// is needed, the kind of break (newline vs formfeed) depends on the // is needed, the kind of break (newline vs formfeed) depends on the
// pending whitespace. writeCommentSuffix returns true if a pending // pending whitespace. The writeCommentSuffix result indicates if a
// formfeed was dropped from the whitespace buffer. // newline was written or if a formfeed was dropped from the whitespace
// buffer.
// //
func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) { func (p *printer) writeCommentSuffix(needsLinebreak bool) (wroteNewline, droppedFF bool) {
for i, ch := range p.wsbuf { for i, ch := range p.wsbuf {
switch ch { switch ch {
case blank, vtab: case blank, vtab:
...@@ -566,6 +554,7 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) { ...@@ -566,6 +554,7 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
// but remember if we dropped any formfeeds // but remember if we dropped any formfeeds
if needsLinebreak { if needsLinebreak {
needsLinebreak = false needsLinebreak = false
wroteNewline = true
} else { } else {
if ch == formfeed { if ch == formfeed {
droppedFF = true droppedFF = true
...@@ -579,6 +568,7 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) { ...@@ -579,6 +568,7 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
// make sure we have a line break // make sure we have a line break
if needsLinebreak { if needsLinebreak {
p.writeByte('\n') p.writeByte('\n')
wroteNewline = true
} }
return return
...@@ -587,10 +577,10 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) { ...@@ -587,10 +577,10 @@ 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
// tok 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. intersperseComments returns true if a pending // the comments and whitespace. The intersperseComments result indicates if a
// formfeed was dropped from the whitespace buffer. // newline was written or if a formfeed was dropped from the whitespace buffer.
// //
func (p *printer) intersperseComments(next token.Position, tok token.Token) (droppedFF bool) { func (p *printer) intersperseComments(next token.Position, tok token.Token) (wroteNewline, 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 {
...@@ -618,7 +608,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro ...@@ -618,7 +608,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro
// no comment was written - we should never reach here since // no comment was written - we should never reach here since
// intersperseComments should not be called in that case // intersperseComments should not be called in that case
p.internalError("intersperseComments called without pending comments") p.internalError("intersperseComments called without pending comments")
return false return
} }
// whiteWhitespace writes the first n whitespace entries. // whiteWhitespace writes the first n whitespace entries.
...@@ -671,6 +661,14 @@ func (p *printer) writeWhitespace(n int) { ...@@ -671,6 +661,14 @@ func (p *printer) writeWhitespace(n int) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Printing interface // Printing interface
// nlines limits n to maxNewlines.
func nlimit(n int) int {
if n > maxNewlines {
n = maxNewlines
}
return n
}
func mayCombine(prev token.Token, next byte) (b bool) { func mayCombine(prev token.Token, next byte) (b bool) {
switch prev { switch prev {
case token.INT: case token.INT:
...@@ -765,17 +763,22 @@ func (p *printer) print(args ...interface{}) { ...@@ -765,17 +763,22 @@ func (p *printer) print(args ...interface{}) {
p.pos = next p.pos = next
if data != "" { if data != "" {
nl := byte('\n') wroteNewline, droppedFF := p.flush(next, tok)
if p.flush(next, tok) {
nl = '\f' // dropped formfeed before
}
// 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
// at the end of a file) - use formfeeds if we dropped one // at the end of a file)
// before n := nlimit(next.Line - p.pos.Line)
if n := next.Line - p.pos.Line; n > 0 { // don't exceed maxNewlines if we already wrote one
p.writeNewlines(n, nl) if wroteNewline && n == maxNewlines {
n = maxNewlines - 1
}
if n > 0 {
ch := byte('\n')
if droppedFF {
ch = '\f' // use formfeed since we dropped one before
}
p.writeByteN(ch, n)
} }
p.writeItem(next, data, isLit) p.writeItem(next, data, isLit)
...@@ -790,16 +793,15 @@ func (p *printer) commentBefore(next token.Position) bool { ...@@ -790,16 +793,15 @@ func (p *printer) commentBefore(next token.Position) bool {
return p.cindex < len(p.comments) && p.fset.Position(p.comments[p.cindex].List[0].Pos()).Offset < next.Offset return p.cindex < len(p.comments) && p.fset.Position(p.comments[p.cindex].List[0].Pos()).Offset < next.Offset
} }
// Flush prints any pending comments and whitespace occurring // Flush prints any pending comments and whitespace occurring textually
// textually before the position of the next token tok. Flush // before the position of the next token tok. The Flush result indicates
// returns true if a pending formfeed character was dropped // if a newline was written or if a formfeed was dropped from the whitespace
// from the whitespace buffer as a result of interspersing // buffer.
// comments.
// //
func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) { func (p *printer) flush(next token.Position, tok token.Token) (wroteNewline, 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, tok) wroteNewline, droppedFF = p.intersperseComments(next, tok)
} else { } else {
// otherwise, write any leftover whitespace // otherwise, write any leftover whitespace
p.writeWhitespace(len(p.wsbuf)) p.writeWhitespace(len(p.wsbuf))
......
...@@ -271,7 +271,6 @@ func _() { ...@@ -271,7 +271,6 @@ func _() {
// Known bug: The first use call may have more than one empty line before // Known bug: The first use call may have more than one empty line before
// (see go/printer/nodes.go, func linebreak). // (see go/printer/nodes.go, func linebreak).
use(x) use(x)
if x < x { if x < x {
...@@ -386,7 +385,6 @@ L: // A comment on the same line as the label, followed by a single empty line. ...@@ -386,7 +385,6 @@ L: // A comment on the same line as the label, followed by a single empty line.
// Known bug: There may be more than one empty line before MoreCode() // Known bug: There may be more than one empty line before MoreCode()
// (see go/printer/nodes.go, func linebreak). // (see go/printer/nodes.go, func linebreak).
MoreCode() MoreCode()
} }
......
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