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