mirror of
https://github.com/golang/go
synced 2024-11-12 09:50:21 -07:00
- rewrite declaration printing to take full use of discardable tabwriter columns
- honor line breaks in multi-line expressions - do not add extra indentation to multi-line string lists - don't put blanks around simple function calls and conversions - do not modify `` strings - added extra test cases R=rsc DELTA=398 (246 added, 51 deleted, 101 changed) OCL=35453 CL=35465
This commit is contained in:
parent
093af4e512
commit
7ecfb021f3
@ -40,14 +40,14 @@ type whiteSpace int
|
||||
|
||||
const (
|
||||
blank = whiteSpace(' ');
|
||||
tab = whiteSpace('\t');
|
||||
vtab = whiteSpace('\v');
|
||||
newline = whiteSpace('\n');
|
||||
formfeed = whiteSpace('\f');
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
tabs = [...]byte{'\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t'};
|
||||
htabs = [...]byte{'\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t'};
|
||||
newlines = [...]byte{'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'}; // more than maxNewlines
|
||||
ampersand = strings.Bytes("&");
|
||||
lessthan = strings.Bytes("<");
|
||||
@ -111,23 +111,32 @@ func (p *printer) write0(data []byte) {
|
||||
}
|
||||
|
||||
|
||||
type writeMode uint;
|
||||
const (
|
||||
writeRaw writeMode = 1<<iota; // do not interpret newline/formfeed characters
|
||||
setLineTag; // wrap item with a line tag
|
||||
)
|
||||
|
||||
// write interprets data and writes it to p.output. It inserts indentation
|
||||
// after newline or formfeed and HTML-escapes characters if GenHTML is set.
|
||||
//
|
||||
func (p *printer) write(data []byte) {
|
||||
func (p *printer) write(data []byte, mode writeMode) {
|
||||
i0 := 0;
|
||||
for i, b := range data {
|
||||
switch b {
|
||||
case '\n', '\f':
|
||||
if mode & writeRaw == 0 {
|
||||
// write segment ending in b followed by indentation
|
||||
p.write0(data[i0 : i+1]);
|
||||
|
||||
// write indentation
|
||||
// use horizontal ("hard") tabs - indentation columns
|
||||
// must not be discarded by the tabwriter
|
||||
j := p.indent;
|
||||
for ; j > len(tabs); j -= len(tabs) {
|
||||
p.write0(&tabs);
|
||||
for ; j > len(htabs); j -= len(htabs) {
|
||||
p.write0(&htabs);
|
||||
}
|
||||
p.write0(tabs[0 : j]);
|
||||
p.write0(htabs[0 : j]);
|
||||
|
||||
// update p.pos
|
||||
p.pos.Offset += i+1 - i0 + p.indent;
|
||||
@ -136,6 +145,7 @@ func (p *printer) write(data []byte) {
|
||||
|
||||
// next segment start
|
||||
i0 = i+1;
|
||||
}
|
||||
|
||||
case '&', '<', '>':
|
||||
if p.mode & GenHTML != 0 {
|
||||
@ -176,12 +186,12 @@ func (p *printer) writeNewlines(n int) {
|
||||
if n > maxNewlines {
|
||||
n = maxNewlines;
|
||||
}
|
||||
p.write(newlines[0 : n]);
|
||||
p.write(newlines[0 : n], 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (p *printer) writeItem(pos token.Position, data []byte, setLineTag bool) {
|
||||
func (p *printer) writeItem(pos token.Position, data []byte, mode writeMode) {
|
||||
p.pos = pos;
|
||||
if debug {
|
||||
// do not update p.pos - use write0
|
||||
@ -189,7 +199,7 @@ func (p *printer) writeItem(pos token.Position, data []byte, setLineTag bool) {
|
||||
}
|
||||
if p.mode & GenHTML != 0 {
|
||||
// no html-escaping and no p.pos update for tags - use write0
|
||||
if setLineTag && pos.Line > p.lastTaggedLine {
|
||||
if mode & setLineTag != 0 && pos.Line > p.lastTaggedLine {
|
||||
// id's must be unique within a document: set
|
||||
// line tag only if line number has increased
|
||||
// (note: for now write complete start and end
|
||||
@ -203,14 +213,14 @@ func (p *printer) writeItem(pos token.Position, data []byte, setLineTag bool) {
|
||||
p.write0(strings.Bytes(p.tag.start));
|
||||
p.tag.start = ""; // tag consumed
|
||||
}
|
||||
p.write(data);
|
||||
p.write(data, mode);
|
||||
// write end tag, if any
|
||||
if p.tag.end != "" {
|
||||
p.write0(strings.Bytes(p.tag.end));
|
||||
p.tag.end = ""; // tag consumed
|
||||
}
|
||||
} else {
|
||||
p.write(data);
|
||||
p.write(data, mode);
|
||||
}
|
||||
p.last = p.pos;
|
||||
}
|
||||
@ -242,7 +252,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
|
||||
n := comment.Pos().Line - p.last.Line;
|
||||
if n == 0 {
|
||||
// comment on the same line as last item; separate with tab
|
||||
p.write(tabs[0 : 1]);
|
||||
p.write(htabs[0 : 1], 0);
|
||||
} else {
|
||||
// comment on a different line; separate with newlines
|
||||
p.writeNewlines(n);
|
||||
@ -250,7 +260,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
|
||||
}
|
||||
|
||||
// write comment
|
||||
p.writeItem(comment.Pos(), comment.Text, false);
|
||||
p.writeItem(comment.Pos(), comment.Text, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -318,7 +328,7 @@ func (p *printer) writeWhitespace() {
|
||||
b = b[0 : p.buflen];
|
||||
p.buflen = 0;
|
||||
|
||||
p.write(b);
|
||||
p.write(b, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -334,7 +344,7 @@ func (p *printer) writeWhitespace() {
|
||||
// printed, followed by the actual token.
|
||||
//
|
||||
func (p *printer) print(args ...) {
|
||||
setLineTag := false;
|
||||
var mode writeMode;
|
||||
v := reflect.NewValue(args).(*reflect.StructValue);
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
f := v.Field(i);
|
||||
@ -359,6 +369,10 @@ func (p *printer) print(args ...) {
|
||||
p.buflen++;
|
||||
case []byte:
|
||||
data = x;
|
||||
// do not modify multi-line `` strings!
|
||||
if len(x) > 0 && x[0] == '`' && x[len(x)-1] == '`' {
|
||||
mode |= writeRaw;
|
||||
}
|
||||
case string:
|
||||
data = strings.Bytes(x);
|
||||
case token.Token:
|
||||
@ -371,7 +385,7 @@ func (p *printer) print(args ...) {
|
||||
pos := token.Position(x);
|
||||
if pos.IsValid() {
|
||||
next = pos; // accurate position of next item
|
||||
setLineTag = true;
|
||||
mode |= setLineTag;
|
||||
}
|
||||
case htmlTag:
|
||||
p.tag = x; // tag surrounding next item
|
||||
@ -386,8 +400,8 @@ func (p *printer) print(args ...) {
|
||||
// intersperse extra newlines if present in the source
|
||||
p.writeNewlines(next.Line - p.pos.Line);
|
||||
|
||||
p.writeItem(next, data, setLineTag);
|
||||
setLineTag = false;
|
||||
p.writeItem(next, data, mode);
|
||||
mode = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -463,13 +477,15 @@ func (p *printer) leadComment(d *ast.CommentGroup) {
|
||||
}
|
||||
|
||||
|
||||
// Print a tab followed by a line comment.
|
||||
// Print n tabs followed by a line comment.
|
||||
// A newline must be printed afterwards since
|
||||
// the comment may be a //-style comment.
|
||||
func (p *printer) lineComment(d *ast.CommentGroup) {
|
||||
func (p *printer) lineComment(n int, d *ast.CommentGroup) {
|
||||
// Ignore the comment if we have comments interspersed (p.comment != nil).
|
||||
if p.comment == nil && d != nil {
|
||||
p.print(tab);
|
||||
for ; n > 0; n-- {
|
||||
p.print(vtab);
|
||||
}
|
||||
p.commentList(d.List);
|
||||
}
|
||||
}
|
||||
@ -491,7 +507,7 @@ func (p *printer) stringList(list []*ast.BasicLit) {
|
||||
for i, x := range list {
|
||||
xlist[i] = x;
|
||||
}
|
||||
p.exprList(xlist, 0);
|
||||
p.exprList(xlist, noIndent);
|
||||
}
|
||||
|
||||
|
||||
@ -500,6 +516,7 @@ const (
|
||||
blankStart exprListMode = 1 << iota; // print a blank before the list
|
||||
commaSep; // elements are separated by commas
|
||||
commaTerm; // elements are terminated by comma
|
||||
noIndent; // no extra indentation in multi-line lists
|
||||
)
|
||||
|
||||
|
||||
@ -531,10 +548,12 @@ func (p *printer) exprList(list []ast.Expr, mode exprListMode) {
|
||||
// list entries span multiple lines;
|
||||
// use source code positions to guide line breaks
|
||||
line := list[0].Pos().Line;
|
||||
indented := false;
|
||||
// don't add extra indentation if noIndent is set;
|
||||
// i.e., pretend that the first line is already indented
|
||||
indented := mode&noIndent != 0;
|
||||
// there may or may not be a linebreak before the first list
|
||||
// element; in any case indent once after the first linebreak
|
||||
if p.linebreak(line, 0, 2, true) {
|
||||
if p.linebreak(line, 0, 2, true) && !indented {
|
||||
p.print(+1);
|
||||
indented = true;
|
||||
}
|
||||
@ -560,13 +579,13 @@ func (p *printer) exprList(list []ast.Expr, mode exprListMode) {
|
||||
}
|
||||
if mode & commaTerm != 0 {
|
||||
p.print(token.COMMA);
|
||||
if indented {
|
||||
if indented && mode&noIndent == 0 {
|
||||
// should always be indented here since we have a multi-line
|
||||
// expression list - be conservative and check anyway
|
||||
p.print(-1);
|
||||
}
|
||||
p.print(formfeed); // terminating comma needs a line break to look good
|
||||
} else if indented {
|
||||
} else if indented && mode&noIndent == 0 {
|
||||
p.print(-1);
|
||||
}
|
||||
}
|
||||
@ -612,14 +631,6 @@ func (p *printer) signature(params, result []*ast.Field) (optSemi bool) {
|
||||
}
|
||||
|
||||
|
||||
func separator(useTab bool) whiteSpace {
|
||||
if useTab {
|
||||
return tab;
|
||||
}
|
||||
return blank;
|
||||
}
|
||||
|
||||
|
||||
func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace token.Position, isIncomplete, isStruct bool) {
|
||||
if len(list) == 0 && !isIncomplete {
|
||||
// no blank between keyword and {} in this case
|
||||
@ -631,7 +642,10 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
|
||||
// at least one entry or incomplete
|
||||
p.print(blank, lbrace, token.LBRACE, +1, formfeed);
|
||||
if isStruct {
|
||||
sep := separator(len(list) > 1);
|
||||
sep := blank;
|
||||
if len(list) > 1 {
|
||||
sep = vtab;
|
||||
}
|
||||
for i, f := range list {
|
||||
p.leadComment(f.Doc);
|
||||
if len(f.Names) > 0 {
|
||||
@ -644,7 +658,7 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
|
||||
p.expr(&ast.StringList{f.Tag});
|
||||
}
|
||||
p.print(token.SEMICOLON);
|
||||
p.lineComment(f.Comment);
|
||||
p.lineComment(1, f.Comment);
|
||||
if i+1 < len(list) || isIncomplete {
|
||||
p.print(newline);
|
||||
}
|
||||
@ -664,7 +678,7 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
|
||||
p.expr(f.Type);
|
||||
}
|
||||
p.print(token.SEMICOLON);
|
||||
p.lineComment(f.Comment);
|
||||
p.lineComment(1, f.Comment);
|
||||
if i+1 < len(list) || isIncomplete {
|
||||
p.print(newline);
|
||||
}
|
||||
@ -696,14 +710,13 @@ func needsBlanks(expr ast.Expr) bool {
|
||||
return needsBlanks(x.X)
|
||||
case *ast.CallExpr:
|
||||
// call expressions need blanks if they have more than one
|
||||
// argument or if the function or the argument need blanks
|
||||
return len(x.Args) > 1 || needsBlanks(x.Fun) || len(x.Args) == 1 && needsBlanks(x.Args[0]);
|
||||
// argument or if the function expression needs blanks
|
||||
return len(x.Args) > 1 || needsBlanks(x.Fun);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// TODO(gri): Write this recursively; get rid of vector use.
|
||||
func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1 int) {
|
||||
prec := x.Op.Precedence();
|
||||
if prec < prec1 {
|
||||
@ -717,39 +730,63 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1 int) {
|
||||
}
|
||||
|
||||
// Traverse left, collect all operations at same precedence
|
||||
// and determine if blanks should be printed.
|
||||
// and determine if blanks should be printed around operators.
|
||||
//
|
||||
// This algorithm assumes that the right-hand side of a binary
|
||||
// operation has a different (higher) precedence then the current
|
||||
// node, which is how the parser creates the AST.
|
||||
var list vector.Vector;
|
||||
line := x.Y.Pos().Line;
|
||||
printBlanks := prec <= token.EQL.Precedence() || needsBlanks(x.Y);
|
||||
for {
|
||||
list.Push(x);
|
||||
if t, ok := x.X.(*ast.BinaryExpr); ok && t.Op.Precedence() == prec {
|
||||
x = t;
|
||||
if needsBlanks(x.Y) {
|
||||
prev := line;
|
||||
line = x.Y.Pos().Line;
|
||||
if needsBlanks(x.Y) || prev != line {
|
||||
printBlanks = true;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if needsBlanks(x.X) {
|
||||
prev := line;
|
||||
line = x.X.Pos().Line;
|
||||
if needsBlanks(x.X) || prev != line {
|
||||
printBlanks = true;
|
||||
}
|
||||
|
||||
// Print collected operations left-to-right, with blanks if necessary.
|
||||
indented := false;
|
||||
p.expr1(x.X, prec);
|
||||
for list.Len() > 0 {
|
||||
x = list.Pop().(*ast.BinaryExpr);
|
||||
prev := line;
|
||||
line = x.Y.Pos().Line;
|
||||
if printBlanks {
|
||||
p.print(blank, x.OpPos, x.Op, blank);
|
||||
if prev != line {
|
||||
p.print(blank, x.OpPos, x.Op);
|
||||
// at least one linebreak, but respect an extra empty line
|
||||
// in the source
|
||||
if p.linebreak(line, 1, 2, false) && !indented {
|
||||
p.print(+1);
|
||||
indented = true;
|
||||
}
|
||||
} else {
|
||||
p.print(blank, x.OpPos, x.Op, blank);
|
||||
}
|
||||
} else {
|
||||
if prev != line {
|
||||
panic("internal error");
|
||||
}
|
||||
p.print(x.OpPos, x.Op);
|
||||
}
|
||||
p.expr1(x.Y, prec);
|
||||
}
|
||||
if indented {
|
||||
p.print(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -995,7 +1032,8 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) {
|
||||
p.print("BadStmt");
|
||||
|
||||
case *ast.DeclStmt:
|
||||
optSemi = p.decl(s.Decl);
|
||||
p.decl(s.Decl, inStmtList);
|
||||
optSemi = true; // decl prints terminating semicolon if necessary
|
||||
|
||||
case *ast.EmptyStmt:
|
||||
// nothing to do
|
||||
@ -1005,7 +1043,7 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) {
|
||||
// take place before the previous newline/formfeed is printed
|
||||
p.print(-1);
|
||||
p.expr(s.Label);
|
||||
p.print(token.COLON, tab, +1);
|
||||
p.print(token.COLON, vtab, +1);
|
||||
p.linebreak(s.Stmt.Pos().Line, 0, 1, true);
|
||||
optSemi = p.stmt(s.Stmt);
|
||||
|
||||
@ -1154,55 +1192,81 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) {
|
||||
// ----------------------------------------------------------------------------
|
||||
// Declarations
|
||||
|
||||
// Returns line comment, if any, and whether a separating semicolon is optional.
|
||||
// The parameters m and n control layout; m has different meanings for different
|
||||
// specs, n is the number of specs in the group.
|
||||
type declContext uint;
|
||||
const (
|
||||
atTop declContext = iota;
|
||||
inGroup;
|
||||
inStmtList;
|
||||
)
|
||||
|
||||
// The parameter n is the number of specs in the group; context specifies
|
||||
// the surroundings of the declaration. Separating semicolons are printed
|
||||
// depending on the context.
|
||||
//
|
||||
// ImportSpec:
|
||||
// m = number of imports with a rename
|
||||
//
|
||||
// ValueSpec:
|
||||
// m = number of values with a type
|
||||
//
|
||||
func (p *printer) spec(spec ast.Spec, m, n int) (comment *ast.CommentGroup, optSemi bool) {
|
||||
sep := separator(n > 1);
|
||||
func (p *printer) spec(spec ast.Spec, n int, context declContext) {
|
||||
var (
|
||||
optSemi bool; // true if a semicolon is optional
|
||||
comment *ast.CommentGroup; // a line comment, if any
|
||||
columns int; // number of (discardable) columns missing before comment, if any
|
||||
)
|
||||
|
||||
switch s := spec.(type) {
|
||||
case *ast.ImportSpec:
|
||||
p.leadComment(s.Doc);
|
||||
if m > 0 {
|
||||
// at least one entry with a rename
|
||||
if n == 1 {
|
||||
if s.Name != nil {
|
||||
p.expr(s.Name);
|
||||
p.print(blank);
|
||||
}
|
||||
} else {
|
||||
if s.Name != nil {
|
||||
p.expr(s.Name);
|
||||
}
|
||||
p.print(sep);
|
||||
p.print(vtab); // column discarded if empty
|
||||
}
|
||||
p.expr(&ast.StringList{s.Path});
|
||||
comment = s.Comment;
|
||||
|
||||
case *ast.ValueSpec:
|
||||
p.leadComment(s.Doc);
|
||||
p.identList(s.Names);
|
||||
if m > 0 {
|
||||
// at least one entry with a type
|
||||
p.identList(s.Names); // never empty
|
||||
if n == 1 {
|
||||
if s.Type != nil {
|
||||
p.print(sep);
|
||||
p.print(blank);
|
||||
optSemi = p.expr(s.Type);
|
||||
} else if s.Values != nil {
|
||||
p.print(sep);
|
||||
}
|
||||
}
|
||||
if s.Values != nil {
|
||||
p.print(sep, token.ASSIGN);
|
||||
p.print(blank, token.ASSIGN);
|
||||
p.exprList(s.Values, blankStart | commaSep);
|
||||
optSemi = false;
|
||||
}
|
||||
} else {
|
||||
columns = 2;
|
||||
if s.Type != nil || s.Values != nil {
|
||||
p.print(vtab);
|
||||
}
|
||||
if s.Type != nil {
|
||||
optSemi = p.expr(s.Type);
|
||||
columns = 1;
|
||||
}
|
||||
if s.Values != nil {
|
||||
p.print(vtab);
|
||||
p.print(token.ASSIGN);
|
||||
p.exprList(s.Values, blankStart | commaSep);
|
||||
optSemi = false;
|
||||
columns = 0;
|
||||
}
|
||||
}
|
||||
comment = s.Comment;
|
||||
|
||||
case *ast.TypeSpec:
|
||||
p.leadComment(s.Doc);
|
||||
p.expr(s.Name);
|
||||
p.print(sep);
|
||||
if n == 1 {
|
||||
p.print(blank);
|
||||
} else {
|
||||
p.print(vtab);
|
||||
}
|
||||
optSemi = p.expr(s.Type);
|
||||
comment = s.Comment;
|
||||
|
||||
@ -1210,32 +1274,15 @@ func (p *printer) spec(spec ast.Spec, m, n int) (comment *ast.CommentGroup, optS
|
||||
panic("unreachable");
|
||||
}
|
||||
|
||||
return comment, optSemi;
|
||||
if context == inGroup || context == inStmtList && !optSemi {
|
||||
p.print(token.SEMICOLON);
|
||||
}
|
||||
|
||||
p.lineComment(1+columns, comment);
|
||||
}
|
||||
|
||||
|
||||
func countImportRenames(list []ast.Spec) (n int) {
|
||||
for _, s := range list {
|
||||
if s.(*ast.ImportSpec).Name != nil {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
func countValueTypes(list []ast.Spec) (n int) {
|
||||
for _, s := range list {
|
||||
if s.(*ast.ValueSpec).Type != nil {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Returns true if a separating semicolon is optional.
|
||||
func (p *printer) decl(decl ast.Decl) (optSemi bool) {
|
||||
func (p *printer) decl(decl ast.Decl, context declContext) {
|
||||
switch d := decl.(type) {
|
||||
case *ast.BadDecl:
|
||||
p.print(d.Pos(), "BadDecl");
|
||||
@ -1244,15 +1291,6 @@ func (p *printer) decl(decl ast.Decl) (optSemi bool) {
|
||||
p.leadComment(d.Doc);
|
||||
p.print(lineTag(d.Pos()), d.Tok, blank);
|
||||
|
||||
// determine layout constant m
|
||||
var m int;
|
||||
switch d.Tok {
|
||||
case token.IMPORT:
|
||||
m = countImportRenames(d.Specs);
|
||||
case token.CONST, token.VAR:
|
||||
m = countValueTypes(d.Specs);
|
||||
}
|
||||
|
||||
if d.Lparen.IsValid() {
|
||||
// group of parenthesized declarations
|
||||
p.print(d.Lparen, token.LPAREN);
|
||||
@ -1262,25 +1300,15 @@ func (p *printer) decl(decl ast.Decl) (optSemi bool) {
|
||||
if i > 0 {
|
||||
p.print(newline);
|
||||
}
|
||||
comment, _ := p.spec(s, m, len(d.Specs));
|
||||
p.print(token.SEMICOLON);
|
||||
p.lineComment(comment);
|
||||
p.spec(s, len(d.Specs), inGroup);
|
||||
}
|
||||
p.print(-1, formfeed);
|
||||
}
|
||||
p.print(d.Rparen, token.RPAREN);
|
||||
optSemi = true;
|
||||
|
||||
} else {
|
||||
// single declaration
|
||||
var comment *ast.CommentGroup;
|
||||
comment, optSemi = p.spec(d.Specs[0], m, 1);
|
||||
// If this declaration is inside a statement list, the parser
|
||||
// does not associate a line comment with the declaration but
|
||||
// handles it as ordinary unassociated comment. Thus, in that
|
||||
// case, comment == nil and any trailing semicolon is not part
|
||||
// of a comment.
|
||||
p.lineComment(comment);
|
||||
p.spec(d.Specs[0], 1, context);
|
||||
}
|
||||
|
||||
case *ast.FuncDecl:
|
||||
@ -1306,8 +1334,6 @@ func (p *printer) decl(decl ast.Decl) (optSemi bool) {
|
||||
default:
|
||||
panic("unreachable");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1345,7 +1371,7 @@ func (p *printer) file(src *ast.File) {
|
||||
min = 2;
|
||||
}
|
||||
p.linebreak(d.Pos().Line, min, maxDeclNewlines, false);
|
||||
p.decl(d);
|
||||
p.decl(d, atTop);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1451,9 +1477,9 @@ func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int) (int, o
|
||||
if mode & UseSpaces != 0 {
|
||||
padchar = ' ';
|
||||
}
|
||||
var twmode uint;
|
||||
twmode := tabwriter.DiscardEmptyColumns;
|
||||
if mode & GenHTML != 0 {
|
||||
twmode = tabwriter.FilterHTML;
|
||||
twmode |= tabwriter.FilterHTML;
|
||||
}
|
||||
tw = tabwriter.NewWriter(output, tabwidth, 1, padchar, twmode);
|
||||
output = tw;
|
||||
@ -1469,7 +1495,7 @@ func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int) (int, o
|
||||
case ast.Stmt:
|
||||
p.stmt(n);
|
||||
case ast.Decl:
|
||||
p.decl(n);
|
||||
p.decl(n, atTop);
|
||||
case *ast.File:
|
||||
p.comment = n.Comments;
|
||||
p.file(n);
|
||||
|
26
src/pkg/go/printer/testdata/declarations.go
vendored
26
src/pkg/go/printer/testdata/declarations.go
vendored
@ -147,6 +147,16 @@ func _() {
|
||||
z = 2;
|
||||
zzz = 3;
|
||||
)
|
||||
// no entry has a value
|
||||
var (
|
||||
_ int;
|
||||
_ float;
|
||||
_ string;
|
||||
|
||||
_ int; // comment
|
||||
_ float; // comment
|
||||
_ string; // comment
|
||||
)
|
||||
// some entries have a type
|
||||
var (
|
||||
xxxxxx int;
|
||||
@ -157,6 +167,14 @@ func _() {
|
||||
yyyy = "bar";
|
||||
yyy string = "foo";
|
||||
)
|
||||
// mixed entries - all comments should be aligned
|
||||
var (
|
||||
a, b, c int;
|
||||
x = 10;
|
||||
d int; // comment
|
||||
y = 20; // comment
|
||||
f, ff, fff, ffff int = 0, 1, 2, 3; // comment
|
||||
)
|
||||
}
|
||||
|
||||
func _() {
|
||||
@ -228,6 +246,14 @@ type _ struct {
|
||||
}
|
||||
|
||||
|
||||
// difficult cases
|
||||
type _ struct {
|
||||
bool; // comment
|
||||
text []byte; // comment
|
||||
}
|
||||
|
||||
|
||||
|
||||
// formatting of interfaces
|
||||
type EI interface{}
|
||||
|
||||
|
24
src/pkg/go/printer/testdata/declarations.golden
vendored
24
src/pkg/go/printer/testdata/declarations.golden
vendored
@ -146,6 +146,15 @@ func _() {
|
||||
z = 2;
|
||||
zzz = 3;
|
||||
)
|
||||
// no entry has a value
|
||||
var (
|
||||
_ int;
|
||||
_ float;
|
||||
_ string;
|
||||
_ int; // comment
|
||||
_ float; // comment
|
||||
_ string; // comment
|
||||
)
|
||||
// some entries have a type
|
||||
var (
|
||||
xxxxxx int;
|
||||
@ -156,6 +165,14 @@ func _() {
|
||||
yyyy = "bar";
|
||||
yyy string = "foo";
|
||||
)
|
||||
// mixed entries - all comments should be aligned
|
||||
var (
|
||||
a, b, c int;
|
||||
x = 10;
|
||||
d int; // comment
|
||||
y = 20; // comment
|
||||
f, ff, fff, ffff int = 0, 1, 2, 3; // comment
|
||||
)
|
||||
}
|
||||
|
||||
func _() {
|
||||
@ -227,6 +244,13 @@ type _ struct {
|
||||
}
|
||||
|
||||
|
||||
// difficult cases
|
||||
type _ struct {
|
||||
bool; // comment
|
||||
text []byte; // comment
|
||||
}
|
||||
|
||||
|
||||
// formatting of interfaces
|
||||
type EI interface{}
|
||||
|
||||
|
60
src/pkg/go/printer/testdata/expressions.go
vendored
60
src/pkg/go/printer/testdata/expressions.go
vendored
@ -39,6 +39,8 @@ func _() {
|
||||
_ = "foo"+s;
|
||||
_ = s+"foo";
|
||||
_ = 'a'+'b';
|
||||
_ = len(s)/2;
|
||||
_ = len(t0.x)/a;
|
||||
|
||||
// spaces around expressions of different precedence or expressions containing spaces
|
||||
_ = a + -b;
|
||||
@ -80,6 +82,8 @@ func _() {
|
||||
_ = (a+b+c)*2;
|
||||
_ = a - b + c - d + (a+b+c) + d&e;
|
||||
_ = under_bar-1;
|
||||
_ = Open(dpath + "/file", O_WRONLY | O_CREAT, 0666);
|
||||
_ = int(c0&_Mask4)<<18 | int(c1&_Maskx)<<12 | int(c2&_Maskx)<<6 | int(c3&_Maskx);
|
||||
}
|
||||
|
||||
|
||||
@ -101,9 +105,63 @@ func _() {
|
||||
|
||||
|
||||
func _() {
|
||||
// TODO respect source line breaks in multi-line expressions
|
||||
// do not modify `` strings
|
||||
_ = ``;
|
||||
_ = `
|
||||
`; // TODO(gri): fix line breaks here
|
||||
_ = `foo
|
||||
bar`;
|
||||
}
|
||||
|
||||
|
||||
func _() {
|
||||
// not not add extra indentation to multi-line string lists
|
||||
_ = "foo" "bar";
|
||||
_ = "foo"
|
||||
"bar"
|
||||
"bah";
|
||||
_ = []string {
|
||||
"abc"
|
||||
"def",
|
||||
"foo"
|
||||
"bar"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func _() {
|
||||
// respect source lines in multi-line expressions
|
||||
_ = a+
|
||||
b+
|
||||
c;
|
||||
_ = a < b ||
|
||||
b < a;
|
||||
_ = "1234567890"
|
||||
"1234567890";
|
||||
// TODO(gri): add more test cases
|
||||
// TODO(gri): these comments should be indented
|
||||
}
|
||||
|
||||
|
||||
func same(t, u *Time) bool {
|
||||
// respect source lines in multi-line expressions
|
||||
return t.Year == u.Year
|
||||
&& t.Month == u.Month
|
||||
&& t.Day == u.Day
|
||||
&& t.Hour == u.Hour
|
||||
&& t.Minute == u.Minute
|
||||
&& t.Second == u.Second
|
||||
&& t.Weekday == u.Weekday
|
||||
&& t.ZoneOffset == u.ZoneOffset
|
||||
&& t.Zone == u.Zone
|
||||
}
|
||||
|
||||
|
||||
func (p *parser) charClass() {
|
||||
// respect source lines in multi-line expressions
|
||||
if cc.negate && len(cc.ranges) == 2 &&
|
||||
cc.ranges[0] == '\n' && cc.ranges[1] == '\n' {
|
||||
nl := new(_NotNl);
|
||||
p.re.add(nl);
|
||||
}
|
||||
}
|
||||
|
65
src/pkg/go/printer/testdata/expressions.golden
vendored
65
src/pkg/go/printer/testdata/expressions.golden
vendored
@ -39,6 +39,8 @@ func _() {
|
||||
_ = "foo"+s;
|
||||
_ = s+"foo";
|
||||
_ = 'a'+'b';
|
||||
_ = len(s)/2;
|
||||
_ = len(t0.x)/a;
|
||||
|
||||
// spaces around expressions of different precedence or expressions containing spaces
|
||||
_ = a + -b;
|
||||
@ -80,6 +82,8 @@ func _() {
|
||||
_ = (a+b+c)*2;
|
||||
_ = a - b + c - d + (a+b+c) + d&e;
|
||||
_ = under_bar - 1;
|
||||
_ = Open(dpath+"/file", O_WRONLY|O_CREAT, 0666);
|
||||
_ = int(c0&_Mask4)<<18 | int(c1&_Maskx)<<12 | int(c2&_Maskx)<<6 | int(c3&_Maskx);
|
||||
}
|
||||
|
||||
|
||||
@ -101,8 +105,65 @@ func _() {
|
||||
|
||||
|
||||
func _() {
|
||||
// TODO respect source line breaks in multi-line expressions
|
||||
_ = a < b || b < a;
|
||||
// do not modify `` strings
|
||||
_ = ``;
|
||||
_ = `
|
||||
`;
|
||||
// TODO(gri): fix line breaks here
|
||||
_ = `foo
|
||||
bar`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
func _() {
|
||||
// not not add extra indentation to multi-line string lists
|
||||
_ = "foo" "bar";
|
||||
_ = "foo"
|
||||
"bar"
|
||||
"bah";
|
||||
_ = []string{
|
||||
"abc"
|
||||
"def",
|
||||
"foo"
|
||||
"bar",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
func _() {
|
||||
// respect source lines in multi-line expressions
|
||||
_ = a +
|
||||
b +
|
||||
c;
|
||||
_ = a < b ||
|
||||
b < a;
|
||||
_ = "1234567890"
|
||||
"1234567890";
|
||||
// TODO(gri): add more test cases
|
||||
// TODO(gri): these comments should be indented
|
||||
}
|
||||
|
||||
|
||||
func same(t, u *Time) bool {
|
||||
// respect source lines in multi-line expressions
|
||||
return t.Year == u.Year &&
|
||||
t.Month == u.Month &&
|
||||
t.Day == u.Day &&
|
||||
t.Hour == u.Hour &&
|
||||
t.Minute == u.Minute &&
|
||||
t.Second == u.Second &&
|
||||
t.Weekday == u.Weekday &&
|
||||
t.ZoneOffset == u.ZoneOffset &&
|
||||
t.Zone == u.Zone;
|
||||
}
|
||||
|
||||
|
||||
func (p *parser) charClass() {
|
||||
// respect source lines in multi-line expressions
|
||||
if cc.negate && len(cc.ranges) == 2 &&
|
||||
cc.ranges[0] == '\n' && cc.ranges[1] == '\n' {
|
||||
nl := new(_NotNl);
|
||||
p.re.add(nl);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user