// ----------------------------------------------------------------------------
// Common AST nodes.
-// Print as many newlines as necessary (but at least min and and at most
-// max newlines) to get to the current line. ws is printed before the first
-// line break. If newSection is set, the first line break is printed as
-// formfeed. Returns true if any line break was printed; returns false otherwise.
+// Print as many newlines as necessary (but at least min newlines) to get to
+// the current line. ws is printed before the first line break. If newSection
+// is set, the first line break is printed as formfeed. Returns true if any
+// line break was printed; returns false otherwise.
//
-// TODO(gri): Reconsider signature (provide position instead of line)
+// TODO(gri): linebreak may add too many lines if the next statement at "line"
+// is preceeded by comments because the computation of n assumes
+// the current position before the comment and the target position
+// after the comment. Thus, after interspersing such comments, the
+// space taken up by them is not considered to reduce the number of
+// linebreaks. At the moment there is no easy way to know about
+// future (not yet interspersed) comments in this function.
//
-func (p *printer) linebreak(line, min, max int, ws whiteSpace, newSection bool) (printedBreak bool) {
- n := line - p.pos.Line
- switch {
- case n < min:
- n = min
- case n > max:
- n = max
- }
-
+func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) {
+ n := p.nlines(line-p.pos.Line, min)
if n > 0 {
p.print(ws)
if newSection {
p.print(formfeed)
n--
- printedBreak = true
}
- }
- for ; n > 0; n-- {
- p.print(newline)
+ for ; n > 0; n-- {
+ p.print(newline)
+ }
printedBreak = true
}
return
// lines for them.
linebreakMin = 0
}
- if prev.IsValid() && prev.Line < line && p.linebreak(line, linebreakMin, 2, ws, true) {
+ if prev.IsValid() && prev.Line < line && p.linebreak(line, linebreakMin, ws, true) {
ws = ignore
*multiLine = true
prevBreak = 0
// unless forceFF is set or there are multiple expressions on
// the same line in which case formfeed is used
// broken with a formfeed
- if p.linebreak(line, linebreakMin, 2, ws, useFF || prevBreak+1 < i) {
+ if p.linebreak(line, linebreakMin, ws, useFF || prevBreak+1 < i) {
ws = ignore
*multiLine = true
prevBreak = i
func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprContext) {
+ p.nesting++
+ defer func() {
+ p.nesting--
+ }()
+
lbrace := fields.Opening
list := fields.List
rbrace := fields.Closing
var ml bool
for i, f := range list {
if i > 0 {
- p.linebreak(f.Pos().Line, 1, 2, ignore, ml)
+ p.linebreak(f.Pos().Line, 1, ignore, ml)
}
ml = false
extraTabs := 0
var ml bool
for i, f := range list {
if i > 0 {
- p.linebreak(f.Pos().Line, 1, 2, ignore, ml)
+ p.linebreak(f.Pos().Line, 1, ignore, ml)
}
ml = false
p.setComment(f.Doc)
if xline != yline && xline > 0 && yline > 0 {
// at least one line break, but respect an extra empty line
// in the source
- if p.linebreak(yline, 1, 2, ws, true) {
+ if p.linebreak(yline, 1, ws, true) {
ws = ignore
*multiLine = true
printBlank = false // no blank after line break
// ----------------------------------------------------------------------------
// Statements
-const maxStmtNewlines = 2 // maximum number of newlines between statements
-
// Print the statement list indented, but without a newline after the last statement.
// Extra line breaks between statements in the source are respected but at most one
// empty line is printed between statements.
for i, s := range list {
// _indent == 0 only for lists of switch/select case clauses;
// in those cases each clause is a new section
- p.linebreak(s.Pos().Line, 1, maxStmtNewlines, ignore, i == 0 || _indent == 0 || multiLine)
+ p.linebreak(s.Pos().Line, 1, ignore, i == 0 || _indent == 0 || multiLine)
multiLine = false
p.stmt(s, nextIsRBrace && i == len(list)-1, &multiLine)
}
func (p *printer) block(s *ast.BlockStmt, indent int) {
p.print(s.Pos(), token.LBRACE)
p.stmtList(s.List, indent, true)
- p.linebreak(s.Rbrace.Line, 1, maxStmtNewlines, ignore, true)
+ p.linebreak(s.Rbrace.Line, 1, ignore, true)
p.print(s.Rbrace, token.RBRACE)
}
break
}
} else {
- p.print(newline)
+ p.linebreak(s.Stmt.Pos().Line, 1, ignore, true)
}
p.stmt(s.Stmt, nextIsRBrace, multiLine)
var ml bool
for i, s := range d.Specs {
if i > 0 {
- p.linebreak(s.Pos().Line, 1, 2, ignore, ml)
+ p.linebreak(s.Pos().Line, 1, ignore, ml)
}
ml = false
p.spec(s, len(d.Specs), false, &ml)
return
}
+ p.nesting++
+ defer func() {
+ p.nesting--
+ }()
+
if p.isOneLineFunc(b, headerSize) {
sep := vtab
if isLit {
// ----------------------------------------------------------------------------
// Files
-const maxDeclNewlines = 3 // maximum number of newlines between declarations
-
func declToken(decl ast.Decl) (tok token.Token) {
tok = token.ILLEGAL
switch d := decl.(type) {
if prev != tok {
min = 2
}
- p.linebreak(d.Pos().Line, min, maxDeclNewlines, ignore, false)
+ p.linebreak(d.Pos().Line, min, ignore, false)
p.decl(d, ignoreMultiLine)
}
}
)
-const (
- debug = false // enable for debugging
- maxNewlines = 3 // maximum vertical white space
-)
+const debug = false // enable for debugging
type whiteSpace int
esc = []byte{tabwriter.Escape}
htab = []byte{'\t'}
htabs = []byte("\t\t\t\t\t\t\t\t")
- newlines = []byte("\n\n\n\n\n\n\n\n") // more than maxNewlines
- formfeeds = []byte("\f\f\f\f\f\f\f\f") // more than maxNewlines
+ newlines = []byte("\n\n\n\n\n\n\n\n") // more than the max determined by nlines
+ formfeeds = []byte("\f\f\f\f\f\f\f\f") // more than the max determined by nlines
esc_quot = []byte(""") // shorter than """
esc_apos = []byte("'") // shorter than "'"
errors chan os.Error
// Current state
+ nesting int // nesting level (0: top-level (package scope), >0: functions/decls.)
written int // number of bytes written
indent int // current indentation
escape bool // true if in escape sequence
}
+// nlines returns the adjusted number of linebreaks given the desired number
+// of breaks n such that min <= result <= max where max depends on the current
+// nesting level.
+//
+func (p *printer) nlines(n, min int) int {
+ if n < min {
+ return min
+ }
+ max := 3 // max. number of newlines at the top level (p.nesting == 0)
+ if p.nesting > 0 {
+ max = 2 // max. number of newlines everywhere else
+ }
+ if n > max {
+ return max
+ }
+ return n
+}
+
+
// write0 writes raw (uninterpreted) data to p.output and handles errors.
// write0 does not indent after newlines, and does not HTML-escape or update p.pos.
//
func (p *printer) writeNewlines(n int, useFF bool) {
if n > 0 {
- if n > maxNewlines {
- n = maxNewlines
- }
+ n = p.nlines(n, 0)
if useFF {
p.write(formfeeds[0:n])
} else {
}
if pos.IsValid() && pos.Filename != p.last.Filename {
- // comment in a different file - separate with newlines
- p.writeNewlines(maxNewlines, true)
+ // comment in a different file - separate with newlines (writeNewlines will limit the number)
+ p.writeNewlines(10, true)
return
}
go func() {
switch n := node.(type) {
case ast.Expr:
+ p.nesting = 1
p.useNodeComments = true
p.expr(n, ignoreMultiLine)
case ast.Stmt:
+ p.nesting = 1
p.useNodeComments = true
// A labeled statement will un-indent to position the
// label. Set indent to 1 so we don't get indent "underflow".
}
p.stmt(n, false, ignoreMultiLine)
case ast.Decl:
+ p.nesting = 1
p.useNodeComments = true
p.decl(n, ignoreMultiLine)
case ast.Spec:
+ p.nesting = 1
p.useNodeComments = true
p.spec(n, 1, false, ignoreMultiLine)
case *ast.File:
+ p.nesting = 0
p.comments = n.Comments
p.useNodeComments = n.Comments == nil
p.file(n)