in build/rewrite.go [669:806]
func fixMultilinePlus(f *File) {
// List manipulation helpers.
// As a special case, we treat f([...]) as a list, mainly
// for glob.
// isList reports whether x is a list.
var isList func(x Expr) bool
isList = func(x Expr) bool {
switch x := x.(type) {
case *ListExpr:
return true
case *CallExpr:
if len(x.List) == 1 {
return isList(x.List[0])
}
}
return false
}
// isMultiLine reports whether x is a multiline list.
var isMultiLine func(Expr) bool
isMultiLine = func(x Expr) bool {
switch x := x.(type) {
case *ListExpr:
return x.ForceMultiLine || len(x.List) > 1
case *CallExpr:
if x.ForceMultiLine || len(x.List) > 1 && !x.ForceCompact {
return true
}
if len(x.List) == 1 {
return isMultiLine(x.List[0])
}
}
return false
}
// forceMultiLine tries to force the list x to use a multiline form.
// It reports whether it was successful.
var forceMultiLine func(Expr) bool
forceMultiLine = func(x Expr) bool {
switch x := x.(type) {
case *ListExpr:
// Already multi line?
if x.ForceMultiLine {
return true
}
// If this is a list containing a list, force the
// inner list to be multiline instead.
if len(x.List) == 1 && forceMultiLine(x.List[0]) {
return true
}
x.ForceMultiLine = true
return true
case *CallExpr:
if len(x.List) == 1 {
return forceMultiLine(x.List[0])
}
}
return false
}
skip := map[Expr]bool{}
Walk(f, func(v Expr, stk []Expr) {
if skip[v] {
return
}
bin, ok := v.(*BinaryExpr)
if !ok || bin.Op != "+" {
return
}
// Found a +.
// w + x + y + z parses as ((w + x) + y) + z,
// so chase down the left side to make a list of
// all the things being added together, separated
// by the BinaryExprs that join them.
// Mark them as "skip" so that when Walk recurses
// into the subexpressions, we won't reprocess them.
var all []Expr
for {
all = append(all, bin.Y, bin)
bin1, ok := bin.X.(*BinaryExpr)
if !ok || bin1.Op != "+" {
break
}
bin = bin1
skip[bin] = true
}
all = append(all, bin.X)
// Because the outermost expression was the
// rightmost one, the list is backward. Reverse it.
for i, j := 0, len(all)-1; i < j; i, j = i+1, j-1 {
all[i], all[j] = all[j], all[i]
}
// The 'all' slice is alternating addends and BinaryExpr +'s:
// w, +, x, +, y, +, z
// If there are no lists involved, don't rewrite anything.
haveList := false
for i := 0; i < len(all); i += 2 {
if isList(all[i]) {
haveList = true
break
}
}
if !haveList {
return
}
// Okay, there are lists.
// Consider each + next to a line break.
for i := 1; i < len(all); i += 2 {
bin := all[i].(*BinaryExpr)
if !bin.LineBreak {
continue
}
// We're going to break the line after the +.
// If it is followed by a list, force that to be
// multiline instead.
if forceMultiLine(all[i+1]) {
bin.LineBreak = false
continue
}
// If the previous list was multiline already,
// don't bother with the line break after
// the +.
if isMultiLine(all[i-1]) {
bin.LineBreak = false
continue
}
}
})
}