1 // Copyright 2012 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.
5 // Package format implements standard formatting of Go source.
7 // Note that formatting of Go source code changes over time, so tools relying on
8 // consistent formatting should execute a specific version of the gofmt binary
9 // instead of using this package. That way, the formatting will be stable, and
10 // the tools won't need to be recompiled each time gofmt changes.
12 // For example, pre-submit checks that use this package directly would behave
13 // differently depending on what Go version each developer uses, causing the
14 // check to be inherently fragile.
27 var config
= printer
.Config
{Mode
: printer
.UseSpaces | printer
.TabIndent
, Tabwidth
: 8}
29 const parserMode
= parser
.ParseComments
31 // Node formats node in canonical gofmt style and writes the result to dst.
33 // The node type must be *ast.File, *printer.CommentedNode, []ast.Decl,
34 // []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec,
35 // or ast.Stmt. Node does not modify node. Imports are not sorted for
36 // nodes representing partial source files (for instance, if the node is
37 // not an *ast.File or a *printer.CommentedNode not wrapping an *ast.File).
39 // The function may return early (before the entire result is written)
40 // and return a formatting error, for instance due to an incorrect AST.
42 func Node(dst io
.Writer
, fset
*token
.FileSet
, node
interface{}) error
{
43 // Determine if we have a complete source file (file != nil).
45 var cnode
*printer
.CommentedNode
46 switch n
:= node
.(type) {
49 case *printer
.CommentedNode
:
50 if f
, ok
:= n
.Node
.(*ast
.File
); ok
{
56 // Sort imports if necessary.
57 if file
!= nil && hasUnsortedImports(file
) {
58 // Make a copy of the AST because ast.SortImports is destructive.
59 // TODO(gri) Do this more efficiently.
61 err
:= config
.Fprint(&buf
, fset
, file
)
65 file
, err
= parser
.ParseFile(fset
, "", buf
.Bytes(), parserMode
)
67 // We should never get here. If we do, provide good diagnostic.
68 return fmt
.Errorf("format.Node internal error (%s)", err
)
70 ast
.SortImports(fset
, file
)
72 // Use new file with sorted imports.
75 node
= &printer
.CommentedNode
{Node
: file
, Comments
: cnode
.Comments
}
79 return config
.Fprint(dst
, fset
, node
)
82 // Source formats src in canonical gofmt style and returns the result
83 // or an (I/O or syntax) error. src is expected to be a syntactically
84 // correct Go source file, or a list of Go declarations or statements.
86 // If src is a partial source file, the leading and trailing space of src
87 // is applied to the result (such that it has the same leading and trailing
88 // space as src), and the result is indented by the same amount as the first
89 // line of src containing code. Imports are not sorted for partial source files.
91 func Source(src
[]byte) ([]byte, error
) {
92 fset
:= token
.NewFileSet()
93 file
, sourceAdj
, indentAdj
, err
:= parse(fset
, "", src
, true)
99 // Complete source file.
100 // TODO(gri) consider doing this always.
101 ast
.SortImports(fset
, file
)
104 return format(fset
, file
, sourceAdj
, indentAdj
, src
, config
)
107 func hasUnsortedImports(file
*ast
.File
) bool {
108 for _
, d
:= range file
.Decls
{
109 d
, ok
:= d
.(*ast
.GenDecl
)
110 if !ok || d
.Tok
!= token
.IMPORT
{
111 // Not an import declaration, so we're done.
112 // Imports are always first.
115 if d
.Lparen
.IsValid() {
116 // For now assume all grouped imports are unsorted.
117 // TODO(gri) Should check if they are sorted already.
120 // Ungrouped imports are sorted by default.