...

Source file src/go/printer/printer.go

Documentation: go/printer

		 1  // Copyright 2009 The Go Authors. All rights reserved.
		 2  // Use of this source code is governed by a BSD-style
		 3  // license that can be found in the LICENSE file.
		 4  
		 5  // Package printer implements printing of AST nodes.
		 6  package printer
		 7  
		 8  import (
		 9  	"fmt"
		10  	"go/ast"
		11  	"go/build/constraint"
		12  	"go/token"
		13  	"io"
		14  	"os"
		15  	"strings"
		16  	"text/tabwriter"
		17  	"unicode"
		18  )
		19  
		20  const (
		21  	maxNewlines = 2		 // max. number of newlines between source text
		22  	debug			 = false // enable for debugging
		23  	infinity		= 1 << 30
		24  )
		25  
		26  type whiteSpace byte
		27  
		28  const (
		29  	ignore	 = whiteSpace(0)
		30  	blank		= whiteSpace(' ')
		31  	vtab		 = whiteSpace('\v')
		32  	newline	= whiteSpace('\n')
		33  	formfeed = whiteSpace('\f')
		34  	indent	 = whiteSpace('>')
		35  	unindent = whiteSpace('<')
		36  )
		37  
		38  // A pmode value represents the current printer mode.
		39  type pmode int
		40  
		41  const (
		42  	noExtraBlank		 pmode = 1 << iota // disables extra blank after /*-style comment
		43  	noExtraLinebreak									 // disables extra line break after /*-style comment
		44  )
		45  
		46  type commentInfo struct {
		47  	cindex				 int							 // current comment index
		48  	comment				*ast.CommentGroup // = printer.comments[cindex]; or nil
		49  	commentOffset	int							 // = printer.posFor(printer.comments[cindex].List[0].Pos()).Offset; or infinity
		50  	commentNewline bool							// true if the comment group contains newlines
		51  }
		52  
		53  type printer struct {
		54  	// Configuration (does not change after initialization)
		55  	Config
		56  	fset *token.FileSet
		57  
		58  	// Current state
		59  	output			 []byte			 // raw printer result
		60  	indent			 int					// current indentation
		61  	level				int					// level == 0: outside composite literal; level > 0: inside composite literal
		62  	mode				 pmode				// current printer mode
		63  	endAlignment bool				 // if set, terminate alignment immediately
		64  	impliedSemi	bool				 // if set, a linebreak implies a semicolon
		65  	lastTok			token.Token	// last token printed (token.ILLEGAL if it's whitespace)
		66  	prevOpen		 token.Token	// previous non-brace "open" token (, [, or token.ILLEGAL
		67  	wsbuf				[]whiteSpace // delayed white space
		68  	goBuild			[]int				// start index of all //go:build comments in output
		69  	plusBuild		[]int				// start index of all // +build comments in output
		70  
		71  	// Positions
		72  	// The out position differs from the pos position when the result
		73  	// formatting differs from the source formatting (in the amount of
		74  	// white space). If there's a difference and SourcePos is set in
		75  	// ConfigMode, //line directives are used in the output to restore
		76  	// original source positions for a reader.
		77  	pos		 token.Position // current position in AST (source) space
		78  	out		 token.Position // current position in output space
		79  	last		token.Position // value of pos after calling writeString
		80  	linePtr *int					 // if set, record out.Line for the next token in *linePtr
		81  
		82  	// The list of all source comments, in order of appearance.
		83  	comments				[]*ast.CommentGroup // may be nil
		84  	useNodeComments bool								// if not set, ignore lead and line comments of nodes
		85  
		86  	// Information about p.comments[p.cindex]; set up by nextComment.
		87  	commentInfo
		88  
		89  	// Cache of already computed node sizes.
		90  	nodeSizes map[ast.Node]int
		91  
		92  	// Cache of most recently computed line position.
		93  	cachedPos	token.Pos
		94  	cachedLine int // line corresponding to cachedPos
		95  }
		96  
		97  func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) {
		98  	p.Config = *cfg
		99  	p.fset = fset
	 100  	p.pos = token.Position{Line: 1, Column: 1}
	 101  	p.out = token.Position{Line: 1, Column: 1}
	 102  	p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short
	 103  	p.nodeSizes = nodeSizes
	 104  	p.cachedPos = -1
	 105  }
	 106  
	 107  func (p *printer) internalError(msg ...interface{}) {
	 108  	if debug {
	 109  		fmt.Print(p.pos.String() + ": ")
	 110  		fmt.Println(msg...)
	 111  		panic("go/printer")
	 112  	}
	 113  }
	 114  
	 115  // commentsHaveNewline reports whether a list of comments belonging to
	 116  // an *ast.CommentGroup contains newlines. Because the position information
	 117  // may only be partially correct, we also have to read the comment text.
	 118  func (p *printer) commentsHaveNewline(list []*ast.Comment) bool {
	 119  	// len(list) > 0
	 120  	line := p.lineFor(list[0].Pos())
	 121  	for i, c := range list {
	 122  		if i > 0 && p.lineFor(list[i].Pos()) != line {
	 123  			// not all comments on the same line
	 124  			return true
	 125  		}
	 126  		if t := c.Text; len(t) >= 2 && (t[1] == '/' || strings.Contains(t, "\n")) {
	 127  			return true
	 128  		}
	 129  	}
	 130  	_ = line
	 131  	return false
	 132  }
	 133  
	 134  func (p *printer) nextComment() {
	 135  	for p.cindex < len(p.comments) {
	 136  		c := p.comments[p.cindex]
	 137  		p.cindex++
	 138  		if list := c.List; len(list) > 0 {
	 139  			p.comment = c
	 140  			p.commentOffset = p.posFor(list[0].Pos()).Offset
	 141  			p.commentNewline = p.commentsHaveNewline(list)
	 142  			return
	 143  		}
	 144  		// we should not reach here (correct ASTs don't have empty
	 145  		// ast.CommentGroup nodes), but be conservative and try again
	 146  	}
	 147  	// no more comments
	 148  	p.commentOffset = infinity
	 149  }
	 150  
	 151  // commentBefore reports whether the current comment group occurs
	 152  // before the next position in the source code and printing it does
	 153  // not introduce implicit semicolons.
	 154  //
	 155  func (p *printer) commentBefore(next token.Position) bool {
	 156  	return p.commentOffset < next.Offset && (!p.impliedSemi || !p.commentNewline)
	 157  }
	 158  
	 159  // commentSizeBefore returns the estimated size of the
	 160  // comments on the same line before the next position.
	 161  //
	 162  func (p *printer) commentSizeBefore(next token.Position) int {
	 163  	// save/restore current p.commentInfo (p.nextComment() modifies it)
	 164  	defer func(info commentInfo) {
	 165  		p.commentInfo = info
	 166  	}(p.commentInfo)
	 167  
	 168  	size := 0
	 169  	for p.commentBefore(next) {
	 170  		for _, c := range p.comment.List {
	 171  			size += len(c.Text)
	 172  		}
	 173  		p.nextComment()
	 174  	}
	 175  	return size
	 176  }
	 177  
	 178  // recordLine records the output line number for the next non-whitespace
	 179  // token in *linePtr. It is used to compute an accurate line number for a
	 180  // formatted construct, independent of pending (not yet emitted) whitespace
	 181  // or comments.
	 182  //
	 183  func (p *printer) recordLine(linePtr *int) {
	 184  	p.linePtr = linePtr
	 185  }
	 186  
	 187  // linesFrom returns the number of output lines between the current
	 188  // output line and the line argument, ignoring any pending (not yet
	 189  // emitted) whitespace or comments. It is used to compute an accurate
	 190  // size (in number of lines) for a formatted construct.
	 191  //
	 192  func (p *printer) linesFrom(line int) int {
	 193  	return p.out.Line - line
	 194  }
	 195  
	 196  func (p *printer) posFor(pos token.Pos) token.Position {
	 197  	// not used frequently enough to cache entire token.Position
	 198  	return p.fset.PositionFor(pos, false /* absolute position */)
	 199  }
	 200  
	 201  func (p *printer) lineFor(pos token.Pos) int {
	 202  	if pos != p.cachedPos {
	 203  		p.cachedPos = pos
	 204  		p.cachedLine = p.fset.PositionFor(pos, false /* absolute position */).Line
	 205  	}
	 206  	return p.cachedLine
	 207  }
	 208  
	 209  // writeLineDirective writes a //line directive if necessary.
	 210  func (p *printer) writeLineDirective(pos token.Position) {
	 211  	if pos.IsValid() && (p.out.Line != pos.Line || p.out.Filename != pos.Filename) {
	 212  		p.output = append(p.output, tabwriter.Escape) // protect '\n' in //line from tabwriter interpretation
	 213  		p.output = append(p.output, fmt.Sprintf("//line %s:%d\n", pos.Filename, pos.Line)...)
	 214  		p.output = append(p.output, tabwriter.Escape)
	 215  		// p.out must match the //line directive
	 216  		p.out.Filename = pos.Filename
	 217  		p.out.Line = pos.Line
	 218  	}
	 219  }
	 220  
	 221  // writeIndent writes indentation.
	 222  func (p *printer) writeIndent() {
	 223  	// use "hard" htabs - indentation columns
	 224  	// must not be discarded by the tabwriter
	 225  	n := p.Config.Indent + p.indent // include base indentation
	 226  	for i := 0; i < n; i++ {
	 227  		p.output = append(p.output, '\t')
	 228  	}
	 229  
	 230  	// update positions
	 231  	p.pos.Offset += n
	 232  	p.pos.Column += n
	 233  	p.out.Column += n
	 234  }
	 235  
	 236  // writeByte writes ch n times to p.output and updates p.pos.
	 237  // Only used to write formatting (white space) characters.
	 238  func (p *printer) writeByte(ch byte, n int) {
	 239  	if p.endAlignment {
	 240  		// Ignore any alignment control character;
	 241  		// and at the end of the line, break with
	 242  		// a formfeed to indicate termination of
	 243  		// existing columns.
	 244  		switch ch {
	 245  		case '\t', '\v':
	 246  			ch = ' '
	 247  		case '\n', '\f':
	 248  			ch = '\f'
	 249  			p.endAlignment = false
	 250  		}
	 251  	}
	 252  
	 253  	if p.out.Column == 1 {
	 254  		// no need to write line directives before white space
	 255  		p.writeIndent()
	 256  	}
	 257  
	 258  	for i := 0; i < n; i++ {
	 259  		p.output = append(p.output, ch)
	 260  	}
	 261  
	 262  	// update positions
	 263  	p.pos.Offset += n
	 264  	if ch == '\n' || ch == '\f' {
	 265  		p.pos.Line += n
	 266  		p.out.Line += n
	 267  		p.pos.Column = 1
	 268  		p.out.Column = 1
	 269  		return
	 270  	}
	 271  	p.pos.Column += n
	 272  	p.out.Column += n
	 273  }
	 274  
	 275  // writeString writes the string s to p.output and updates p.pos, p.out,
	 276  // and p.last. If isLit is set, s is escaped w/ tabwriter.Escape characters
	 277  // to protect s from being interpreted by the tabwriter.
	 278  //
	 279  // Note: writeString is only used to write Go tokens, literals, and
	 280  // comments, all of which must be written literally. Thus, it is correct
	 281  // to always set isLit = true. However, setting it explicitly only when
	 282  // needed (i.e., when we don't know that s contains no tabs or line breaks)
	 283  // avoids processing extra escape characters and reduces run time of the
	 284  // printer benchmark by up to 10%.
	 285  //
	 286  func (p *printer) writeString(pos token.Position, s string, isLit bool) {
	 287  	if p.out.Column == 1 {
	 288  		if p.Config.Mode&SourcePos != 0 {
	 289  			p.writeLineDirective(pos)
	 290  		}
	 291  		p.writeIndent()
	 292  	}
	 293  
	 294  	if pos.IsValid() {
	 295  		// update p.pos (if pos is invalid, continue with existing p.pos)
	 296  		// Note: Must do this after handling line beginnings because
	 297  		// writeIndent updates p.pos if there's indentation, but p.pos
	 298  		// is the position of s.
	 299  		p.pos = pos
	 300  	}
	 301  
	 302  	if isLit {
	 303  		// Protect s such that is passes through the tabwriter
	 304  		// unchanged. Note that valid Go programs cannot contain
	 305  		// tabwriter.Escape bytes since they do not appear in legal
	 306  		// UTF-8 sequences.
	 307  		p.output = append(p.output, tabwriter.Escape)
	 308  	}
	 309  
	 310  	if debug {
	 311  		p.output = append(p.output, fmt.Sprintf("/*%s*/", pos)...) // do not update p.pos!
	 312  	}
	 313  	p.output = append(p.output, s...)
	 314  
	 315  	// update positions
	 316  	nlines := 0
	 317  	var li int // index of last newline; valid if nlines > 0
	 318  	for i := 0; i < len(s); i++ {
	 319  		// Raw string literals may contain any character except back quote (`).
	 320  		if ch := s[i]; ch == '\n' || ch == '\f' {
	 321  			// account for line break
	 322  			nlines++
	 323  			li = i
	 324  			// A line break inside a literal will break whatever column
	 325  			// formatting is in place; ignore any further alignment through
	 326  			// the end of the line.
	 327  			p.endAlignment = true
	 328  		}
	 329  	}
	 330  	p.pos.Offset += len(s)
	 331  	if nlines > 0 {
	 332  		p.pos.Line += nlines
	 333  		p.out.Line += nlines
	 334  		c := len(s) - li
	 335  		p.pos.Column = c
	 336  		p.out.Column = c
	 337  	} else {
	 338  		p.pos.Column += len(s)
	 339  		p.out.Column += len(s)
	 340  	}
	 341  
	 342  	if isLit {
	 343  		p.output = append(p.output, tabwriter.Escape)
	 344  	}
	 345  
	 346  	p.last = p.pos
	 347  }
	 348  
	 349  // writeCommentPrefix writes the whitespace before a comment.
	 350  // If there is any pending whitespace, it consumes as much of
	 351  // it as is likely to help position the comment nicely.
	 352  // pos is the comment position, next the position of the item
	 353  // after all pending comments, prev is the previous comment in
	 354  // a group of comments (or nil), and tok is the next token.
	 355  //
	 356  func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment, tok token.Token) {
	 357  	if len(p.output) == 0 {
	 358  		// the comment is the first item to be printed - don't write any whitespace
	 359  		return
	 360  	}
	 361  
	 362  	if pos.IsValid() && pos.Filename != p.last.Filename {
	 363  		// comment in a different file - separate with newlines
	 364  		p.writeByte('\f', maxNewlines)
	 365  		return
	 366  	}
	 367  
	 368  	if pos.Line == p.last.Line && (prev == nil || prev.Text[1] != '/') {
	 369  		// comment on the same line as last item:
	 370  		// separate with at least one separator
	 371  		hasSep := false
	 372  		if prev == nil {
	 373  			// first comment of a comment group
	 374  			j := 0
	 375  			for i, ch := range p.wsbuf {
	 376  				switch ch {
	 377  				case blank:
	 378  					// ignore any blanks before a comment
	 379  					p.wsbuf[i] = ignore
	 380  					continue
	 381  				case vtab:
	 382  					// respect existing tabs - important
	 383  					// for proper formatting of commented structs
	 384  					hasSep = true
	 385  					continue
	 386  				case indent:
	 387  					// apply pending indentation
	 388  					continue
	 389  				}
	 390  				j = i
	 391  				break
	 392  			}
	 393  			p.writeWhitespace(j)
	 394  		}
	 395  		// make sure there is at least one separator
	 396  		if !hasSep {
	 397  			sep := byte('\t')
	 398  			if pos.Line == next.Line {
	 399  				// next item is on the same line as the comment
	 400  				// (which must be a /*-style comment): separate
	 401  				// with a blank instead of a tab
	 402  				sep = ' '
	 403  			}
	 404  			p.writeByte(sep, 1)
	 405  		}
	 406  
	 407  	} else {
	 408  		// comment on a different line:
	 409  		// separate with at least one line break
	 410  		droppedLinebreak := false
	 411  		j := 0
	 412  		for i, ch := range p.wsbuf {
	 413  			switch ch {
	 414  			case blank, vtab:
	 415  				// ignore any horizontal whitespace before line breaks
	 416  				p.wsbuf[i] = ignore
	 417  				continue
	 418  			case indent:
	 419  				// apply pending indentation
	 420  				continue
	 421  			case unindent:
	 422  				// if this is not the last unindent, apply it
	 423  				// as it is (likely) belonging to the last
	 424  				// construct (e.g., a multi-line expression list)
	 425  				// and is not part of closing a block
	 426  				if i+1 < len(p.wsbuf) && p.wsbuf[i+1] == unindent {
	 427  					continue
	 428  				}
	 429  				// if the next token is not a closing }, apply the unindent
	 430  				// if it appears that the comment is aligned with the
	 431  				// token; otherwise assume the unindent is part of a
	 432  				// closing block and stop (this scenario appears with
	 433  				// comments before a case label where the comments
	 434  				// apply to the next case instead of the current one)
	 435  				if tok != token.RBRACE && pos.Column == next.Column {
	 436  					continue
	 437  				}
	 438  			case newline, formfeed:
	 439  				p.wsbuf[i] = ignore
	 440  				droppedLinebreak = prev == nil // record only if first comment of a group
	 441  			}
	 442  			j = i
	 443  			break
	 444  		}
	 445  		p.writeWhitespace(j)
	 446  
	 447  		// determine number of linebreaks before the comment
	 448  		n := 0
	 449  		if pos.IsValid() && p.last.IsValid() {
	 450  			n = pos.Line - p.last.Line
	 451  			if n < 0 { // should never happen
	 452  				n = 0
	 453  			}
	 454  		}
	 455  
	 456  		// at the package scope level only (p.indent == 0),
	 457  		// add an extra newline if we dropped one before:
	 458  		// this preserves a blank line before documentation
	 459  		// comments at the package scope level (issue 2570)
	 460  		if p.indent == 0 && droppedLinebreak {
	 461  			n++
	 462  		}
	 463  
	 464  		// make sure there is at least one line break
	 465  		// if the previous comment was a line comment
	 466  		if n == 0 && prev != nil && prev.Text[1] == '/' {
	 467  			n = 1
	 468  		}
	 469  
	 470  		if n > 0 {
	 471  			// use formfeeds to break columns before a comment;
	 472  			// this is analogous to using formfeeds to separate
	 473  			// individual lines of /*-style comments
	 474  			p.writeByte('\f', nlimit(n))
	 475  		}
	 476  	}
	 477  }
	 478  
	 479  // Returns true if s contains only white space
	 480  // (only tabs and blanks can appear in the printer's context).
	 481  //
	 482  func isBlank(s string) bool {
	 483  	for i := 0; i < len(s); i++ {
	 484  		if s[i] > ' ' {
	 485  			return false
	 486  		}
	 487  	}
	 488  	return true
	 489  }
	 490  
	 491  // commonPrefix returns the common prefix of a and b.
	 492  func commonPrefix(a, b string) string {
	 493  	i := 0
	 494  	for i < len(a) && i < len(b) && a[i] == b[i] && (a[i] <= ' ' || a[i] == '*') {
	 495  		i++
	 496  	}
	 497  	return a[0:i]
	 498  }
	 499  
	 500  // trimRight returns s with trailing whitespace removed.
	 501  func trimRight(s string) string {
	 502  	return strings.TrimRightFunc(s, unicode.IsSpace)
	 503  }
	 504  
	 505  // stripCommonPrefix removes a common prefix from /*-style comment lines (unless no
	 506  // comment line is indented, all but the first line have some form of space prefix).
	 507  // The prefix is computed using heuristics such that is likely that the comment
	 508  // contents are nicely laid out after re-printing each line using the printer's
	 509  // current indentation.
	 510  //
	 511  func stripCommonPrefix(lines []string) {
	 512  	if len(lines) <= 1 {
	 513  		return // at most one line - nothing to do
	 514  	}
	 515  	// len(lines) > 1
	 516  
	 517  	// The heuristic in this function tries to handle a few
	 518  	// common patterns of /*-style comments: Comments where
	 519  	// the opening /* and closing */ are aligned and the
	 520  	// rest of the comment text is aligned and indented with
	 521  	// blanks or tabs, cases with a vertical "line of stars"
	 522  	// on the left, and cases where the closing */ is on the
	 523  	// same line as the last comment text.
	 524  
	 525  	// Compute maximum common white prefix of all but the first,
	 526  	// last, and blank lines, and replace blank lines with empty
	 527  	// lines (the first line starts with /* and has no prefix).
	 528  	// In cases where only the first and last lines are not blank,
	 529  	// such as two-line comments, or comments where all inner lines
	 530  	// are blank, consider the last line for the prefix computation
	 531  	// since otherwise the prefix would be empty.
	 532  	//
	 533  	// Note that the first and last line are never empty (they
	 534  	// contain the opening /* and closing */ respectively) and
	 535  	// thus they can be ignored by the blank line check.
	 536  	prefix := ""
	 537  	prefixSet := false
	 538  	if len(lines) > 2 {
	 539  		for i, line := range lines[1 : len(lines)-1] {
	 540  			if isBlank(line) {
	 541  				lines[1+i] = "" // range starts with lines[1]
	 542  			} else {
	 543  				if !prefixSet {
	 544  					prefix = line
	 545  					prefixSet = true
	 546  				}
	 547  				prefix = commonPrefix(prefix, line)
	 548  			}
	 549  
	 550  		}
	 551  	}
	 552  	// If we don't have a prefix yet, consider the last line.
	 553  	if !prefixSet {
	 554  		line := lines[len(lines)-1]
	 555  		prefix = commonPrefix(line, line)
	 556  	}
	 557  
	 558  	/*
	 559  	 * Check for vertical "line of stars" and correct prefix accordingly.
	 560  	 */
	 561  	lineOfStars := false
	 562  	if i := strings.Index(prefix, "*"); i >= 0 {
	 563  		// Line of stars present.
	 564  		if i > 0 && prefix[i-1] == ' ' {
	 565  			i-- // remove trailing blank from prefix so stars remain aligned
	 566  		}
	 567  		prefix = prefix[0:i]
	 568  		lineOfStars = true
	 569  	} else {
	 570  		// No line of stars present.
	 571  		// Determine the white space on the first line after the /*
	 572  		// and before the beginning of the comment text, assume two
	 573  		// blanks instead of the /* unless the first character after
	 574  		// the /* is a tab. If the first comment line is empty but
	 575  		// for the opening /*, assume up to 3 blanks or a tab. This
	 576  		// whitespace may be found as suffix in the common prefix.
	 577  		first := lines[0]
	 578  		if isBlank(first[2:]) {
	 579  			// no comment text on the first line:
	 580  			// reduce prefix by up to 3 blanks or a tab
	 581  			// if present - this keeps comment text indented
	 582  			// relative to the /* and */'s if it was indented
	 583  			// in the first place
	 584  			i := len(prefix)
	 585  			for n := 0; n < 3 && i > 0 && prefix[i-1] == ' '; n++ {
	 586  				i--
	 587  			}
	 588  			if i == len(prefix) && i > 0 && prefix[i-1] == '\t' {
	 589  				i--
	 590  			}
	 591  			prefix = prefix[0:i]
	 592  		} else {
	 593  			// comment text on the first line
	 594  			suffix := make([]byte, len(first))
	 595  			n := 2 // start after opening /*
	 596  			for n < len(first) && first[n] <= ' ' {
	 597  				suffix[n] = first[n]
	 598  				n++
	 599  			}
	 600  			if n > 2 && suffix[2] == '\t' {
	 601  				// assume the '\t' compensates for the /*
	 602  				suffix = suffix[2:n]
	 603  			} else {
	 604  				// otherwise assume two blanks
	 605  				suffix[0], suffix[1] = ' ', ' '
	 606  				suffix = suffix[0:n]
	 607  			}
	 608  			// Shorten the computed common prefix by the length of
	 609  			// suffix, if it is found as suffix of the prefix.
	 610  			prefix = strings.TrimSuffix(prefix, string(suffix))
	 611  		}
	 612  	}
	 613  
	 614  	// Handle last line: If it only contains a closing */, align it
	 615  	// with the opening /*, otherwise align the text with the other
	 616  	// lines.
	 617  	last := lines[len(lines)-1]
	 618  	closing := "*/"
	 619  	i := strings.Index(last, closing) // i >= 0 (closing is always present)
	 620  	if isBlank(last[0:i]) {
	 621  		// last line only contains closing */
	 622  		if lineOfStars {
	 623  			closing = " */" // add blank to align final star
	 624  		}
	 625  		lines[len(lines)-1] = prefix + closing
	 626  	} else {
	 627  		// last line contains more comment text - assume
	 628  		// it is aligned like the other lines and include
	 629  		// in prefix computation
	 630  		prefix = commonPrefix(prefix, last)
	 631  	}
	 632  
	 633  	// Remove the common prefix from all but the first and empty lines.
	 634  	for i, line := range lines {
	 635  		if i > 0 && line != "" {
	 636  			lines[i] = line[len(prefix):]
	 637  		}
	 638  	}
	 639  }
	 640  
	 641  func (p *printer) writeComment(comment *ast.Comment) {
	 642  	text := comment.Text
	 643  	pos := p.posFor(comment.Pos())
	 644  
	 645  	const linePrefix = "//line "
	 646  	if strings.HasPrefix(text, linePrefix) && (!pos.IsValid() || pos.Column == 1) {
	 647  		// Possibly a //-style line directive.
	 648  		// Suspend indentation temporarily to keep line directive valid.
	 649  		defer func(indent int) { p.indent = indent }(p.indent)
	 650  		p.indent = 0
	 651  	}
	 652  
	 653  	// shortcut common case of //-style comments
	 654  	if text[1] == '/' {
	 655  		if constraint.IsGoBuild(text) {
	 656  			p.goBuild = append(p.goBuild, len(p.output))
	 657  		} else if constraint.IsPlusBuild(text) {
	 658  			p.plusBuild = append(p.plusBuild, len(p.output))
	 659  		}
	 660  		p.writeString(pos, trimRight(text), true)
	 661  		return
	 662  	}
	 663  
	 664  	// for /*-style comments, print line by line and let the
	 665  	// write function take care of the proper indentation
	 666  	lines := strings.Split(text, "\n")
	 667  
	 668  	// The comment started in the first column but is going
	 669  	// to be indented. For an idempotent result, add indentation
	 670  	// to all lines such that they look like they were indented
	 671  	// before - this will make sure the common prefix computation
	 672  	// is the same independent of how many times formatting is
	 673  	// applied (was issue 1835).
	 674  	if pos.IsValid() && pos.Column == 1 && p.indent > 0 {
	 675  		for i, line := range lines[1:] {
	 676  			lines[1+i] = "	 " + line
	 677  		}
	 678  	}
	 679  
	 680  	stripCommonPrefix(lines)
	 681  
	 682  	// write comment lines, separated by formfeed,
	 683  	// without a line break after the last line
	 684  	for i, line := range lines {
	 685  		if i > 0 {
	 686  			p.writeByte('\f', 1)
	 687  			pos = p.pos
	 688  		}
	 689  		if len(line) > 0 {
	 690  			p.writeString(pos, trimRight(line), true)
	 691  		}
	 692  	}
	 693  }
	 694  
	 695  // writeCommentSuffix writes a line break after a comment if indicated
	 696  // and processes any leftover indentation information. If a line break
	 697  // is needed, the kind of break (newline vs formfeed) depends on the
	 698  // pending whitespace. The writeCommentSuffix result indicates if a
	 699  // newline was written or if a formfeed was dropped from the whitespace
	 700  // buffer.
	 701  //
	 702  func (p *printer) writeCommentSuffix(needsLinebreak bool) (wroteNewline, droppedFF bool) {
	 703  	for i, ch := range p.wsbuf {
	 704  		switch ch {
	 705  		case blank, vtab:
	 706  			// ignore trailing whitespace
	 707  			p.wsbuf[i] = ignore
	 708  		case indent, unindent:
	 709  			// don't lose indentation information
	 710  		case newline, formfeed:
	 711  			// if we need a line break, keep exactly one
	 712  			// but remember if we dropped any formfeeds
	 713  			if needsLinebreak {
	 714  				needsLinebreak = false
	 715  				wroteNewline = true
	 716  			} else {
	 717  				if ch == formfeed {
	 718  					droppedFF = true
	 719  				}
	 720  				p.wsbuf[i] = ignore
	 721  			}
	 722  		}
	 723  	}
	 724  	p.writeWhitespace(len(p.wsbuf))
	 725  
	 726  	// make sure we have a line break
	 727  	if needsLinebreak {
	 728  		p.writeByte('\n', 1)
	 729  		wroteNewline = true
	 730  	}
	 731  
	 732  	return
	 733  }
	 734  
	 735  // containsLinebreak reports whether the whitespace buffer contains any line breaks.
	 736  func (p *printer) containsLinebreak() bool {
	 737  	for _, ch := range p.wsbuf {
	 738  		if ch == newline || ch == formfeed {
	 739  			return true
	 740  		}
	 741  	}
	 742  	return false
	 743  }
	 744  
	 745  // intersperseComments consumes all comments that appear before the next token
	 746  // tok and prints it together with the buffered whitespace (i.e., the whitespace
	 747  // that needs to be written before the next token). A heuristic is used to mix
	 748  // the comments and whitespace. The intersperseComments result indicates if a
	 749  // newline was written or if a formfeed was dropped from the whitespace buffer.
	 750  //
	 751  func (p *printer) intersperseComments(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) {
	 752  	var last *ast.Comment
	 753  	for p.commentBefore(next) {
	 754  		for _, c := range p.comment.List {
	 755  			p.writeCommentPrefix(p.posFor(c.Pos()), next, last, tok)
	 756  			p.writeComment(c)
	 757  			last = c
	 758  		}
	 759  		p.nextComment()
	 760  	}
	 761  
	 762  	if last != nil {
	 763  		// If the last comment is a /*-style comment and the next item
	 764  		// follows on the same line but is not a comma, and not a "closing"
	 765  		// token immediately following its corresponding "opening" token,
	 766  		// add an extra separator unless explicitly disabled. Use a blank
	 767  		// as separator unless we have pending linebreaks, they are not
	 768  		// disabled, and we are outside a composite literal, in which case
	 769  		// we want a linebreak (issue 15137).
	 770  		// TODO(gri) This has become overly complicated. We should be able
	 771  		// to track whether we're inside an expression or statement and
	 772  		// use that information to decide more directly.
	 773  		needsLinebreak := false
	 774  		if p.mode&noExtraBlank == 0 &&
	 775  			last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line &&
	 776  			tok != token.COMMA &&
	 777  			(tok != token.RPAREN || p.prevOpen == token.LPAREN) &&
	 778  			(tok != token.RBRACK || p.prevOpen == token.LBRACK) {
	 779  			if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 && p.level == 0 {
	 780  				needsLinebreak = true
	 781  			} else {
	 782  				p.writeByte(' ', 1)
	 783  			}
	 784  		}
	 785  		// Ensure that there is a line break after a //-style comment,
	 786  		// before EOF, and before a closing '}' unless explicitly disabled.
	 787  		if last.Text[1] == '/' ||
	 788  			tok == token.EOF ||
	 789  			tok == token.RBRACE && p.mode&noExtraLinebreak == 0 {
	 790  			needsLinebreak = true
	 791  		}
	 792  		return p.writeCommentSuffix(needsLinebreak)
	 793  	}
	 794  
	 795  	// no comment was written - we should never reach here since
	 796  	// intersperseComments should not be called in that case
	 797  	p.internalError("intersperseComments called without pending comments")
	 798  	return
	 799  }
	 800  
	 801  // whiteWhitespace writes the first n whitespace entries.
	 802  func (p *printer) writeWhitespace(n int) {
	 803  	// write entries
	 804  	for i := 0; i < n; i++ {
	 805  		switch ch := p.wsbuf[i]; ch {
	 806  		case ignore:
	 807  			// ignore!
	 808  		case indent:
	 809  			p.indent++
	 810  		case unindent:
	 811  			p.indent--
	 812  			if p.indent < 0 {
	 813  				p.internalError("negative indentation:", p.indent)
	 814  				p.indent = 0
	 815  			}
	 816  		case newline, formfeed:
	 817  			// A line break immediately followed by a "correcting"
	 818  			// unindent is swapped with the unindent - this permits
	 819  			// proper label positioning. If a comment is between
	 820  			// the line break and the label, the unindent is not
	 821  			// part of the comment whitespace prefix and the comment
	 822  			// will be positioned correctly indented.
	 823  			if i+1 < n && p.wsbuf[i+1] == unindent {
	 824  				// Use a formfeed to terminate the current section.
	 825  				// Otherwise, a long label name on the next line leading
	 826  				// to a wide column may increase the indentation column
	 827  				// of lines before the label; effectively leading to wrong
	 828  				// indentation.
	 829  				p.wsbuf[i], p.wsbuf[i+1] = unindent, formfeed
	 830  				i-- // do it again
	 831  				continue
	 832  			}
	 833  			fallthrough
	 834  		default:
	 835  			p.writeByte(byte(ch), 1)
	 836  		}
	 837  	}
	 838  
	 839  	// shift remaining entries down
	 840  	l := copy(p.wsbuf, p.wsbuf[n:])
	 841  	p.wsbuf = p.wsbuf[:l]
	 842  }
	 843  
	 844  // ----------------------------------------------------------------------------
	 845  // Printing interface
	 846  
	 847  // nlimit limits n to maxNewlines.
	 848  func nlimit(n int) int {
	 849  	if n > maxNewlines {
	 850  		n = maxNewlines
	 851  	}
	 852  	return n
	 853  }
	 854  
	 855  func mayCombine(prev token.Token, next byte) (b bool) {
	 856  	switch prev {
	 857  	case token.INT:
	 858  		b = next == '.' // 1.
	 859  	case token.ADD:
	 860  		b = next == '+' // ++
	 861  	case token.SUB:
	 862  		b = next == '-' // --
	 863  	case token.QUO:
	 864  		b = next == '*' // /*
	 865  	case token.LSS:
	 866  		b = next == '-' || next == '<' // <- or <<
	 867  	case token.AND:
	 868  		b = next == '&' || next == '^' // && or &^
	 869  	}
	 870  	return
	 871  }
	 872  
	 873  // print prints a list of "items" (roughly corresponding to syntactic
	 874  // tokens, but also including whitespace and formatting information).
	 875  // It is the only print function that should be called directly from
	 876  // any of the AST printing functions in nodes.go.
	 877  //
	 878  // Whitespace is accumulated until a non-whitespace token appears. Any
	 879  // comments that need to appear before that token are printed first,
	 880  // taking into account the amount and structure of any pending white-
	 881  // space for best comment placement. Then, any leftover whitespace is
	 882  // printed, followed by the actual token.
	 883  //
	 884  func (p *printer) print(args ...interface{}) {
	 885  	for _, arg := range args {
	 886  		// information about the current arg
	 887  		var data string
	 888  		var isLit bool
	 889  		var impliedSemi bool // value for p.impliedSemi after this arg
	 890  
	 891  		// record previous opening token, if any
	 892  		switch p.lastTok {
	 893  		case token.ILLEGAL:
	 894  			// ignore (white space)
	 895  		case token.LPAREN, token.LBRACK:
	 896  			p.prevOpen = p.lastTok
	 897  		default:
	 898  			// other tokens followed any opening token
	 899  			p.prevOpen = token.ILLEGAL
	 900  		}
	 901  
	 902  		switch x := arg.(type) {
	 903  		case pmode:
	 904  			// toggle printer mode
	 905  			p.mode ^= x
	 906  			continue
	 907  
	 908  		case whiteSpace:
	 909  			if x == ignore {
	 910  				// don't add ignore's to the buffer; they
	 911  				// may screw up "correcting" unindents (see
	 912  				// LabeledStmt)
	 913  				continue
	 914  			}
	 915  			i := len(p.wsbuf)
	 916  			if i == cap(p.wsbuf) {
	 917  				// Whitespace sequences are very short so this should
	 918  				// never happen. Handle gracefully (but possibly with
	 919  				// bad comment placement) if it does happen.
	 920  				p.writeWhitespace(i)
	 921  				i = 0
	 922  			}
	 923  			p.wsbuf = p.wsbuf[0 : i+1]
	 924  			p.wsbuf[i] = x
	 925  			if x == newline || x == formfeed {
	 926  				// newlines affect the current state (p.impliedSemi)
	 927  				// and not the state after printing arg (impliedSemi)
	 928  				// because comments can be interspersed before the arg
	 929  				// in this case
	 930  				p.impliedSemi = false
	 931  			}
	 932  			p.lastTok = token.ILLEGAL
	 933  			continue
	 934  
	 935  		case *ast.Ident:
	 936  			data = x.Name
	 937  			impliedSemi = true
	 938  			p.lastTok = token.IDENT
	 939  
	 940  		case *ast.BasicLit:
	 941  			data = x.Value
	 942  			isLit = true
	 943  			impliedSemi = true
	 944  			p.lastTok = x.Kind
	 945  
	 946  		case token.Token:
	 947  			s := x.String()
	 948  			if mayCombine(p.lastTok, s[0]) {
	 949  				// the previous and the current token must be
	 950  				// separated by a blank otherwise they combine
	 951  				// into a different incorrect token sequence
	 952  				// (except for token.INT followed by a '.' this
	 953  				// should never happen because it is taken care
	 954  				// of via binary expression formatting)
	 955  				if len(p.wsbuf) != 0 {
	 956  					p.internalError("whitespace buffer not empty")
	 957  				}
	 958  				p.wsbuf = p.wsbuf[0:1]
	 959  				p.wsbuf[0] = ' '
	 960  			}
	 961  			data = s
	 962  			// some keywords followed by a newline imply a semicolon
	 963  			switch x {
	 964  			case token.BREAK, token.CONTINUE, token.FALLTHROUGH, token.RETURN,
	 965  				token.INC, token.DEC, token.RPAREN, token.RBRACK, token.RBRACE:
	 966  				impliedSemi = true
	 967  			}
	 968  			p.lastTok = x
	 969  
	 970  		case token.Pos:
	 971  			if x.IsValid() {
	 972  				p.pos = p.posFor(x) // accurate position of next item
	 973  			}
	 974  			continue
	 975  
	 976  		case string:
	 977  			// incorrect AST - print error message
	 978  			data = x
	 979  			isLit = true
	 980  			impliedSemi = true
	 981  			p.lastTok = token.STRING
	 982  
	 983  		default:
	 984  			fmt.Fprintf(os.Stderr, "print: unsupported argument %v (%T)\n", arg, arg)
	 985  			panic("go/printer type")
	 986  		}
	 987  		// data != ""
	 988  
	 989  		next := p.pos // estimated/accurate position of next item
	 990  		wroteNewline, droppedFF := p.flush(next, p.lastTok)
	 991  
	 992  		// intersperse extra newlines if present in the source and
	 993  		// if they don't cause extra semicolons (don't do this in
	 994  		// flush as it will cause extra newlines at the end of a file)
	 995  		if !p.impliedSemi {
	 996  			n := nlimit(next.Line - p.pos.Line)
	 997  			// don't exceed maxNewlines if we already wrote one
	 998  			if wroteNewline && n == maxNewlines {
	 999  				n = maxNewlines - 1
	1000  			}
	1001  			if n > 0 {
	1002  				ch := byte('\n')
	1003  				if droppedFF {
	1004  					ch = '\f' // use formfeed since we dropped one before
	1005  				}
	1006  				p.writeByte(ch, n)
	1007  				impliedSemi = false
	1008  			}
	1009  		}
	1010  
	1011  		// the next token starts now - record its line number if requested
	1012  		if p.linePtr != nil {
	1013  			*p.linePtr = p.out.Line
	1014  			p.linePtr = nil
	1015  		}
	1016  
	1017  		p.writeString(next, data, isLit)
	1018  		p.impliedSemi = impliedSemi
	1019  	}
	1020  }
	1021  
	1022  // flush prints any pending comments and whitespace occurring textually
	1023  // before the position of the next token tok. The flush result indicates
	1024  // if a newline was written or if a formfeed was dropped from the whitespace
	1025  // buffer.
	1026  //
	1027  func (p *printer) flush(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) {
	1028  	if p.commentBefore(next) {
	1029  		// if there are comments before the next item, intersperse them
	1030  		wroteNewline, droppedFF = p.intersperseComments(next, tok)
	1031  	} else {
	1032  		// otherwise, write any leftover whitespace
	1033  		p.writeWhitespace(len(p.wsbuf))
	1034  	}
	1035  	return
	1036  }
	1037  
	1038  // getNode returns the ast.CommentGroup associated with n, if any.
	1039  func getDoc(n ast.Node) *ast.CommentGroup {
	1040  	switch n := n.(type) {
	1041  	case *ast.Field:
	1042  		return n.Doc
	1043  	case *ast.ImportSpec:
	1044  		return n.Doc
	1045  	case *ast.ValueSpec:
	1046  		return n.Doc
	1047  	case *ast.TypeSpec:
	1048  		return n.Doc
	1049  	case *ast.GenDecl:
	1050  		return n.Doc
	1051  	case *ast.FuncDecl:
	1052  		return n.Doc
	1053  	case *ast.File:
	1054  		return n.Doc
	1055  	}
	1056  	return nil
	1057  }
	1058  
	1059  func getLastComment(n ast.Node) *ast.CommentGroup {
	1060  	switch n := n.(type) {
	1061  	case *ast.Field:
	1062  		return n.Comment
	1063  	case *ast.ImportSpec:
	1064  		return n.Comment
	1065  	case *ast.ValueSpec:
	1066  		return n.Comment
	1067  	case *ast.TypeSpec:
	1068  		return n.Comment
	1069  	case *ast.GenDecl:
	1070  		if len(n.Specs) > 0 {
	1071  			return getLastComment(n.Specs[len(n.Specs)-1])
	1072  		}
	1073  	case *ast.File:
	1074  		if len(n.Comments) > 0 {
	1075  			return n.Comments[len(n.Comments)-1]
	1076  		}
	1077  	}
	1078  	return nil
	1079  }
	1080  
	1081  func (p *printer) printNode(node interface{}) error {
	1082  	// unpack *CommentedNode, if any
	1083  	var comments []*ast.CommentGroup
	1084  	if cnode, ok := node.(*CommentedNode); ok {
	1085  		node = cnode.Node
	1086  		comments = cnode.Comments
	1087  	}
	1088  
	1089  	if comments != nil {
	1090  		// commented node - restrict comment list to relevant range
	1091  		n, ok := node.(ast.Node)
	1092  		if !ok {
	1093  			goto unsupported
	1094  		}
	1095  		beg := n.Pos()
	1096  		end := n.End()
	1097  		// if the node has associated documentation,
	1098  		// include that commentgroup in the range
	1099  		// (the comment list is sorted in the order
	1100  		// of the comment appearance in the source code)
	1101  		if doc := getDoc(n); doc != nil {
	1102  			beg = doc.Pos()
	1103  		}
	1104  		if com := getLastComment(n); com != nil {
	1105  			if e := com.End(); e > end {
	1106  				end = e
	1107  			}
	1108  		}
	1109  		// token.Pos values are global offsets, we can
	1110  		// compare them directly
	1111  		i := 0
	1112  		for i < len(comments) && comments[i].End() < beg {
	1113  			i++
	1114  		}
	1115  		j := i
	1116  		for j < len(comments) && comments[j].Pos() < end {
	1117  			j++
	1118  		}
	1119  		if i < j {
	1120  			p.comments = comments[i:j]
	1121  		}
	1122  	} else if n, ok := node.(*ast.File); ok {
	1123  		// use ast.File comments, if any
	1124  		p.comments = n.Comments
	1125  	}
	1126  
	1127  	// if there are no comments, use node comments
	1128  	p.useNodeComments = p.comments == nil
	1129  
	1130  	// get comments ready for use
	1131  	p.nextComment()
	1132  
	1133  	p.print(pmode(0))
	1134  
	1135  	// format node
	1136  	switch n := node.(type) {
	1137  	case ast.Expr:
	1138  		p.expr(n)
	1139  	case ast.Stmt:
	1140  		// A labeled statement will un-indent to position the label.
	1141  		// Set p.indent to 1 so we don't get indent "underflow".
	1142  		if _, ok := n.(*ast.LabeledStmt); ok {
	1143  			p.indent = 1
	1144  		}
	1145  		p.stmt(n, false)
	1146  	case ast.Decl:
	1147  		p.decl(n)
	1148  	case ast.Spec:
	1149  		p.spec(n, 1, false)
	1150  	case []ast.Stmt:
	1151  		// A labeled statement will un-indent to position the label.
	1152  		// Set p.indent to 1 so we don't get indent "underflow".
	1153  		for _, s := range n {
	1154  			if _, ok := s.(*ast.LabeledStmt); ok {
	1155  				p.indent = 1
	1156  			}
	1157  		}
	1158  		p.stmtList(n, 0, false)
	1159  	case []ast.Decl:
	1160  		p.declList(n)
	1161  	case *ast.File:
	1162  		p.file(n)
	1163  	default:
	1164  		goto unsupported
	1165  	}
	1166  
	1167  	return nil
	1168  
	1169  unsupported:
	1170  	return fmt.Errorf("go/printer: unsupported node type %T", node)
	1171  }
	1172  
	1173  // ----------------------------------------------------------------------------
	1174  // Trimmer
	1175  
	1176  // A trimmer is an io.Writer filter for stripping tabwriter.Escape
	1177  // characters, trailing blanks and tabs, and for converting formfeed
	1178  // and vtab characters into newlines and htabs (in case no tabwriter
	1179  // is used). Text bracketed by tabwriter.Escape characters is passed
	1180  // through unchanged.
	1181  //
	1182  type trimmer struct {
	1183  	output io.Writer
	1184  	state	int
	1185  	space	[]byte
	1186  }
	1187  
	1188  // trimmer is implemented as a state machine.
	1189  // It can be in one of the following states:
	1190  const (
	1191  	inSpace	= iota // inside space
	1192  	inEscape				// inside text bracketed by tabwriter.Escapes
	1193  	inText					// inside text
	1194  )
	1195  
	1196  func (p *trimmer) resetSpace() {
	1197  	p.state = inSpace
	1198  	p.space = p.space[0:0]
	1199  }
	1200  
	1201  // Design note: It is tempting to eliminate extra blanks occurring in
	1202  //							whitespace in this function as it could simplify some
	1203  //							of the blanks logic in the node printing functions.
	1204  //							However, this would mess up any formatting done by
	1205  //							the tabwriter.
	1206  
	1207  var aNewline = []byte("\n")
	1208  
	1209  func (p *trimmer) Write(data []byte) (n int, err error) {
	1210  	// invariants:
	1211  	// p.state == inSpace:
	1212  	//	p.space is unwritten
	1213  	// p.state == inEscape, inText:
	1214  	//	data[m:n] is unwritten
	1215  	m := 0
	1216  	var b byte
	1217  	for n, b = range data {
	1218  		if b == '\v' {
	1219  			b = '\t' // convert to htab
	1220  		}
	1221  		switch p.state {
	1222  		case inSpace:
	1223  			switch b {
	1224  			case '\t', ' ':
	1225  				p.space = append(p.space, b)
	1226  			case '\n', '\f':
	1227  				p.resetSpace() // discard trailing space
	1228  				_, err = p.output.Write(aNewline)
	1229  			case tabwriter.Escape:
	1230  				_, err = p.output.Write(p.space)
	1231  				p.state = inEscape
	1232  				m = n + 1 // +1: skip tabwriter.Escape
	1233  			default:
	1234  				_, err = p.output.Write(p.space)
	1235  				p.state = inText
	1236  				m = n
	1237  			}
	1238  		case inEscape:
	1239  			if b == tabwriter.Escape {
	1240  				_, err = p.output.Write(data[m:n])
	1241  				p.resetSpace()
	1242  			}
	1243  		case inText:
	1244  			switch b {
	1245  			case '\t', ' ':
	1246  				_, err = p.output.Write(data[m:n])
	1247  				p.resetSpace()
	1248  				p.space = append(p.space, b)
	1249  			case '\n', '\f':
	1250  				_, err = p.output.Write(data[m:n])
	1251  				p.resetSpace()
	1252  				if err == nil {
	1253  					_, err = p.output.Write(aNewline)
	1254  				}
	1255  			case tabwriter.Escape:
	1256  				_, err = p.output.Write(data[m:n])
	1257  				p.state = inEscape
	1258  				m = n + 1 // +1: skip tabwriter.Escape
	1259  			}
	1260  		default:
	1261  			panic("unreachable")
	1262  		}
	1263  		if err != nil {
	1264  			return
	1265  		}
	1266  	}
	1267  	n = len(data)
	1268  
	1269  	switch p.state {
	1270  	case inEscape, inText:
	1271  		_, err = p.output.Write(data[m:n])
	1272  		p.resetSpace()
	1273  	}
	1274  
	1275  	return
	1276  }
	1277  
	1278  // ----------------------------------------------------------------------------
	1279  // Public interface
	1280  
	1281  // A Mode value is a set of flags (or 0). They control printing.
	1282  type Mode uint
	1283  
	1284  const (
	1285  	RawFormat Mode = 1 << iota // do not use a tabwriter; if set, UseSpaces is ignored
	1286  	TabIndent									// use tabs for indentation independent of UseSpaces
	1287  	UseSpaces									// use spaces instead of tabs for alignment
	1288  	SourcePos									// emit //line directives to preserve original source positions
	1289  )
	1290  
	1291  // The mode below is not included in printer's public API because
	1292  // editing code text is deemed out of scope. Because this mode is
	1293  // unexported, it's also possible to modify or remove it based on
	1294  // the evolving needs of go/format and cmd/gofmt without breaking
	1295  // users. See discussion in CL 240683.
	1296  const (
	1297  	// normalizeNumbers means to canonicalize number
	1298  	// literal prefixes and exponents while printing.
	1299  	//
	1300  	// This value is known in and used by go/format and cmd/gofmt.
	1301  	// It is currently more convenient and performant for those
	1302  	// packages to apply number normalization during printing,
	1303  	// rather than by modifying the AST in advance.
	1304  	normalizeNumbers Mode = 1 << 30
	1305  )
	1306  
	1307  // A Config node controls the output of Fprint.
	1308  type Config struct {
	1309  	Mode		 Mode // default: 0
	1310  	Tabwidth int	// default: 8
	1311  	Indent	 int	// default: 0 (all code is indented at least by this much)
	1312  }
	1313  
	1314  // fprint implements Fprint and takes a nodesSizes map for setting up the printer state.
	1315  func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (err error) {
	1316  	// print node
	1317  	var p printer
	1318  	p.init(cfg, fset, nodeSizes)
	1319  	if err = p.printNode(node); err != nil {
	1320  		return
	1321  	}
	1322  	// print outstanding comments
	1323  	p.impliedSemi = false // EOF acts like a newline
	1324  	p.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF)
	1325  
	1326  	// output is buffered in p.output now.
	1327  	// fix //go:build and // +build comments if needed.
	1328  	p.fixGoBuildLines()
	1329  
	1330  	// redirect output through a trimmer to eliminate trailing whitespace
	1331  	// (Input to a tabwriter must be untrimmed since trailing tabs provide
	1332  	// formatting information. The tabwriter could provide trimming
	1333  	// functionality but no tabwriter is used when RawFormat is set.)
	1334  	output = &trimmer{output: output}
	1335  
	1336  	// redirect output through a tabwriter if necessary
	1337  	if cfg.Mode&RawFormat == 0 {
	1338  		minwidth := cfg.Tabwidth
	1339  
	1340  		padchar := byte('\t')
	1341  		if cfg.Mode&UseSpaces != 0 {
	1342  			padchar = ' '
	1343  		}
	1344  
	1345  		twmode := tabwriter.DiscardEmptyColumns
	1346  		if cfg.Mode&TabIndent != 0 {
	1347  			minwidth = 0
	1348  			twmode |= tabwriter.TabIndent
	1349  		}
	1350  
	1351  		output = tabwriter.NewWriter(output, minwidth, cfg.Tabwidth, 1, padchar, twmode)
	1352  	}
	1353  
	1354  	// write printer result via tabwriter/trimmer to output
	1355  	if _, err = output.Write(p.output); err != nil {
	1356  		return
	1357  	}
	1358  
	1359  	// flush tabwriter, if any
	1360  	if tw, _ := output.(*tabwriter.Writer); tw != nil {
	1361  		err = tw.Flush()
	1362  	}
	1363  
	1364  	return
	1365  }
	1366  
	1367  // A CommentedNode bundles an AST node and corresponding comments.
	1368  // It may be provided as argument to any of the Fprint functions.
	1369  //
	1370  type CommentedNode struct {
	1371  	Node		 interface{} // *ast.File, or ast.Expr, ast.Decl, ast.Spec, or ast.Stmt
	1372  	Comments []*ast.CommentGroup
	1373  }
	1374  
	1375  // Fprint "pretty-prints" an AST node to output for a given configuration cfg.
	1376  // Position information is interpreted relative to the file set fset.
	1377  // The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt,
	1378  // or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.
	1379  //
	1380  func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
	1381  	return cfg.fprint(output, fset, node, make(map[ast.Node]int))
	1382  }
	1383  
	1384  // Fprint "pretty-prints" an AST node to output.
	1385  // It calls Config.Fprint with default settings.
	1386  // Note that gofmt uses tabs for indentation but spaces for alignment;
	1387  // use format.Node (package go/format) for output that matches gofmt.
	1388  //
	1389  func Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
	1390  	return (&Config{Tabwidth: 8}).Fprint(output, fset, node)
	1391  }
	1392  

View as plain text