From a32c65b4beb3b2598c5811a9527e4fd27e8bd19f Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 2 Dec 2008 16:49:44 -0800 Subject: - fine-tuning of white space - by default consider extra newlines in src for better formatting - additional flags for control (-newlines, -maxnewlines, -optsemicolons) - don't print ()'s around single anonymous result types Status: Comparing the output of pretty with the input for larger files shows mostly whitespace/formatting differences, which is what is desired. TODO: - Handling of overlong lines - some esoteric cases which look funny R=r OCL=20293 CL=20293 --- usr/gri/pretty/printer.go | 136 ++++++++++++++++++++++++++++---------------- usr/gri/pretty/selftest2.go | 30 +++++++++- 2 files changed, 115 insertions(+), 51 deletions(-) diff --git a/usr/gri/pretty/printer.go b/usr/gri/pretty/printer.go index ed51e7396..f1da8c3d3 100644 --- a/usr/gri/pretty/printer.go +++ b/usr/gri/pretty/printer.go @@ -16,16 +16,23 @@ import ( var ( debug = flag.Bool("debug", false, nil, "print debugging information"); + + // layout control tabwidth = flag.Int("tabwidth", 8, nil, "tab width"); usetabs = flag.Bool("usetabs", true, nil, "align with tabs instead of blanks"); - comments = flag.Bool("comments", true, nil, "enable printing of comments"); + newlines = flag.Bool("newlines", true, nil, "respect newlines in source"); + maxnewlines = flag.Int("maxnewlines", 3, nil, "max. number of consecutive newlines"); + + // formatting control + comments = flag.Bool("comments", true, nil, "print comments"); + optsemicolons = flag.Bool("optsemicolons", false, nil, "print optional semicolons"); ) // ---------------------------------------------------------------------------- // Printer -// Separators are printed in a delayed fashion, depending on the next token. +// Separators - printed in a delayed fashion, depending on context. const ( none = iota; blank; @@ -35,11 +42,12 @@ const ( ) -// Formatting actions control formatting parameters during printing. +// Semantic states - control formatting. const ( - no_action = iota; - open_scope; - close_scope; + normal = iota; + opening_scope; // controls indentation, scope level + closing_scope; // controls indentation, scope level + inside_list; // controls extra line breaks ) @@ -61,9 +69,14 @@ type Printer struct { separator int; // pending separator newlines int; // pending newlines - // formatting action - action int; // action executed on formatting parameters - lastaction int; // action for last string + // semantic state + state int; // current semantic state + laststate int; // state for last string +} + + +func (P *Printer) HasComment(pos int) bool { + return comments.BVal() && P.cpos < pos; } @@ -90,7 +103,7 @@ func (P *Printer) Init(writer *tabwriter.Writer, comments *array.Array) { P.cindex = -1; P.NextComment(); - // formatting parameters & action initialized correctly by default + // formatting parameters & semantic state initialized correctly by default } @@ -106,10 +119,10 @@ func (P *Printer) Printf(format string, s ...) { func (P *Printer) Newline(n int) { - const maxnl = 2; if n > 0 { - if n > maxnl { - n = maxnl; + m := int(maxnewlines.IVal()); + if n > m { + n = m; } for ; n > 0; n-- { P.Printf("\n"); @@ -122,14 +135,16 @@ func (P *Printer) Newline(n int) { func (P *Printer) String(pos int, s string) { - // correct pos if necessary + // use estimate for pos if we don't have one if pos == 0 { - pos = P.lastpos; // estimate + pos = P.lastpos; } // -------------------------------- // print pending separator, if any // - keep track of white space printed for better comment formatting + // TODO print white space separators after potential comments and newlines + // (currently, we may get trailing white space before a newline) trailing_char := 0; switch P.separator { case none: // nothing to do @@ -160,7 +175,7 @@ func (P *Printer) String(pos int, s string) { // -------------------------------- // interleave comments, if any nlcount := 0; - for comments.BVal() && P.cpos < pos { + for ; P.HasComment(pos); P.NextComment() { // we have a comment/newline that comes before the string comment := P.comments.At(P.cindex).(*AST.Comment); ctext := comment.text; @@ -176,7 +191,12 @@ func (P *Printer) String(pos int, s string) { // only white space before comment on this line // or file starts with comment // - indent + if !newlines.BVal() && P.cpos != 0 { + nlcount = 1; + } P.Newline(nlcount); + nlcount = 0; + } else { // black space before comment on this line if ctext[1] == '/' { @@ -184,7 +204,7 @@ func (P *Printer) String(pos int, s string) { // - put in next cell unless a scope was just opened // in which case we print 2 blanks (otherwise the // entire scope gets indented like the next cell) - if P.lastaction == open_scope { + if P.laststate == opening_scope { switch trailing_char { case ' ': P.Printf(" "); // one space already printed case '\t': // do nothing @@ -205,6 +225,7 @@ func (P *Printer) String(pos int, s string) { } } + // print comment if debug.BVal() { P.Printf("[%d]", P.cpos); } @@ -216,33 +237,36 @@ func (P *Printer) String(pos int, s string) { P.newlines = 1; } } - - nlcount = 0; } - - P.NextComment(); } + // At this point we may have nlcount > 0: In this case we found newlines + // that were not followed by a comment. They are recognized (or not) when + // printing newlines below. // -------------------------------- - // handle extra newlines - if nlcount > 0 { - P.newlines += nlcount - 1; - } - - // -------------------------------- - // interpret control + // interpret state // (any pending separator or comment must be printed in previous state) - switch P.action { - case none: - case open_scope: - case close_scope: + switch P.state { + case normal: + case opening_scope: + case closing_scope: P.indentation--; + case inside_list: default: panic("UNREACHABLE"); } // -------------------------------- - // adjust formatting depending on state + // print pending newlines + if newlines.BVal() && (P.newlines > 0 || P.state == inside_list) && nlcount > P.newlines { + // Respect additional newlines in the source, but only if we + // enabled this feature (newlines.BVal()) and we are expecting + // newlines (P.newlines > 0 || P.state == inside_list). + // Otherwise - because we don't have all token positions - we + // get funny formatting. + P.newlines = nlcount; + } + nlcount = 0; P.Newline(P.newlines); P.newlines = 0; @@ -254,20 +278,20 @@ func (P *Printer) String(pos int, s string) { P.Printf("%s", s); // -------------------------------- - // interpret control - switch P.action { - case none: - case open_scope: + // interpret state + switch P.state { + case normal: + case opening_scope: P.level++; P.indentation++; - //P.newlines = 1; - case close_scope: + case closing_scope: P.level--; + case inside_list: default: panic("UNREACHABLE"); } - P.lastaction = P.action; - P.action = none; + P.laststate = P.state; + P.state = none; // -------------------------------- // done @@ -321,7 +345,7 @@ func (P *Printer) Parameters(pos int, list *array.Array) { func (P *Printer) Fields(list *array.Array, end int) { - P.action = open_scope; + P.state = opening_scope; P.String(0, "{"); if list != nil { @@ -345,7 +369,7 @@ func (P *Printer) Fields(list *array.Array, end int) { P.newlines = 1; } - P.action = close_scope; + P.state = closing_scope; P.String(end, "}"); } @@ -394,7 +418,13 @@ func (P *Printer) Type(t *AST.Type) { P.Parameters(t.pos, t.list); if t.elt != nil { P.separator = blank; - P.Parameters(0, t.elt.list); + list := t.elt.list; + if list.Len() > 1 { + P.Parameters(0, list); + } else { + // single, anonymous result type + P.Expr(list.At(0).(*AST.Expr)); + } } case Scanner.ELLIPSIS: @@ -438,6 +468,7 @@ func (P *Printer) Expr1(x *AST.Expr, prec1 int) { P.Expr(x.x); P.String(x.pos, ","); P.separator = blank; + P.state = inside_list; P.Expr(x.y); case Scanner.PERIOD: @@ -522,7 +553,7 @@ func (P *Printer) StatementList(list *array.Array) { func (P *Printer) Block(pos int, list *array.Array, end int, indent bool) { - P.action = open_scope; + P.state = opening_scope; P.String(pos, "{"); if !indent { P.indentation--; @@ -531,8 +562,10 @@ func (P *Printer) Block(pos int, list *array.Array, end int, indent bool) { if !indent { P.indentation++; } - P.separator = none; - P.action = close_scope; + if !optsemicolons.BVal() { + P.separator = none; + } + P.state = closing_scope; P.String(end, "}"); } @@ -651,6 +684,8 @@ func (P *Printer) Stat(s *AST.Stat) { // ---------------------------------------------------------------------------- // Declarations +// TODO This code is unreadable! Clean up AST and rewrite this. + func (P *Printer) Declaration(d *AST.Decl, parenthesized bool) { if !parenthesized { if d.exported { @@ -662,7 +697,7 @@ func (P *Printer) Declaration(d *AST.Decl, parenthesized bool) { } if d.tok != Scanner.FUNC && d.list != nil { - P.action = open_scope; + P.state = opening_scope; P.String(0, "("); if d.list.Len() > 0 { P.newlines = 1; @@ -672,7 +707,7 @@ func (P *Printer) Declaration(d *AST.Decl, parenthesized bool) { P.newlines = 1; } } - P.action = close_scope; + P.state = closing_scope; P.String(d.end, ")"); } else { @@ -691,11 +726,12 @@ func (P *Printer) Declaration(d *AST.Decl, parenthesized bool) { P.separator = blank; } P.Type(d.typ); + P.separator = tab; } if d.val != nil { - P.String(0, "\t"); if d.tok != Scanner.IMPORT { + P.separator = tab; P.String(0, "="); P.separator = blank; } diff --git a/usr/gri/pretty/selftest2.go b/usr/gri/pretty/selftest2.go index 53b4fbd89..306239932 100644 --- a/usr/gri/pretty/selftest2.go +++ b/usr/gri/pretty/selftest2.go @@ -4,7 +4,25 @@ package main -import Fmt "fmt" +import ( + "array"; // not needed + "utf8"; // not needed + Fmt "fmt" +) + + +const /* enum */ ( + EnumTag0 = iota; + EnumTag1; + EnumTag2; + EnumTag3; + EnumTag4; + EnumTag5; + EnumTag6; + EnumTag7; + EnumTag8; + EnumTag9; +) type T struct { @@ -29,6 +47,16 @@ func f0(a, b int) int { } +func f1(tag int) { + switch tag { + case + EnumTag0, EnumTag1, EnumTag2, EnumTag3, EnumTag4, + EnumTag5, EnumTag6, EnumTag7, EnumTag8, EnumTag9: break; + default: + } +} + + func main() { // the prologue for i := 0; i <= 10 /* limit */; i++ { -- cgit v1.2.3