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  }