1
0
mirror of https://github.com/golang/go synced 2024-11-25 13:07:57 -07:00

Gofmt preserves newlines in multiline selector expressions.

This is for making the fluent interface idiom usable with gofmt.

R=gri
CC=golang-dev
https://golang.org/cl/802043
This commit is contained in:
Risto Jaakko Saarelma 2010-03-30 11:46:21 -07:00 committed by Robert Griesemer
parent 76fc2f62d3
commit ac58f646ac
4 changed files with 298 additions and 6 deletions

View File

@ -10,6 +10,7 @@ package printer
import ( import (
"bytes" "bytes"
"container/vector"
"go/ast" "go/ast"
"go/token" "go/token"
) )
@ -89,6 +90,7 @@ const (
commaSep // elements are separated by commas commaSep // elements are separated by commas
commaTerm // list is optionally terminated by a comma commaTerm // list is optionally terminated by a comma
noIndent // no extra indentation in multi-line lists noIndent // no extra indentation in multi-line lists
periodSep // elements are separated by periods
) )
@ -176,7 +178,19 @@ func (p *printer) exprList(prev token.Position, list []ast.Expr, depth int, mode
// the first linebreak is always a formfeed since this section must not // the first linebreak is always a formfeed since this section must not
// depend on any previous formatting // depend on any previous formatting
prevBreak := -1 // index of last expression that was followed by a linebreak prevBreak := -1 // index of last expression that was followed by a linebreak
if prev.IsValid() && prev.Line < line && p.linebreak(line, 1, 2, ws, true) { linebreakMin := 1
if mode&periodSep != 0 {
// Make fragments like
//
// a.Bar(1,
// 2).Foo
//
// format correctly (a linebreak shouldn't be added before Foo) when
// doing period-separated expr lists by setting minimum linebreak to 0
// lines for them.
linebreakMin = 0
}
if prev.IsValid() && prev.Line < line && p.linebreak(line, linebreakMin, 2, ws, true) {
ws = ignore ws = ignore
*multiLine = true *multiLine = true
prevBreak = 0 prevBreak = 0
@ -230,19 +244,23 @@ func (p *printer) exprList(prev token.Position, list []ast.Expr, depth int, mode
if mode&commaSep != 0 { if mode&commaSep != 0 {
p.print(token.COMMA) p.print(token.COMMA)
} }
if mode&periodSep != 0 {
p.print(token.PERIOD)
}
if prevLine < line && prevLine > 0 && line > 0 { if prevLine < line && prevLine > 0 && line > 0 {
// lines are broken using newlines so comments remain aligned // lines are broken using newlines so comments remain aligned
// unless forceFF is set or there are multiple expressions on // unless forceFF is set or there are multiple expressions on
// the same line in which case formfeed is used // the same line in which case formfeed is used
// broken with a formfeed // broken with a formfeed
if p.linebreak(line, 1, 2, ws, useFF || prevBreak+1 < i) { if p.linebreak(line, linebreakMin, 2, ws, useFF || prevBreak+1 < i) {
ws = ignore ws = ignore
*multiLine = true *multiLine = true
prevBreak = i prevBreak = i
} }
} else { } else if mode&periodSep == 0 {
p.print(blank) p.print(blank)
} }
// period-separadet list elements don't need a blank
} }
if isPair && size > 0 && len(list) > 1 { if isPair && size > 0 && len(list) > 1 {
@ -652,6 +670,68 @@ func isBinary(expr ast.Expr) bool {
} }
// If the expression contains one or more selector expressions, splits it into
// two expressions at the rightmost period. Writes entire expr to suffix when
// selector isn't found. Rewrites AST nodes for calls, index expressions and
// type assertions, all of which may be found in selector chains, to make them
// parts of the chain.
func splitSelector(expr ast.Expr) (body, suffix ast.Expr) {
// Rewrite call and index expressions to be a part of the selector chain so
// that their multiline arguments get indented correctly.
switch x := expr.(type) {
case *ast.SelectorExpr:
body, suffix = x.X, x.Sel
return
case *ast.CallExpr:
body, suffix = splitSelector(x.Fun)
if body != nil {
suffix = &ast.CallExpr{suffix, x.Lparen, x.Args, x.Rparen}
return
}
case *ast.IndexExpr:
body, suffix = splitSelector(x.X)
if body != nil {
suffix = &ast.IndexExpr{suffix, x.Index}
return
}
case *ast.SliceExpr:
body, suffix = splitSelector(x.X)
if body != nil {
suffix = &ast.SliceExpr{suffix, x.Index, x.End}
return
}
case *ast.TypeAssertExpr:
body, suffix = splitSelector(x.X)
if body != nil {
suffix = &ast.TypeAssertExpr{suffix, x.Type}
return
}
}
suffix = expr
return
}
// Convert an expression into an expression list split at the periods of
// selector expressions.
func selectorExprList(expr ast.Expr) (result []ast.Expr) {
var list vector.Vector
for expr != nil {
var suffix ast.Expr
expr, suffix = splitSelector(expr)
list.Push(suffix)
}
result = make([]ast.Expr, len(list))
i := len(result)
for _, x := range list {
i--
result[i] = x.(ast.Expr)
}
return
}
// Sets multiLine to true if the expression spans multiple lines. // Sets multiLine to true if the expression spans multiple lines.
func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multiLine *bool) { func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multiLine *bool) {
p.print(expr.Pos()) p.print(expr.Pos())
@ -719,9 +799,8 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
p.print(x.Rparen, token.RPAREN) p.print(x.Rparen, token.RPAREN)
case *ast.SelectorExpr: case *ast.SelectorExpr:
p.expr1(x.X, token.HighestPrec, depth, 0, multiLine) parts := selectorExprList(expr)
p.print(token.PERIOD) p.exprList(noPos, parts, depth, periodSep, multiLine, noPos)
p.expr1(x.Sel, token.HighestPrec, depth, 0, multiLine)
case *ast.TypeAssertExpr: case *ast.TypeAssertExpr:
p.expr1(x.X, token.HighestPrec, depth, 0, multiLine) p.expr1(x.X, token.HighestPrec, depth, 0, multiLine)

View File

@ -403,3 +403,74 @@ func addState(s []state, inst instr, match []int) {
} }
} }
} }
func (self *T) foo(x int) *T { return self }
func _() { module.Func1().Func2() }
func _() {
_ = new(T).
foo(1).
foo(2).
foo(3)
_ = new(T).
foo(1).
foo(2). // inline comments
foo(3)
_ = new(T).foo(1).foo(2).foo(3)
// handle multiline argument list correctly
_ = new(T).
foo(
1).
foo(2)
_ = new(T).foo(
1).foo(2)
_ = Array[3+
4]
_ = Method(1, 2,
3)
_ = new(T).
foo().
bar().(*Type)
_ = new(T).
foo().
bar().(*Type).
baz()
_ = new(T).
foo().
bar()["idx"]
_ = new(T).
foo().
bar()["idx"].
baz()
_ = new(T).
foo().
bar()[1:2]
_ = new(T).
foo().
bar()[1:2].
baz()
_ = new(T).
Field.
Array[3+
4].
Table["foo"].
Blob.(*Type).
Slices[1:4].
Method(1, 2,
3).
Thingy
}

View File

@ -395,3 +395,74 @@ func addState(s []state, inst instr, match []int) {
} }
} }
} }
func (self *T) foo(x int) *T { return self }
func _() { module.Func1().Func2() }
func _() {
_ = new(T).
foo(1).
foo(2).
foo(3)
_ = new(T).
foo(1).
foo(2). // inline comments
foo(3)
_ = new(T).foo(1).foo(2).foo(3)
// handle multiline argument list correctly
_ = new(T).
foo(
1).
foo(2)
_ = new(T).foo(
1).foo(2)
_ = Array[3 +
4]
_ = Method(1, 2,
3)
_ = new(T).
foo().
bar().(*Type)
_ = new(T).
foo().
bar().(*Type).
baz()
_ = new(T).
foo().
bar()["idx"]
_ = new(T).
foo().
bar()["idx"].
baz()
_ = new(T).
foo().
bar()[1:2]
_ = new(T).
foo().
bar()[1:2].
baz()
_ = new(T).
Field.
Array[3+
4].
Table["foo"].
Blob.(*Type).
Slices[1:4].
Method(1, 2,
3).
Thingy
}

View File

@ -403,3 +403,74 @@ func addState(s []state, inst instr, match []int) {
} }
} }
} }
func (self *T) foo(x int) *T { return self }
func _() { module.Func1().Func2() }
func _() {
_ = new(T).
foo(1).
foo(2).
foo(3)
_ = new(T).
foo(1).
foo(2). // inline comments
foo(3)
_ = new(T).foo(1).foo(2).foo(3)
// handle multiline argument list correctly
_ = new(T).
foo(
1).
foo(2)
_ = new(T).foo(
1).foo(2)
_ = Array[3+
4]
_ = Method(1, 2,
3)
_ = new(T).
foo().
bar().(*Type)
_ = new(T).
foo().
bar().(*Type).
baz()
_ = new(T).
foo().
bar()["idx"]
_ = new(T).
foo().
bar()["idx"].
baz()
_ = new(T).
foo().
bar()[1:2]
_ = new(T).
foo().
bar()[1:2].
baz()
_ = new(T).
Field.
Array[3+
4].
Table["foo"].
Blob.(*Type).
Slices[1:4].
Method(1, 2,
3).
Thingy
}