// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package format implements standard formatting of Go source. // // Note that formatting of Go source code changes over time, so tools relying on // consistent formatting should execute a specific version of the gofmt binary // instead of using this package. That way, the formatting will be stable, and // the tools won't need to be recompiled each time gofmt changes. // // For example, pre-submit checks that use this package directly would behave // differently depending on what Go version each developer uses, causing the // check to be inherently fragile.
package format import ( ) // Keep these in sync with cmd/gofmt/gofmt.go. const ( tabWidth = 8 printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers // printerNormalizeNumbers means to canonicalize number literal prefixes // and exponents while printing. See https://golang.org/doc/go1.13#gofmt. // // This value is defined in go/printer specifically for go/format and cmd/gofmt. printerNormalizeNumbers = 1 << 30 ) var config = printer.Config{Mode: printerMode, Tabwidth: tabWidth} const parserMode = parser.ParseComments | parser.SkipObjectResolution // Node formats node in canonical gofmt style and writes the result to dst. // // The node type must be *[ast.File], *[printer.CommentedNode], [][ast.Decl], // [][ast.Stmt], or assignment-compatible to [ast.Expr], [ast.Decl], [ast.Spec], // or [ast.Stmt]. Node does not modify node. Imports are not sorted for // nodes representing partial source files (for instance, if the node is // not an *[ast.File] or a *[printer.CommentedNode] not wrapping an *[ast.File]). // // The function may return early (before the entire result is written) // and return a formatting error, for instance due to an incorrect AST. func ( io.Writer, *token.FileSet, any) error { // Determine if we have a complete source file (file != nil). var *ast.File var *printer.CommentedNode switch n := .(type) { case *ast.File: = case *printer.CommentedNode: if , := .Node.(*ast.File); { = = } } // Sort imports if necessary. if != nil && hasUnsortedImports() { // Make a copy of the AST because ast.SortImports is destructive. // TODO(gri) Do this more efficiently. var bytes.Buffer := config.Fprint(&, , ) if != nil { return } , = parser.ParseFile(, "", .Bytes(), parserMode) if != nil { // We should never get here. If we do, provide good diagnostic. return fmt.Errorf("format.Node internal error (%s)", ) } ast.SortImports(, ) // Use new file with sorted imports. = if != nil { = &printer.CommentedNode{Node: , Comments: .Comments} } } return config.Fprint(, , ) } // Source formats src in canonical gofmt style and returns the result // or an (I/O or syntax) error. src is expected to be a syntactically // correct Go source file, or a list of Go declarations or statements. // // If src is a partial source file, the leading and trailing space of src // is applied to the result (such that it has the same leading and trailing // space as src), and the result is indented by the same amount as the first // line of src containing code. Imports are not sorted for partial source files. func ( []byte) ([]byte, error) { := token.NewFileSet() , , , := parse(, "", , true) if != nil { return nil, } if == nil { // Complete source file. // TODO(gri) consider doing this always. ast.SortImports(, ) } return format(, , , , , config) } func hasUnsortedImports( *ast.File) bool { for , := range .Decls { , := .(*ast.GenDecl) if ! || .Tok != token.IMPORT { // Not an import declaration, so we're done. // Imports are always first. return false } if .Lparen.IsValid() { // For now assume all grouped imports are unsorted. // TODO(gri) Should check if they are sorted already. return true } // Ungrouped imports are sorted by default. } return false }