Involved Source Filesgobuild.gonodes.go
Package printer implements printing of AST nodes.
Code Examples
package main
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"strings"
)
func parseFunc(filename, functionname string) (fun *ast.FuncDecl, fset *token.FileSet) {
fset = token.NewFileSet()
if file, err := parser.ParseFile(fset, filename, nil, 0); err == nil {
for _, d := range file.Decls {
if f, ok := d.(*ast.FuncDecl); ok && f.Name.Name == functionname {
fun = f
return
}
}
}
panic("function not found")
}
func main() {
// Parse source file and extract the AST without comments for
// this function, with position information referring to the
// file set fset.
funcAST, fset := parseFunc("example_test.go", "ExampleFprint")
// Print the function body into buffer buf.
// The file set is provided to the printer so that it knows
// about the original source formatting and can add additional
// line breaks where they were present in the source.
var buf bytes.Buffer
printer.Fprint(&buf, fset, funcAST.Body)
// Remove braces {} enclosing the function body, unindent,
// and trim leading and trailing white space.
s := buf.String()
s = s[1 : len(s)-1]
s = strings.TrimSpace(strings.ReplaceAll(s, "\n\t", "\n"))
// Print the cleaned-up body text to stdout.
fmt.Println(s)
}
Package-Level Type Names (total 10, in which 3 are exported)
/* sort exporteds by: | */
A CommentedNode bundles an AST node and corresponding comments.
It may be provided as argument to any of the Fprint functions.
Comments[]*ast.CommentGroup
// *ast.File, or ast.Expr, ast.Decl, ast.Spec, or ast.Stmt
A Config node controls the output of Fprint.
// default: 0 (all code is indented at least by this much)
// default: 0
// default: 8
Fprint "pretty-prints" an AST node to output for a given configuration cfg.
Position information is interpreted relative to the file set fset.
The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt,
or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.
fprint implements Fprint and takes a nodesSizes map for setting up the printer state.
func go/format.format(fset *token.FileSet, file *ast.File, sourceAdj func(src []byte, indent int) []byte, indentAdj int, src []byte, cfg Config) ([]byte, error)
var go/format.config
// current comment index
// = printer.comments[cindex]; or nil
// true if the comment group contains newlines
// = printer.posFor(printer.comments[cindex].List[0].Pos()).Offset; or infinity
Configuration (does not change after initialization)
// default: 0 (all code is indented at least by this much)
// default: 0
// default: 8
// line corresponding to cachedPos
Cache of most recently computed line position.
Information about p.comments[p.cindex]; set up by nextComment.
// current comment index
// = printer.comments[cindex]; or nil
// true if the comment group contains newlines
// = printer.posFor(printer.comments[cindex].List[0].Pos()).Offset; or infinity
The list of all source comments, in order of appearance.
// may be nil
// if set, terminate alignment immediately
fset*token.FileSet
// start index of all //go:build comments in output
// if set, a linebreak implies a semicolon
// current indentation
// value of pos after calling writeString
// last token printed (token.ILLEGAL if it's whitespace)
// level == 0: outside composite literal; level > 0: inside composite literal
// if set, record out.Line for the next token in *linePtr
// current printer mode
Cache of already computed node sizes.
// current position in output space
Current state
// raw printer result
// start index of all // +build comments in output
Positions
The out position differs from the pos position when the result
formatting differs from the source formatting (in the amount of
white space). If there's a difference and SourcePos is set in
ConfigMode, //line directives are used in the output to restore
original source positions for a reader.
// current position in AST (source) space
// previous non-brace "open" token (, [, or token.ILLEGAL
// if not set, ignore lead and line comments of nodes
// delayed white space
Fprint "pretty-prints" an AST node to output for a given configuration cfg.
Position information is interpreted relative to the file set fset.
The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt,
or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.
Format the binary expression: decide the cutoff and then format.
Let's call depth == 1 Normal mode, and depth > 1 Compact mode.
(Algorithm suggestion by Russ Cox.)
The precedences are:
5 * / % << >> & &^
4 + - | ^
3 == != < <= > >=
2 &&
1 ||
The only decision is whether there will be spaces around levels 4 and 5.
There are never spaces at level 6 (unary), and always spaces at levels 3 and below.
To choose the cutoff, look at the whole expression but excluding primary
expressions (function calls, parenthesized exprs), and apply these rules:
1) If there is a binary operator with a right side unary operand
that would clash without a space, the cutoff must be (in order):
/* 6
&& 6
&^ 6
++ 5
-- 5
(Comparison operators always have spaces around them.)
2) If there is a mix of level 5 and level 4 operators, then the cutoff
is 5 (use spaces to distinguish precedence) in Normal mode
and 4 (never use spaces) in Compact mode.
3) If there are no level 4 operators or no level 5 operators, then the
cutoff is 6 (always use spaces) in Normal mode
and 4 (never use spaces) in Compact mode.
block prints an *ast.BlockStmt; it always spans at least two lines.
bodySize is like nodeSize but it is specialized for *ast.BlockStmt's.
commentBefore reports whether the current comment group occurs
before the next position in the source code and printing it does
not introduce implicit semicolons.
commentSizeBefore returns the estimated size of the
comments on the same line before the next position.
(*printer) commentTextAt(start int) string
commentsHaveNewline reports whether a list of comments belonging to
an *ast.CommentGroup contains newlines. Because the position information
may only be partially correct, we also have to read the comment text.
containsLinebreak reports whether the whitespace buffer contains any line breaks.
(*printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt)(*printer) decl(decl ast.Decl)(*printer) declList(list []ast.Decl)
distanceFrom returns the column difference between p.out (the current output
position) and startOutCol. If the start position is on a different line from
the current position (or either is unknown), the result is infinity.
(*printer) expr(x ast.Expr)(*printer) expr0(x ast.Expr, depth int)(*printer) expr1(expr ast.Expr, prec1, depth int)
Print a list of expressions. If the list spans multiple
source lines, the original line breaks are respected between
expressions.
TODO(gri) Consider rewriting this to be independent of []ast.Expr
so that we can use the algorithm for any kind of list
(e.g., pass list via a channel over which to range).
(*printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)(*printer) file(src *ast.File)(*printer) fixGoBuildLines()
flush prints any pending comments and whitespace occurring textually
before the position of the next token tok. The flush result indicates
if a newline was written or if a formfeed was dropped from the whitespace
buffer.
fprint implements Fprint and takes a nodesSizes map for setting up the printer state.
funcBody prints a function body following a function header of given headerSize.
If the header's and block's size are "small enough" and the block is "simple enough",
the block is printed on the current line, without line breaks, spaced from the header
by sep. Otherwise the block's opening "{" is printed on the current line, followed by
lines for the block's statements and its closing "}".
(*printer) funcDecl(d *ast.FuncDecl)(*printer) genDecl(d *ast.GenDecl)
If indent is set, a multi-line identifier list is indented after the
first linebreak encountered.
indentList reports whether an expression list would look better if it
were indented wholesale (starting with the very first element, rather
than starting at the first line break).
(*printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int)(*printer) internalError(msg ...any)
intersperseComments consumes all comments that appear before the next token
tok 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. The intersperseComments result indicates if a
newline was written or if a formfeed was dropped from the whitespace buffer.
(*printer) isOneLineFieldList(list []*ast.Field) bool(*printer) lineAt(start int) []byte(*printer) lineFor(pos token.Pos) int
Print as many newlines as necessary (but at least min newlines) to get to
the current line. ws is printed before the first line break. If newSection
is set, the first line break is printed as formfeed. Returns 0 if no line
breaks were printed, returns 1 if there was exactly one newline printed,
and returns a value > 1 if there was a formfeed or more than one newline
printed.
TODO(gri): linebreak may add too many lines if the next statement at "line"
is preceded by comments because the computation of n assumes
the current position before the comment and the target position
after the comment. Thus, after interspersing such comments, the
space taken up by them is not considered to reduce the number of
linebreaks. At the moment there is no easy way to know about
future (not yet interspersed) comments in this function.
linesFrom returns the number of output lines between the current
output line and the line argument, ignoring any pending (not yet
emitted) whitespace or comments. It is used to compute an accurate
size (in number of lines) for a formatted construct.
(*printer) nextComment()
nodeSize determines the size of n in chars after formatting.
The result is <= maxSize if the node fits on one line with at
most maxSize chars and the formatted output doesn't contain
any control chars. Otherwise, the result is > maxSize.
numLines returns the number of lines spanned by node n in the original source.
(*printer) parameters(fields *ast.FieldList, mode paramMode)(*printer) posFor(pos token.Pos) token.Position(*printer) possibleSelectorExpr(expr ast.Expr, prec1, depth int) bool
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
any of the AST printing functions in nodes.go.
Whitespace is accumulated until a non-whitespace token appears. Any
comments that need to appear before that token are printed first,
taking into account the amount and structure of any pending white-
space for best comment placement. Then, any leftover whitespace is
printed, followed by the actual token.
(*printer) printNode(node any) error
recordLine records the output line number for the next non-whitespace
token in *linePtr. It is used to compute an accurate line number for a
formatted construct, independent of pending (not yet emitted) whitespace
or comments.
selectorExpr handles an *ast.SelectorExpr node and reports whether x spans
multiple lines.
setComment sets g as the next comment if g != nil and if node comments
are enabled - this mode is used when printing source code fragments such
as exports only. It assumes that there is no pending comment in p.comments
and at most one pending comment in the p.comment cache.
(*printer) setLineComment(text string)(*printer) signature(sig *ast.FuncType)
The parameter n is the number of specs in the group. If doIndent is set,
multi-line identifier lists in the spec are indented when the first
linebreak is encountered.
(*printer) stmt(stmt ast.Stmt, nextIsRBrace bool)
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.
(*printer) valueSpec(s *ast.ValueSpec, keepType bool)
writeByte writes ch n times to p.output and updates p.pos.
Only used to write formatting (white space) characters.
(*printer) writeComment(comment *ast.Comment)
writeCommentPrefix writes the whitespace before a comment.
If there is any pending whitespace, it consumes as much of
it as is likely to help position the comment nicely.
pos is the comment position, next the position of the item
after all pending comments, prev is the previous comment in
a group of comments (or nil), and tok is the next token.
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. The writeCommentSuffix result indicates if a
newline was written or if a formfeed was dropped from the whitespace
buffer.
writeIndent writes indentation.
writeLineDirective writes a //line directive if necessary.
writeString writes the string s to p.output and updates p.pos, p.out,
and p.last. If isLit is set, s is escaped w/ tabwriter.Escape characters
to protect s from being interpreted by the tabwriter.
Note: writeString is only used to write Go tokens, literals, and
comments, all of which must be written literally. Thus, it is correct
to always set isLit = true. However, setting it explicitly only when
needed (i.e., when we don't know that s contains no tabs or line breaks)
avoids processing extra escape characters and reduces run time of the
printer benchmark by up to 10%.
whiteWhitespace writes the first n whitespace entries.
A trimmer is an io.Writer filter for stripping tabwriter.Escape
characters, trailing blanks and tabs, and for converting formfeed
and vtab characters into newlines and htabs (in case no tabwriter
is used). Text bracketed by tabwriter.Escape characters is passed
through unchanged.
outputio.Writerspace[]bytestateint(*trimmer) Write(data []byte) (n int, err error)(*trimmer) resetSpace()
*trimmer : io.Writer
Package-Level Functions (total 25, in which 1 are exported)
Fprint "pretty-prints" an AST node to output.
It calls Config.Fprint with default settings.
Note that gofmt uses tabs for indentation but spaces for alignment;
use format.Node (package go/format) for output that matches gofmt.
appendLines is like append(x, y...)
but it avoids creating doubled blank lines,
which would not be gofmt-standard output.
It assumes that only whole blocks of lines are being appended,
not line fragments.
commonPrefix returns the common prefix of a and b.
The keepTypeColumn function determines if the type column of a series of
consecutive const or var declarations must be kept, or if initialization
values (V) can be placed in the type column (T) instead. The i'th entry
in the result slice is true if the type column in spec[i] must be kept.
For example, the declaration:
const (
foobar int = 42 // comment
x = 7 // comment
foo
bar = 991
)
leads to the type/values matrix below. A run of value columns (V) can
be moved into the type column if there is no type for any of the values
in that column (we only move entire columns so that they align properly).
matrix formatted result
matrix
T V -> T V -> true there is a T and so the type
- V - V true column must be kept
- - - - false
- V V - false V is moved into T column
normalizedNumber rewrites base prefixes and exponents
of numbers to use lower-case letters (0X123 to 0x123 and 1.2E3 to 1.2e3),
and removes leading 0's from integer imaginary literals (0765i to 765i).
It leaves hexadecimal digits alone.
normalizedNumber doesn't modify the ast.BasicLit value lit points to.
If lit is not a number or a number in canonical format already,
lit is returned as is. Otherwise a new ast.BasicLit is created.
stripCommonPrefix removes a common prefix from /*-style comment lines (unless no
comment line is indented, all but the first line have some form of space prefix).
The prefix is computed using heuristics such that is likely that the comment
contents are nicely laid out after re-printing each line using the printer's
current indentation.
normalizeNumbers means to canonicalize number
literal prefixes and exponents while printing.
This value is known in and used by go/format and cmd/gofmt.
It is currently more convenient and performant for those
packages to apply number normalization during printing,
rather than by modifying the AST in advance.