diff options
Diffstat (limited to 'src/pkg/go/printer/nodes.go')
-rw-r--r-- | src/pkg/go/printer/nodes.go | 183 |
1 files changed, 136 insertions, 47 deletions
diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go index 86c327930..0fca8a161 100644 --- a/src/pkg/go/printer/nodes.go +++ b/src/pkg/go/printer/nodes.go @@ -33,7 +33,7 @@ import ( // line break was printed; returns false otherwise. // // TODO(gri): linebreak may add too many lines if the next statement at "line" -// is preceeded by comments because the computation of n assumes +// is preceded 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 @@ -215,12 +215,13 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp } if i > 0 { - if mode&commaSep != 0 { + switch { + case mode&commaSep != 0: p.print(token.COMMA) - } - if mode&periodSep != 0 { + case mode&periodSep != 0: p.print(token.PERIOD) } + needsBlank := mode&periodSep == 0 // period-separated list elements don't need a blank if prevLine < line && prevLine > 0 && line > 0 { // lines are broken using newlines so comments remain aligned // unless forceFF is set or there are multiple expressions on @@ -229,11 +230,12 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp ws = ignore *multiLine = true prevBreak = i + needsBlank = false // we got a line break instead } - } else if mode&periodSep == 0 { + } + if needsBlank { p.print(blank) } - // period-separated list elements don't need a blank } if isPair && size > 0 && len(list) > 1 { @@ -438,8 +440,8 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) if len(list) > 0 { p.print(formfeed) } - p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't loose the last line comment - p.setLineComment("// contains unexported fields") + p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't lose the last line comment + p.setLineComment("// contains filtered or unexported fields") } } else { // interface @@ -465,8 +467,8 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) if len(list) > 0 { p.print(formfeed) } - p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't loose the last line comment - p.setLineComment("// contains unexported methods") + p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't lose the last line comment + p.setLineComment("// contains filtered or unexported methods") } } @@ -1189,6 +1191,97 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) { // ---------------------------------------------------------------------------- // Declarations +// The keepTypeColumn function determines if the type column of a series of +// consecutive const or var declarations must be kept, or if initialization +// values (V) can be placed in the type column (T) instead. The i'th entry +// in the result slice is true if the type column in spec[i] must be kept. +// +// For example, the declaration: +// +// const ( +// foobar int = 42 // comment +// x = 7 // comment +// foo +// bar = 991 +// ) +// +// leads to the type/values matrix below. A run of value columns (V) can +// be moved into the type column if there is no type for any of the values +// in that column (we only move entire columns so that they align properly). +// +// matrix formatted result +// matrix +// T V -> T V -> true there is a T and so the type +// - V - V true column must be kept +// - - - - false +// - V V - false V is moved into T column +// +func keepTypeColumn(specs []ast.Spec) []bool { + m := make([]bool, len(specs)) + + populate := func(i, j int, keepType bool) { + if keepType { + for ; i < j; i++ { + m[i] = true + } + } + } + + i0 := -1 // if i0 >= 0 we are in a run and i0 is the start of the run + var keepType bool + for i, s := range specs { + t := s.(*ast.ValueSpec) + if t.Values != nil { + if i0 < 0 { + // start of a run of ValueSpecs with non-nil Values + i0 = i + keepType = false + } + } else { + if i0 >= 0 { + // end of a run + populate(i0, i, keepType) + i0 = -1 + } + } + if t.Type != nil { + keepType = true + } + } + if i0 >= 0 { + // end of a run + populate(i0, len(specs), keepType) + } + + return m +} + + +func (p *printer) valueSpec(s *ast.ValueSpec, keepType, doIndent bool, multiLine *bool) { + p.setComment(s.Doc) + p.identList(s.Names, doIndent, multiLine) // always present + extraTabs := 3 + if s.Type != nil || keepType { + p.print(vtab) + extraTabs-- + } + if s.Type != nil { + p.expr(s.Type, multiLine) + } + if s.Values != nil { + p.print(vtab, token.ASSIGN) + p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos) + extraTabs-- + } + if s.Comment != nil { + for ; extraTabs > 0; extraTabs-- { + p.print(vtab) + } + p.setComment(s.Comment) + } +} + + // The parameter n is the number of specs in the group. If doIndent is set, // multi-line identifier lists in the spec are indented when the first // linebreak is encountered. @@ -1206,38 +1299,20 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool, multiLine *bool) { p.setComment(s.Comment) case *ast.ValueSpec: + if n != 1 { + p.internalError("expected n = 1; got", n) + } p.setComment(s.Doc) p.identList(s.Names, doIndent, multiLine) // always present - if n == 1 { - if s.Type != nil { - p.print(blank) - p.expr(s.Type, multiLine) - } - if s.Values != nil { - p.print(blank, token.ASSIGN) - p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos) - } - p.setComment(s.Comment) - - } else { - extraTabs := 3 - if s.Type != nil { - p.print(vtab) - p.expr(s.Type, multiLine) - extraTabs-- - } - if s.Values != nil { - p.print(vtab, token.ASSIGN) - p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos) - extraTabs-- - } - if s.Comment != nil { - for ; extraTabs > 0; extraTabs-- { - p.print(vtab) - } - p.setComment(s.Comment) - } + if s.Type != nil { + p.print(blank) + p.expr(s.Type, multiLine) + } + if s.Values != nil { + p.print(blank, token.ASSIGN) + p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos) } + p.setComment(s.Comment) case *ast.TypeSpec: p.setComment(s.Doc) @@ -1264,15 +1339,29 @@ func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) { if d.Lparen.IsValid() { // group of parenthesized declarations p.print(d.Lparen, token.LPAREN) - if len(d.Specs) > 0 { + if n := len(d.Specs); n > 0 { p.print(indent, formfeed) - var ml bool - for i, s := range d.Specs { - if i > 0 { - p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml) + if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) { + // two or more grouped const/var declarations: + // determine if the type column must be kept + keepType := keepTypeColumn(d.Specs) + var ml bool + for i, s := range d.Specs { + if i > 0 { + p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml) + } + ml = false + p.valueSpec(s.(*ast.ValueSpec), keepType[i], false, &ml) + } + } else { + var ml bool + for i, s := range d.Specs { + if i > 0 { + p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml) + } + ml = false + p.spec(s, n, false, &ml) } - ml = false - p.spec(s, len(d.Specs), false, &ml) } p.print(unindent, formfeed) *multiLine = true @@ -1303,7 +1392,7 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) { size = maxSize + 1 // assume n doesn't fit p.nodeSizes[n] = size - // nodeSize computation must be indendent of particular + // nodeSize computation must be independent of particular // style so that we always get the same decision; print // in RawFormat cfg := Config{Mode: RawFormat} |