github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/go/format/format.go (about) 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. 4 5 // Package format implements standard formatting of Go source. 6 package format 7 8 import ( 9 "bytes" 10 "fmt" 11 "go/ast" 12 "go/parser" 13 "go/printer" 14 "go/token" 15 "io" 16 ) 17 18 var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} 19 20 const parserMode = parser.ParseComments 21 22 // Node formats node in canonical gofmt style and writes the result to dst. 23 // 24 // The node type must be *ast.File, *printer.CommentedNode, []ast.Decl, 25 // []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, 26 // or ast.Stmt. Node does not modify node. Imports are not sorted for 27 // nodes representing partial source files (i.e., if the node is not an 28 // *ast.File or a *printer.CommentedNode not wrapping an *ast.File). 29 // 30 // The function may return early (before the entire result is written) 31 // and return a formatting error, for instance due to an incorrect AST. 32 // 33 func Node(dst io.Writer, fset *token.FileSet, node interface{}) error { 34 // Determine if we have a complete source file (file != nil). 35 var file *ast.File 36 var cnode *printer.CommentedNode 37 switch n := node.(type) { 38 case *ast.File: 39 file = n 40 case *printer.CommentedNode: 41 if f, ok := n.Node.(*ast.File); ok { 42 file = f 43 cnode = n 44 } 45 } 46 47 // Sort imports if necessary. 48 if file != nil && hasUnsortedImports(file) { 49 // Make a copy of the AST because ast.SortImports is destructive. 50 // TODO(gri) Do this more efficiently. 51 var buf bytes.Buffer 52 err := config.Fprint(&buf, fset, file) 53 if err != nil { 54 return err 55 } 56 file, err = parser.ParseFile(fset, "", buf.Bytes(), parserMode) 57 if err != nil { 58 // We should never get here. If we do, provide good diagnostic. 59 return fmt.Errorf("format.Node internal error (%s)", err) 60 } 61 ast.SortImports(fset, file) 62 63 // Use new file with sorted imports. 64 node = file 65 if cnode != nil { 66 node = &printer.CommentedNode{Node: file, Comments: cnode.Comments} 67 } 68 } 69 70 return config.Fprint(dst, fset, node) 71 } 72 73 // Source formats src in canonical gofmt style and returns the result 74 // or an (I/O or syntax) error. src is expected to be a syntactically 75 // correct Go source file, or a list of Go declarations or statements. 76 // 77 // If src is a partial source file, the leading and trailing space of src 78 // is applied to the result (such that it has the same leading and trailing 79 // space as src), and the result is indented by the same amount as the first 80 // line of src containing code. Imports are not sorted for partial source files. 81 // 82 func Source(src []byte) ([]byte, error) { 83 fset := token.NewFileSet() 84 file, sourceAdj, indentAdj, err := parse(fset, "", src, true) 85 if err != nil { 86 return nil, err 87 } 88 89 if sourceAdj == nil { 90 // Complete source file. 91 // TODO(gri) consider doing this always. 92 ast.SortImports(fset, file) 93 } 94 95 return format(fset, file, sourceAdj, indentAdj, src, config) 96 } 97 98 func hasUnsortedImports(file *ast.File) bool { 99 for _, d := range file.Decls { 100 d, ok := d.(*ast.GenDecl) 101 if !ok || d.Tok != token.IMPORT { 102 // Not an import declaration, so we're done. 103 // Imports are always first. 104 return false 105 } 106 if d.Lparen.IsValid() { 107 // For now assume all grouped imports are unsorted. 108 // TODO(gri) Should check if they are sorted already. 109 return true 110 } 111 // Ungrouped imports are sorted by default. 112 } 113 return false 114 }