mirror of https://github.com/golang/go.git
- improved comment intersperse heuristic:
comments should now be indented properly in corner cases (at the end of statement lists, for instance) - changed import decl. formatting as suggested by Russ (no "global" indentation of imports if there are renames present) - better field list handling - better documentation R=rsc DELTA=534 (324 added, 35 deleted, 175 changed) OCL=35557 CL=35630
This commit is contained in:
parent
e2854875c5
commit
df7efaf9e8
|
|
@ -39,14 +39,18 @@ const (
|
|||
type whiteSpace int
|
||||
|
||||
const (
|
||||
ignore = whiteSpace(0);
|
||||
blank = whiteSpace(' ');
|
||||
vtab = whiteSpace('\v');
|
||||
newline = whiteSpace('\n');
|
||||
formfeed = whiteSpace('\f');
|
||||
indent = whiteSpace('>');
|
||||
unindent = whiteSpace('<');
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
htab = []byte{'\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("&");
|
||||
|
|
@ -69,25 +73,34 @@ type htmlTag struct {
|
|||
|
||||
|
||||
type printer struct {
|
||||
// configuration (does not change after initialization)
|
||||
// Configuration (does not change after initialization)
|
||||
output io.Writer;
|
||||
mode uint;
|
||||
errors chan os.Error;
|
||||
|
||||
// current state (changes during printing)
|
||||
// Current state
|
||||
written int; // number of bytes written
|
||||
indent int; // current indentation
|
||||
last token.Position; // (possibly estimated) position immediately after the last item; in AST space
|
||||
pos token.Position; // (possibly estimated) position; in AST space
|
||||
|
||||
// Buffered whitespace
|
||||
buffer []whiteSpace;
|
||||
|
||||
// The (possibly estimated) position in the generated output;
|
||||
// in AST space (i.e., pos is set whenever a token position is
|
||||
// known accurately, and updated dependending on what has been
|
||||
// written)
|
||||
pos token.Position;
|
||||
|
||||
// The value of pos immediately after the last item has been
|
||||
// written using writeItem.
|
||||
last token.Position;
|
||||
|
||||
// HTML support
|
||||
tag htmlTag; // tag to be used around next item
|
||||
lastTaggedLine int; // last line for which a line tag was written
|
||||
|
||||
// buffered whitespace
|
||||
buffer [8]whiteSpace; // whitespace sequences are short (1 or 2); 8 entries is plenty
|
||||
buflen int;
|
||||
|
||||
// comments
|
||||
comment *ast.CommentGroup; // list of comments; or nil
|
||||
// The list of comments; or nil.
|
||||
comment *ast.CommentGroup;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -95,6 +108,7 @@ func (p *printer) init(output io.Writer, mode uint) {
|
|||
p.output = output;
|
||||
p.mode = mode;
|
||||
p.errors = make(chan os.Error);
|
||||
p.buffer = make([]whiteSpace, 0, 16); // whitespace sequences are short
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -118,35 +132,40 @@ const (
|
|||
)
|
||||
|
||||
// write interprets data and writes it to p.output. It inserts indentation
|
||||
// after newline or formfeed and HTML-escapes characters if GenHTML is set.
|
||||
// after newline or formfeed if not in writeRaw mode and HTML-escapes characters
|
||||
// if GenHTML is set. It updates p.pos as a side-effect.
|
||||
//
|
||||
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 segment ending in b
|
||||
p.write0(data[i0 : i+1]);
|
||||
|
||||
// update p.pos
|
||||
p.pos.Offset += i+1 - i0;
|
||||
p.pos.Line++;
|
||||
p.pos.Column = 1;
|
||||
|
||||
if mode & writeRaw == 0 {
|
||||
// write indentation
|
||||
// use horizontal ("hard") tabs - indentation columns
|
||||
// use "hard" htabs - indentation columns
|
||||
// must not be discarded by the tabwriter
|
||||
j := p.indent;
|
||||
for ; j > len(htabs); j -= len(htabs) {
|
||||
p.write0(&htabs);
|
||||
}
|
||||
p.write0(htabs[0 : j]);
|
||||
p.write0(htabs[0:j]);
|
||||
|
||||
// update p.pos
|
||||
p.pos.Offset += i+1 - i0 + p.indent;
|
||||
p.pos.Line++;
|
||||
p.pos.Column = p.indent + 1;
|
||||
|
||||
// next segment start
|
||||
i0 = i+1;
|
||||
p.pos.Offset += p.indent;
|
||||
p.pos.Column += p.indent;
|
||||
}
|
||||
|
||||
// next segment start
|
||||
i0 = i+1;
|
||||
|
||||
case '&', '<', '>':
|
||||
if p.mode & GenHTML != 0 {
|
||||
// write segment ending in b
|
||||
|
|
@ -162,8 +181,9 @@ func (p *printer) write(data []byte, mode writeMode) {
|
|||
p.write0(esc);
|
||||
|
||||
// update p.pos
|
||||
p.pos.Offset += i+1 - i0;
|
||||
p.pos.Column += i+1 - i0;
|
||||
d := i+1 - i0;
|
||||
p.pos.Offset += d;
|
||||
p.pos.Column += d;
|
||||
|
||||
// next segment start
|
||||
i0 = i+1;
|
||||
|
|
@ -175,9 +195,9 @@ func (p *printer) write(data []byte, mode writeMode) {
|
|||
p.write0(data[i0 : len(data)]);
|
||||
|
||||
// update p.pos
|
||||
n := len(data) - i0;
|
||||
p.pos.Offset += n;
|
||||
p.pos.Column += n;
|
||||
d := len(data) - i0;
|
||||
p.pos.Offset += d;
|
||||
p.pos.Column += d;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -191,6 +211,13 @@ func (p *printer) writeNewlines(n int) {
|
|||
}
|
||||
|
||||
|
||||
// writeItem writes data at position pos. data is the text corresponding to
|
||||
// a single lexical token, but may also be comment text. pos is the actual
|
||||
// (or at least very accurately estimated) position of the data in the original
|
||||
// source text. The data may be tagged, depending on p.mode and the mode
|
||||
// parameter. writeItem updates p.last to the position immediately following
|
||||
// the data.
|
||||
//
|
||||
func (p *printer) writeItem(pos token.Position, data []byte, mode writeMode) {
|
||||
p.pos = pos;
|
||||
if debug {
|
||||
|
|
@ -226,10 +253,81 @@ func (p *printer) writeItem(pos token.Position, data []byte, mode writeMode) {
|
|||
}
|
||||
|
||||
|
||||
// TODO(gri): decide if this is needed - keep around for now
|
||||
/*
|
||||
// Reduce contiguous sequences of '\t' in a []byte to a single '\t'.
|
||||
func untabify(src []byte) []byte {
|
||||
// writeCommentPrefix writes the whitespace before a comment.
|
||||
// If there is any pending whitespace, it consumes as much of
|
||||
// it as is likely to help the comment position properly.
|
||||
// line is the comment line, isFirst indicates if this is the
|
||||
// first comment in a group of comments.
|
||||
//
|
||||
func (p *printer) writeCommentPrefix(line int, isFirst bool) {
|
||||
if !p.last.IsValid() {
|
||||
// there was no preceeding item and the comment is the
|
||||
// first item to be printed - don't write any whitespace
|
||||
return;
|
||||
}
|
||||
|
||||
n := line - p.last.Line;
|
||||
if n == 0 {
|
||||
// comment on the same line as last item:
|
||||
// separate with at least one tab
|
||||
hasTab := false;
|
||||
if isFirst {
|
||||
j := 0;
|
||||
for i, ch := range p.buffer {
|
||||
switch ch {
|
||||
case blank:
|
||||
// ignore any blanks before a comment
|
||||
p.buffer[i] = ignore;
|
||||
continue;
|
||||
case vtab:
|
||||
// respect existing tabs - important
|
||||
// for proper formatting of commented structs
|
||||
hasTab = true;
|
||||
continue;
|
||||
case indent:
|
||||
// apply pending indentation
|
||||
continue;
|
||||
}
|
||||
j = i;
|
||||
break;
|
||||
}
|
||||
p.writeWhitespace(j);
|
||||
}
|
||||
// make sure there is at least one tab
|
||||
if !hasTab {
|
||||
p.write(htab, 0);
|
||||
}
|
||||
|
||||
} else {
|
||||
// comment on a different line:
|
||||
// separate with at least one line break
|
||||
if isFirst {
|
||||
j := 0;
|
||||
for i, ch := range p.buffer {
|
||||
switch ch {
|
||||
case blank, vtab:
|
||||
// ignore any horizontal whitespace before line breaks
|
||||
p.buffer[i] = ignore;
|
||||
continue;
|
||||
case indent:
|
||||
// apply pending indentation
|
||||
continue;
|
||||
case newline, formfeed:
|
||||
// TODO(gri): may want to keep formfeed info in some cases
|
||||
p.buffer[i] = ignore;
|
||||
}
|
||||
j = i;
|
||||
break;
|
||||
}
|
||||
p.writeWhitespace(j);
|
||||
}
|
||||
p.writeNewlines(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Collapse contiguous sequences of '\t' in a []byte to a single '\t'.
|
||||
func collapseTabs(src []byte) []byte {
|
||||
dst := make([]byte, len(src));
|
||||
j := 0;
|
||||
for i, c := range src {
|
||||
|
|
@ -240,98 +338,128 @@ func untabify(src []byte) []byte {
|
|||
}
|
||||
return dst[0 : j];
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
func (p *printer) writeComment(comment *ast.Comment) {
|
||||
// separation from last item
|
||||
if p.last.IsValid() {
|
||||
// there was a preceding item (otherwise, the comment is the
|
||||
// first item to be printed - in that case do not apply extra
|
||||
// spacing)
|
||||
n := comment.Pos().Line - p.last.Line;
|
||||
if n == 0 {
|
||||
// comment on the same line as last item; separate with tab
|
||||
p.write(htabs[0 : 1], 0);
|
||||
} else {
|
||||
// comment on a different line; separate with newlines
|
||||
p.writeNewlines(n);
|
||||
}
|
||||
// If there are tabs in the comment text, they were probably introduced
|
||||
// to align the comment contents. If the same tab settings were used as
|
||||
// by the printer, reducing tab sequences to single tabs will yield the
|
||||
// original comment again after reformatting via the tabwriter.
|
||||
text := comment.Text;
|
||||
if p.mode & RawFormat == 0 {
|
||||
// tabwriter is used
|
||||
text = collapseTabs(comment.Text);
|
||||
}
|
||||
|
||||
// write comment
|
||||
p.writeItem(comment.Pos(), comment.Text, 0);
|
||||
p.writeItem(comment.Pos(), text, 0);
|
||||
}
|
||||
|
||||
|
||||
func (p *printer) intersperseComments(next token.Position) {
|
||||
firstLine := 0;
|
||||
needsNewline := false;
|
||||
for ; p.comment != nil && p.comment.List[0].Pos().Offset < next.Offset; p.comment = p.comment.Next {
|
||||
for _, c := range p.comment.List {
|
||||
if firstLine == 0 {
|
||||
firstLine = c.Pos().Line;
|
||||
|
||||
// writeCommentSuffix writes a line break after a comment if indicated
|
||||
// and processes any leftover indentation information. If a line break
|
||||
// is needed, the kind of break (newline vs formfeed) depends on the
|
||||
// pending whitespace.
|
||||
//
|
||||
func (p *printer) writeCommentSuffix(needsLinebreak bool) {
|
||||
for i, ch := range p.buffer {
|
||||
switch ch {
|
||||
case blank, vtab:
|
||||
// ignore trailing whitespace
|
||||
p.buffer[i] = ignore;
|
||||
case indent, unindent:
|
||||
// don't loose indentation information
|
||||
case newline, formfeed:
|
||||
// if we need a line break, keep exactly one
|
||||
if needsLinebreak {
|
||||
needsLinebreak = false;
|
||||
} else {
|
||||
p.buffer[i] = ignore;
|
||||
}
|
||||
}
|
||||
}
|
||||
p.writeWhitespace(len(p.buffer));
|
||||
|
||||
// make sure we have a line break
|
||||
if needsLinebreak {
|
||||
p.write([]byte{'\n'}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// intersperseComments consumes all comments that appear before the next token
|
||||
// and prints it together with the buffered whitespace (i.e., the whitespace
|
||||
// that needs to be written before the next token). A heuristic is used to mix
|
||||
// the comments and whitespace.
|
||||
//
|
||||
func (p *printer) intersperseComments(next token.Position) {
|
||||
isFirst := true;
|
||||
needsLinebreak := false;
|
||||
for ; p.commentBefore(next); p.comment = p.comment.Next {
|
||||
for _, c := range p.comment.List {
|
||||
p.writeCommentPrefix(c.Pos().Line, isFirst);
|
||||
isFirst = false;
|
||||
p.writeComment(c);
|
||||
needsNewline = c.Text[1] == '/';
|
||||
needsLinebreak = c.Text[1] == '/';
|
||||
}
|
||||
}
|
||||
|
||||
// Eliminate non-newline whitespace from whitespace buffer.
|
||||
j := 0;
|
||||
for i := 0; i < p.buflen; i++ {
|
||||
ch := p.buffer[i];
|
||||
if ch == '\n' || ch == '\f' {
|
||||
p.buffer[j] = ch;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
p.buflen = j;
|
||||
|
||||
// Eliminate extra newlines from whitespace buffer if they
|
||||
// are not present in the original source. This makes sure
|
||||
// that comments that need to be adjacent to a declaration
|
||||
// remain adjacent.
|
||||
if p.last.IsValid() {
|
||||
n := next.Line - p.last.Line;
|
||||
if n < p.buflen {
|
||||
p.buflen = n;
|
||||
}
|
||||
}
|
||||
|
||||
// If the whitespace buffer is not empty, it contains only
|
||||
// newline or formfeed chars. Force a formfeed char if the
|
||||
// comments span more than one line - in this case the
|
||||
// structure of the next line is likely to change. Otherwise
|
||||
// use the existing char, if any.
|
||||
if needsNewline {
|
||||
ch := p.buffer[0]; // existing char takes precedence
|
||||
if p.buflen == 0 {
|
||||
p.buflen = 1;
|
||||
ch = newline; // original ch was a lie
|
||||
}
|
||||
if p.last.Line > firstLine {
|
||||
ch = formfeed; // comments span at least 2 lines
|
||||
}
|
||||
p.buffer[0] = ch;
|
||||
}
|
||||
p.writeCommentSuffix(needsLinebreak);
|
||||
}
|
||||
|
||||
|
||||
func (p *printer) writeWhitespace() {
|
||||
var a [len(p.buffer)]byte;
|
||||
for i := 0; i < p.buflen; i++ {
|
||||
a[i] = byte(p.buffer[i]);
|
||||
// whiteWhitespace writes the first n whitespace entries.
|
||||
func (p *printer) writeWhitespace(n int) {
|
||||
// write entries
|
||||
var data [1]byte;
|
||||
for i := 0; i < n; i++ {
|
||||
switch ch := p.buffer[i]; ch {
|
||||
case ignore:
|
||||
// ignore!
|
||||
case indent:
|
||||
p.indent++;
|
||||
case unindent:
|
||||
p.indent--;
|
||||
if p.indent < 0 {
|
||||
// handle gracefully unless in debug mode
|
||||
if debug {
|
||||
panicln("negative indentation:", p.indent);
|
||||
}
|
||||
p.indent = 0;
|
||||
}
|
||||
case newline, formfeed:
|
||||
// A line break immediately followed by a "correcting"
|
||||
// unindent is swapped with the unindent - this permits
|
||||
// proper label positioning. If a comment is between
|
||||
// the line break and the label, the unindent is not
|
||||
// part of the comment whitespace prefix and the comment
|
||||
// will be positioned correctly indented.
|
||||
if i+1 < n && p.buffer[i+1] == unindent {
|
||||
p.buffer[i], p.buffer[i+1] = unindent, ch;
|
||||
i--; // do it again
|
||||
continue;
|
||||
}
|
||||
fallthrough;
|
||||
default:
|
||||
data[0] = byte(ch);
|
||||
p.write(&data, 0);
|
||||
}
|
||||
}
|
||||
|
||||
var b []byte = &a;
|
||||
b = b[0 : p.buflen];
|
||||
p.buflen = 0;
|
||||
|
||||
p.write(b, 0);
|
||||
// shift remaining entries down
|
||||
i := 0;
|
||||
for ; n < len(p.buffer); n++ {
|
||||
p.buffer[i] = p.buffer[n];
|
||||
i++;
|
||||
}
|
||||
p.buffer = p.buffer[0:i];
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Printing interface
|
||||
|
||||
// print prints a list of "items" (roughly corresponding to syntactic
|
||||
// tokens, but also including whitespace and formatting information).
|
||||
// It is the only print function that should be called directly from
|
||||
|
|
@ -352,21 +480,17 @@ func (p *printer) print(args ...) {
|
|||
next := p.pos; // estimated position of next item
|
||||
var data []byte;
|
||||
switch x := f.Interface().(type) {
|
||||
case int:
|
||||
// indentation delta
|
||||
p.indent += x;
|
||||
if p.indent < 0 {
|
||||
panicln("print: negative indentation", p.indent);
|
||||
}
|
||||
case whiteSpace:
|
||||
if p.buflen >= len(p.buffer) {
|
||||
i := len(p.buffer);
|
||||
if i == cap(p.buffer) {
|
||||
// Whitespace sequences are very short so this should
|
||||
// never happen. Handle gracefully (but possibly with
|
||||
// bad comment placement) if it does happen.
|
||||
p.writeWhitespace();
|
||||
p.writeWhitespace(i);
|
||||
i = 0;
|
||||
}
|
||||
p.buffer[p.buflen] = x;
|
||||
p.buflen++;
|
||||
p.buffer = p.buffer[0 : i+1];
|
||||
p.buffer[i] = x;
|
||||
case []byte:
|
||||
data = x;
|
||||
// do not modify multi-line `` strings!
|
||||
|
|
@ -398,6 +522,8 @@ func (p *printer) print(args ...) {
|
|||
p.flush(next);
|
||||
|
||||
// intersperse extra newlines if present in the source
|
||||
// (don't do this in flush as it will cause extra newlines
|
||||
// at the end of a file)
|
||||
p.writeNewlines(next.Line - p.pos.Line);
|
||||
|
||||
p.writeItem(next, data, mode);
|
||||
|
|
@ -407,16 +533,24 @@ func (p *printer) print(args ...) {
|
|||
}
|
||||
|
||||
|
||||
// commentBefore returns true iff the current comment occurs
|
||||
// before the next position in the source code.
|
||||
//
|
||||
func (p *printer) commentBefore(next token.Position) bool {
|
||||
return p.comment != nil && p.comment.List[0].Pos().Offset < next.Offset;
|
||||
}
|
||||
|
||||
|
||||
// Flush prints any pending comments and whitespace occuring
|
||||
// textually before the position of the next item.
|
||||
//
|
||||
func (p *printer) flush(next token.Position) {
|
||||
// if there are comments before the next item, intersperse them
|
||||
if p.comment != nil && p.comment.List[0].Pos().Offset < next.Offset {
|
||||
if p.commentBefore(next) {
|
||||
p.intersperseComments(next);
|
||||
}
|
||||
|
||||
p.writeWhitespace();
|
||||
// write any leftover whitespace
|
||||
p.writeWhitespace(len(p.buffer));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -426,7 +560,7 @@ func (p *printer) flush(next token.Position) {
|
|||
|
||||
// Print as many newlines as necessary (but at least min and and at most
|
||||
// max newlines) to get to the current line. If newSection is set, the
|
||||
// first newline is printed as a formfeed. Returns true if any linebreak
|
||||
// first newline is printed as a formfeed. Returns true if any line break
|
||||
// was printed; returns false otherwise.
|
||||
//
|
||||
// TODO(gri): Reconsider signature (provide position instead of line)
|
||||
|
|
@ -477,15 +611,13 @@ func (p *printer) leadComment(d *ast.CommentGroup) {
|
|||
}
|
||||
|
||||
|
||||
// Print n tabs followed by a line comment.
|
||||
// Print a tab followed by a line comment.
|
||||
// A newline must be printed afterwards since
|
||||
// the comment may be a //-style comment.
|
||||
func (p *printer) lineComment(n int, d *ast.CommentGroup) {
|
||||
func (p *printer) lineComment(d *ast.CommentGroup) {
|
||||
// Ignore the comment if we have comments interspersed (p.comment != nil).
|
||||
if p.comment == nil && d != nil {
|
||||
for ; n > 0; n-- {
|
||||
p.print(vtab);
|
||||
}
|
||||
p.print(vtab);
|
||||
p.commentList(d.List);
|
||||
}
|
||||
}
|
||||
|
|
@ -551,10 +683,10 @@ func (p *printer) exprList(list []ast.Expr, mode exprListMode) {
|
|||
// 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
|
||||
// there may or may not be a line break before the first list
|
||||
// element; in any case indent once after the first line break
|
||||
if p.linebreak(line, 0, 2, true) && !indented {
|
||||
p.print(+1);
|
||||
p.print(htab, indent); // indent applies to next line
|
||||
indented = true;
|
||||
}
|
||||
for i, x := range list {
|
||||
|
|
@ -565,10 +697,10 @@ func (p *printer) exprList(list []ast.Expr, mode exprListMode) {
|
|||
p.print(token.COMMA);
|
||||
}
|
||||
if prev < line {
|
||||
// at least one linebreak, but respect an extra empty line
|
||||
// at least one line break, but respect an extra empty line
|
||||
// in the source
|
||||
if p.linebreak(x.Pos().Line, 1, 2, true) && !indented {
|
||||
p.print(+1);
|
||||
p.print(htab, indent); // indent applies to next line
|
||||
indented = true;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -582,11 +714,11 @@ func (p *printer) exprList(list []ast.Expr, mode exprListMode) {
|
|||
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(unindent);
|
||||
}
|
||||
p.print(formfeed); // terminating comma needs a line break to look good
|
||||
} else if indented && mode&noIndent == 0 {
|
||||
p.print(-1);
|
||||
p.print(unindent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -598,9 +730,8 @@ func (p *printer) parameters(list []*ast.Field) {
|
|||
if i > 0 {
|
||||
p.print(token.COMMA, blank);
|
||||
}
|
||||
p.identList(par.Names);
|
||||
if len(par.Names) > 0 {
|
||||
// at least one identifier
|
||||
p.identList(par.Names);
|
||||
p.print(blank);
|
||||
}
|
||||
p.expr(par.Type);
|
||||
|
|
@ -632,33 +763,47 @@ func (p *printer) signature(params, result []*ast.Field) (optSemi bool) {
|
|||
|
||||
|
||||
func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace token.Position, isIncomplete, isStruct bool) {
|
||||
if len(list) == 0 && !isIncomplete {
|
||||
if len(list) == 0 && !isIncomplete && !p.commentBefore(rbrace) {
|
||||
// no blank between keyword and {} in this case
|
||||
// TODO(gri): This will not look nice if there are comments inside the {}'s.
|
||||
p.print(lbrace, token.LBRACE, rbrace, token.RBRACE);
|
||||
return;
|
||||
}
|
||||
|
||||
// at least one entry or incomplete
|
||||
p.print(blank, lbrace, token.LBRACE, +1, formfeed);
|
||||
p.print(blank, lbrace, token.LBRACE, indent, formfeed);
|
||||
if isStruct {
|
||||
sep := blank;
|
||||
if len(list) > 1 {
|
||||
sep = vtab;
|
||||
|
||||
sep := vtab;
|
||||
if len(list) == 1 {
|
||||
sep = blank;
|
||||
}
|
||||
for i, f := range list {
|
||||
extraTabs := 0;
|
||||
p.leadComment(f.Doc);
|
||||
if len(f.Names) > 0 {
|
||||
p.identList(f.Names);
|
||||
p.print(sep);
|
||||
p.expr(f.Type);
|
||||
extraTabs = 1;
|
||||
} else {
|
||||
p.expr(f.Type);
|
||||
extraTabs = 2;
|
||||
}
|
||||
p.expr(f.Type);
|
||||
if f.Tag != nil {
|
||||
if len(f.Names) > 0 && sep == vtab {
|
||||
p.print(sep);
|
||||
}
|
||||
p.print(sep);
|
||||
p.expr(&ast.StringList{f.Tag});
|
||||
extraTabs = 0;
|
||||
}
|
||||
p.print(token.SEMICOLON);
|
||||
p.lineComment(1, f.Comment);
|
||||
if f.Comment != nil {
|
||||
for ; extraTabs > 0; extraTabs-- {
|
||||
p.print(vtab);
|
||||
}
|
||||
p.lineComment(f.Comment);
|
||||
}
|
||||
if i+1 < len(list) || isIncomplete {
|
||||
p.print(newline);
|
||||
}
|
||||
|
|
@ -666,7 +811,9 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
|
|||
if isIncomplete {
|
||||
p.print("// contains unexported fields");
|
||||
}
|
||||
|
||||
} else { // interface
|
||||
|
||||
for i, f := range list {
|
||||
p.leadComment(f.Doc);
|
||||
if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
|
||||
|
|
@ -678,7 +825,7 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
|
|||
p.expr(f.Type);
|
||||
}
|
||||
p.print(token.SEMICOLON);
|
||||
p.lineComment(1, f.Comment);
|
||||
p.lineComment(f.Comment);
|
||||
if i+1 < len(list) || isIncomplete {
|
||||
p.print(newline);
|
||||
}
|
||||
|
|
@ -686,8 +833,9 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
|
|||
if isIncomplete {
|
||||
p.print("// contains unexported methods");
|
||||
}
|
||||
|
||||
}
|
||||
p.print(-1, formfeed, rbrace, token.RBRACE);
|
||||
p.print(unindent, formfeed, rbrace, token.RBRACE);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -767,10 +915,10 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1 int) {
|
|||
if printBlanks {
|
||||
if prev != line {
|
||||
p.print(blank, x.OpPos, x.Op);
|
||||
// at least one linebreak, but respect an extra empty line
|
||||
// at least one line break, but respect an extra empty line
|
||||
// in the source
|
||||
if p.linebreak(line, 1, 2, false) && !indented {
|
||||
p.print(+1);
|
||||
p.print(htab, indent); // indent applies to next line
|
||||
indented = true;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -785,7 +933,7 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1 int) {
|
|||
p.expr1(x.Y, prec);
|
||||
}
|
||||
if indented {
|
||||
p.print(-1);
|
||||
p.print(unindent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -838,7 +986,7 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
|
|||
case *ast.FuncLit:
|
||||
p.expr(x.Type);
|
||||
p.print(blank);
|
||||
p.stmt(x.Body);
|
||||
p.block(x.Body, 1);
|
||||
|
||||
case *ast.ParenExpr:
|
||||
p.print(token.LPAREN);
|
||||
|
|
@ -953,23 +1101,28 @@ const maxStmtNewlines = 2 // maximum number of newlines between statements
|
|||
// Print the statement list indented, but without a newline after the last statement.
|
||||
// Extra line breaks between statements in the source are respected but at most one
|
||||
// empty line is printed between statements.
|
||||
func (p *printer) stmtList(list []ast.Stmt, indent int) {
|
||||
p.print(+indent);
|
||||
func (p *printer) stmtList(list []ast.Stmt, _indent int) {
|
||||
// TODO(gri): fix _indent code
|
||||
if _indent > 0 {
|
||||
p.print(indent);
|
||||
}
|
||||
for i, s := range list {
|
||||
// indent == 0 only for lists of switch/select case clauses;
|
||||
// _indent == 0 only for lists of switch/select case clauses;
|
||||
// in those cases each clause is a new section
|
||||
p.linebreak(s.Pos().Line, 1, maxStmtNewlines, i == 0 || indent == 0);
|
||||
p.linebreak(s.Pos().Line, 1, maxStmtNewlines, i == 0 || _indent == 0);
|
||||
if !p.stmt(s) {
|
||||
p.print(token.SEMICOLON);
|
||||
}
|
||||
}
|
||||
p.print(-indent);
|
||||
if _indent > 0 {
|
||||
p.print(unindent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (p *printer) block(s *ast.BlockStmt, indent int) {
|
||||
p.print(s.Pos(), token.LBRACE);
|
||||
if len(s.List) > 0 {
|
||||
if len(s.List) > 0 || p.commentBefore(s.Rbrace) {
|
||||
p.stmtList(s.List, indent);
|
||||
p.linebreak(s.Rbrace.Line, 1, maxStmtNewlines, true);
|
||||
}
|
||||
|
|
@ -1039,11 +1192,12 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) {
|
|||
// nothing to do
|
||||
|
||||
case *ast.LabeledStmt:
|
||||
// whitespace printing is delayed, thus indentation adjustments
|
||||
// take place before the previous newline/formfeed is printed
|
||||
p.print(-1);
|
||||
// a "correcting" unindent immediately following a line break
|
||||
// is applied before the line break if there is no comment
|
||||
// between (see writeWhitespace)
|
||||
p.print(unindent);
|
||||
p.expr(s.Label);
|
||||
p.print(token.COLON, vtab, +1);
|
||||
p.print(token.COLON, vtab, indent);
|
||||
p.linebreak(s.Stmt.Pos().Line, 0, 1, true);
|
||||
optSemi = p.stmt(s.Stmt);
|
||||
|
||||
|
|
@ -1095,9 +1249,9 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) {
|
|||
case *ast.BlockStmt, *ast.IfStmt:
|
||||
optSemi = p.stmt(s.Else);
|
||||
default:
|
||||
p.print(token.LBRACE, +1, formfeed);
|
||||
p.print(token.LBRACE, indent, formfeed);
|
||||
p.stmt(s.Else);
|
||||
p.print(-1, formfeed, token.RBRACE);
|
||||
p.print(unindent, formfeed, token.RBRACE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1207,29 +1361,22 @@ 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
|
||||
extraTabs int; // number of extra tabs before comment, if any
|
||||
)
|
||||
|
||||
switch s := spec.(type) {
|
||||
case *ast.ImportSpec:
|
||||
p.leadComment(s.Doc);
|
||||
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(vtab); // column discarded if empty
|
||||
if s.Name != nil {
|
||||
p.expr(s.Name);
|
||||
p.print(blank);
|
||||
}
|
||||
p.expr(&ast.StringList{s.Path});
|
||||
comment = s.Comment;
|
||||
|
||||
case *ast.ValueSpec:
|
||||
p.leadComment(s.Doc);
|
||||
p.identList(s.Names); // never empty
|
||||
p.identList(s.Names); // always present
|
||||
if n == 1 {
|
||||
if s.Type != nil {
|
||||
p.print(blank);
|
||||
|
|
@ -1241,20 +1388,20 @@ func (p *printer) spec(spec ast.Spec, n int, context declContext) {
|
|||
optSemi = false;
|
||||
}
|
||||
} else {
|
||||
columns = 2;
|
||||
extraTabs = 2;
|
||||
if s.Type != nil || s.Values != nil {
|
||||
p.print(vtab);
|
||||
}
|
||||
if s.Type != nil {
|
||||
optSemi = p.expr(s.Type);
|
||||
columns = 1;
|
||||
extraTabs = 1;
|
||||
}
|
||||
if s.Values != nil {
|
||||
p.print(vtab);
|
||||
p.print(token.ASSIGN);
|
||||
p.exprList(s.Values, blankStart | commaSep);
|
||||
optSemi = false;
|
||||
columns = 0;
|
||||
extraTabs = 0;
|
||||
}
|
||||
}
|
||||
comment = s.Comment;
|
||||
|
|
@ -1278,7 +1425,12 @@ func (p *printer) spec(spec ast.Spec, n int, context declContext) {
|
|||
p.print(token.SEMICOLON);
|
||||
}
|
||||
|
||||
p.lineComment(1+columns, comment);
|
||||
if comment != nil {
|
||||
for ; extraTabs > 0; extraTabs-- {
|
||||
p.print(vtab);
|
||||
}
|
||||
p.lineComment(comment);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1295,14 +1447,14 @@ func (p *printer) decl(decl ast.Decl, context declContext) {
|
|||
// group of parenthesized declarations
|
||||
p.print(d.Lparen, token.LPAREN);
|
||||
if len(d.Specs) > 0 {
|
||||
p.print(+1, formfeed);
|
||||
p.print(indent, formfeed);
|
||||
for i, s := range d.Specs {
|
||||
if i > 0 {
|
||||
p.print(newline);
|
||||
}
|
||||
p.spec(s, len(d.Specs), inGroup);
|
||||
}
|
||||
p.print(-1, formfeed);
|
||||
p.print(unindent, formfeed);
|
||||
}
|
||||
p.print(d.Rparen, token.RPAREN);
|
||||
|
||||
|
|
@ -1328,7 +1480,7 @@ func (p *printer) decl(decl ast.Decl, context declContext) {
|
|||
p.signature(d.Type.Params, d.Type.Results);
|
||||
if d.Body != nil {
|
||||
p.print(blank);
|
||||
p.stmt(d.Body);
|
||||
p.block(d.Body, 1);
|
||||
}
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This is a package for testing purposes.
|
||||
// This is a package for testing comment placement by go/printer.
|
||||
//
|
||||
package main
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ type I1 interface {
|
|||
}
|
||||
|
||||
// The I2 interface; all methods are exported.
|
||||
type I1 interface {
|
||||
type I2 interface {
|
||||
I0;
|
||||
F(x float) float; // exported method
|
||||
G(x float) float; // exported method
|
||||
|
|
@ -91,6 +91,11 @@ func f1() {
|
|||
}
|
||||
|
||||
|
||||
func _() {
|
||||
// this comment should be properly indented
|
||||
}
|
||||
|
||||
|
||||
func abs(x int) int {
|
||||
if x < 0 { // the tab printed before this comment's // must not affect the remaining lines
|
||||
return -x; // this statement should be properly indented
|
||||
|
|
@ -114,9 +119,26 @@ func typeswitch(x interface{}) {
|
|||
|
||||
switch v0, ok := x.(int); x.(type) {
|
||||
case bool, int, float:
|
||||
// this comment should be indented
|
||||
case string:
|
||||
default:
|
||||
// this comment should be indented
|
||||
}
|
||||
// this comment should be indented
|
||||
}
|
||||
|
||||
|
||||
// Line comments with tabs
|
||||
func _() {
|
||||
var finput *bufio.Reader; // input file
|
||||
var stderr *bufio.Writer;
|
||||
var ftable *bufio.Writer; // y.go file
|
||||
var foutput *bufio.Writer; // y.output file
|
||||
|
||||
var oflag string; // -o [y.go] - y.go file
|
||||
var vflag string; // -v [y.output] - y.output file
|
||||
var lflag bool; // -l - disable line directives
|
||||
}
|
||||
|
||||
|
||||
// This comment is the last entry in this file. It must be printed.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This is a package for testing purposes.
|
||||
// This is a package for testing comment placement by go/printer.
|
||||
//
|
||||
package main
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ type I1 interface {
|
|||
}
|
||||
|
||||
// The I2 interface; all methods are exported.
|
||||
type I1 interface {
|
||||
type I2 interface {
|
||||
I0;
|
||||
F(x float) float; // exported method
|
||||
G(x float) float; // exported method
|
||||
|
|
@ -91,6 +91,11 @@ func f1() {
|
|||
}
|
||||
|
||||
|
||||
func _() {
|
||||
// this comment should be properly indented
|
||||
}
|
||||
|
||||
|
||||
func abs(x int) int {
|
||||
if x < 0 { // the tab printed before this comment's // must not affect the remaining lines
|
||||
return -x; // this statement should be properly indented
|
||||
|
|
@ -112,9 +117,26 @@ func typeswitch(x interface{}) {
|
|||
|
||||
switch v0, ok := x.(int); x.(type) {
|
||||
case bool, int, float:
|
||||
// this comment should be indented
|
||||
case string:
|
||||
default:
|
||||
// this comment should be indented
|
||||
}
|
||||
// this comment should be indented
|
||||
}
|
||||
|
||||
|
||||
// Line comments with tabs
|
||||
func _() {
|
||||
var finput *bufio.Reader; // input file
|
||||
var stderr *bufio.Writer;
|
||||
var ftable *bufio.Writer; // y.go file
|
||||
var foutput *bufio.Writer; // y.output file
|
||||
|
||||
var oflag string; // -o [y.go] - y.go file
|
||||
var vflag string; // -v [y.output] - y.output file
|
||||
var lflag bool; // -l - disable line directives
|
||||
}
|
||||
|
||||
|
||||
// This comment is the last entry in this file. It must be printed.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// This is a package for testing purposes.
|
||||
// This is a package for testing comment placement by go/printer.
|
||||
//
|
||||
package main
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ type I1 interface {
|
|||
|
||||
|
||||
// The I2 interface; all methods are exported.
|
||||
type I1 interface {
|
||||
type I2 interface {
|
||||
I0;
|
||||
F(x float) float;
|
||||
G(x float) float;
|
||||
|
|
|
|||
|
|
@ -175,6 +175,14 @@ func _() {
|
|||
y = 20; // comment
|
||||
f, ff, fff, ffff int = 0, 1, 2, 3; // comment
|
||||
)
|
||||
// respect original line breaks
|
||||
var _ = []T {
|
||||
T{0x20, "Telugu"}
|
||||
};
|
||||
var _ = []T {
|
||||
// respect original line breaks
|
||||
T{0x20, "Telugu"}
|
||||
};
|
||||
}
|
||||
|
||||
func _() {
|
||||
|
|
@ -194,7 +202,13 @@ func _() {
|
|||
|
||||
|
||||
// formatting of structs
|
||||
type ES struct{}
|
||||
type _ struct{}
|
||||
|
||||
type _ struct{ /* this comment should be visible */ }
|
||||
|
||||
type _ struct{
|
||||
// this comment should be visible and properly indented
|
||||
}
|
||||
|
||||
type _ struct { // this comment must not change indentation
|
||||
f int;
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ import (
|
|||
)
|
||||
|
||||
import (
|
||||
"io";
|
||||
aLongRename "io";
|
||||
b "io";
|
||||
c "i" "o";
|
||||
"io";
|
||||
aLongRename "io";
|
||||
b "io";
|
||||
c "i" "o";
|
||||
)
|
||||
|
||||
// no newlines between consecutive single imports, but
|
||||
|
|
@ -173,6 +173,11 @@ func _() {
|
|||
y = 20; // comment
|
||||
f, ff, fff, ffff int = 0, 1, 2, 3; // comment
|
||||
)
|
||||
// respect original line breaks
|
||||
var _ = []T{T{0x20, "Telugu"}};
|
||||
var _ = []T{
|
||||
// respect original line breaks
|
||||
T{0x20, "Telugu"}};
|
||||
}
|
||||
|
||||
func _() {
|
||||
|
|
@ -192,7 +197,13 @@ func _() {
|
|||
|
||||
|
||||
// formatting of structs
|
||||
type ES struct{}
|
||||
type _ struct{}
|
||||
|
||||
type _ struct { /* this comment should be visible */}
|
||||
|
||||
type _ struct {
|
||||
// this comment should be visible and properly indented
|
||||
}
|
||||
|
||||
type _ struct { // this comment must not change indentation
|
||||
f int;
|
||||
|
|
@ -235,7 +246,7 @@ type _ struct {
|
|||
bool;
|
||||
a, b, c int;
|
||||
int "tag";
|
||||
ES; // comment
|
||||
ES; // comment
|
||||
float "tag"; // comment
|
||||
f int; // comment
|
||||
f, ff, fff, ffff int; // comment
|
||||
|
|
@ -246,7 +257,7 @@ type _ struct {
|
|||
|
||||
// difficult cases
|
||||
type _ struct {
|
||||
bool; // comment
|
||||
bool; // comment
|
||||
text []byte; // comment
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -165,3 +165,14 @@ func (p *parser) charClass() {
|
|||
p.re.add(nl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func addState(s []state, inst instr, match []int) {
|
||||
// handle comments correctly in multi-line expressions
|
||||
for i := 0; i < l; i++ {
|
||||
if s[i].inst.index() == index && // same instruction
|
||||
s[i].match[0] < pos { // earlier match already going; leftmost wins
|
||||
return s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,11 +108,9 @@ func _() {
|
|||
// do not modify `` strings
|
||||
_ = ``;
|
||||
_ = `
|
||||
`;
|
||||
// TODO(gri): fix line breaks here
|
||||
`; // TODO(gri): fix line breaks here
|
||||
_ = `foo
|
||||
bar`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -140,8 +138,8 @@ func _() {
|
|||
b < a;
|
||||
_ = "1234567890"
|
||||
"1234567890";
|
||||
// TODO(gri): add more test cases
|
||||
// TODO(gri): these comments should be indented
|
||||
// TODO(gri): add more test cases
|
||||
// TODO(gri): these comments should be indented
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -167,3 +165,15 @@ func (p *parser) charClass() {
|
|||
p.re.add(nl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func addState(s []state, inst instr, match []int) {
|
||||
// handle comments correctly in multi-line expressions
|
||||
for i := 0; i < l; i++ {
|
||||
if s[i].inst.index() == index &&
|
||||
// same instruction
|
||||
s[i].match[0] < pos { // earlier match already going; leftmost wins
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,7 +181,9 @@ var facts = map[int]string{
|
|||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
// TODO(gri): the 2nd string of this string list should not be indented
|
||||
|
||||
|
||||
// TODO(gri): the 2nd string of this string list should not be indented
|
||||
"usage: godoc package [name ...]\n"
|
||||
" godoc -http=:6060\n");
|
||||
flag.PrintDefaults();
|
||||
|
|
|
|||
|
|
@ -123,6 +123,18 @@ func _() {
|
|||
|
||||
|
||||
func _() {
|
||||
// this comment should be indented
|
||||
L:
|
||||
}
|
||||
|
||||
|
||||
func _() {
|
||||
L: _ = 0;
|
||||
}
|
||||
|
||||
|
||||
func _() {
|
||||
// this comment should be indented
|
||||
L: _ = 0;
|
||||
}
|
||||
|
||||
|
|
@ -134,3 +146,13 @@ func _() {
|
|||
_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func _() {
|
||||
// this comment should be indented
|
||||
for {
|
||||
L1: _ = 0;
|
||||
L2:
|
||||
_ = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,6 +139,19 @@ L:
|
|||
|
||||
|
||||
func _() {
|
||||
// this comment should be indented
|
||||
L:
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
func _() {
|
||||
L: _ = 0;
|
||||
}
|
||||
|
||||
|
||||
func _() {
|
||||
// this comment should be indented
|
||||
L: _ = 0;
|
||||
}
|
||||
|
||||
|
|
@ -150,3 +163,13 @@ func _() {
|
|||
_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func _() {
|
||||
// this comment should be indented
|
||||
for {
|
||||
L1: _ = 0;
|
||||
L2:
|
||||
_ = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue