// ----------------------------------------------------------------------------
// 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.
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)
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
func _() {
- // the following decls need a semicolon at the end
type _ int
type _ *int
type _ []int
var _ chan int
var _ func() int
- // the following decls don't need a semicolon at the end
type _ struct{}
type _ *struct{}
type _ []struct{}
)
// some entries have a type
const (
- xxxxxx = 1
- x = 2
- xxx = 3
+ xxxxxx = 1
+ x = 2
+ xxx = 3
yyyyyyyy float = iota
- yyyy = "bar"
+ yyyy = "bar"
yyy
yy = 2
)
xxx string
yyyyyyyy int = 1234
y float = 3.14
- yyyy = "bar"
+ yyyy = "bar"
yyy string = "foo"
)
// mixed entries - all comments should be aligned
a, b, c int
x = 10
d int // comment
- y = 20 // comment
+ y = 20 // comment
f, ff, fff, ffff int = 0, 1, 2, 3 // comment
)
// respect original line breaks
)
}
+// alignment of "=" in consecutive lines (extended example from issue 1414)
+const (
+ umax uint = ^uint(0) // maximum value for a uint
+ bpu = 1 << (5 + umax>>63) // bits per uint
+ foo
+ bar = -1
+)
+
+// typical enum
+const (
+ a MyType = iota
+ abcd
+ b
+ c
+ def
+)
+
+// excerpt from godoc.go
+var (
+ goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory")
+ testDir = flag.String("testdir", "", "Go root subdirectory - for testing only (faster startups)")
+ pkgPath = flag.String("path", "", "additional package directories (colon-separated)")
+ filter = flag.String("filter", "", "filter file containing permitted package directory paths")
+ filterMin = flag.Int("filter_minutes", 0, "filter file update interval in minutes; disabled if <= 0")
+ filterDelay delayTime // actual filter update interval in minutes; usually filterDelay == filterMin, but filterDelay may back off exponentially
+)
+
// formatting of structs
type _ struct{}