github.com/april1989/origin-go-tools@v0.0.32/cmd/eg/eg.go (about)

     1  // The eg command performs example-based refactoring.
     2  // For documentation, run the command, or see Help in
     3  // github.com/april1989/origin-go-tools/refactor/eg.
     4  package main // import "github.com/april1989/origin-go-tools/cmd/eg"
     5  
     6  import (
     7  	"flag"
     8  	"fmt"
     9  	"go/build"
    10  	"go/format"
    11  	"go/parser"
    12  	"go/token"
    13  	"os"
    14  	"os/exec"
    15  	"strings"
    16  
    17  	"github.com/april1989/origin-go-tools/go/buildutil"
    18  	"github.com/april1989/origin-go-tools/go/loader"
    19  	"github.com/april1989/origin-go-tools/refactor/eg"
    20  )
    21  
    22  var (
    23  	beforeeditFlag = flag.String("beforeedit", "", "A command to exec before each file is edited (e.g. chmod, checkout).  Whitespace delimits argument words.  The string '{}' is replaced by the file name.")
    24  	helpFlag       = flag.Bool("help", false, "show detailed help message")
    25  	templateFlag   = flag.String("t", "", "template.go file specifying the refactoring")
    26  	transitiveFlag = flag.Bool("transitive", false, "apply refactoring to all dependencies too")
    27  	writeFlag      = flag.Bool("w", false, "rewrite input files in place (by default, the results are printed to standard output)")
    28  	verboseFlag    = flag.Bool("v", false, "show verbose matcher diagnostics")
    29  )
    30  
    31  func init() {
    32  	flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
    33  }
    34  
    35  const usage = `eg: an example-based refactoring tool.
    36  
    37  Usage: eg -t template.go [-w] [-transitive] <args>...
    38  
    39  -help            show detailed help message
    40  -t template.go	 specifies the template file (use -help to see explanation)
    41  -w          	 causes files to be re-written in place.
    42  -transitive 	 causes all dependencies to be refactored too.
    43  -v               show verbose matcher diagnostics
    44  -beforeedit cmd  a command to exec before each file is modified.
    45                   "{}" represents the name of the file.
    46  ` + loader.FromArgsUsage
    47  
    48  func main() {
    49  	if err := doMain(); err != nil {
    50  		fmt.Fprintf(os.Stderr, "eg: %s\n", err)
    51  		os.Exit(1)
    52  	}
    53  }
    54  
    55  func doMain() error {
    56  	flag.Parse()
    57  	args := flag.Args()
    58  
    59  	if *helpFlag {
    60  		fmt.Fprint(os.Stderr, eg.Help)
    61  		os.Exit(2)
    62  	}
    63  
    64  	if len(args) == 0 {
    65  		fmt.Fprint(os.Stderr, usage)
    66  		os.Exit(1)
    67  	}
    68  
    69  	if *templateFlag == "" {
    70  		return fmt.Errorf("no -t template.go file specified")
    71  	}
    72  
    73  	conf := loader.Config{
    74  		Fset:       token.NewFileSet(),
    75  		ParserMode: parser.ParseComments,
    76  	}
    77  
    78  	// The first Created package is the template.
    79  	conf.CreateFromFilenames("template", *templateFlag)
    80  
    81  	if _, err := conf.FromArgs(args, true); err != nil {
    82  		return err
    83  	}
    84  
    85  	// Load, parse and type-check the whole program.
    86  	iprog, err := conf.Load()
    87  	if err != nil {
    88  		return err
    89  	}
    90  
    91  	// Analyze the template.
    92  	template := iprog.Created[0]
    93  	xform, err := eg.NewTransformer(iprog.Fset, template.Pkg, template.Files[0], &template.Info, *verboseFlag)
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	// Apply it to the input packages.
    99  	var pkgs []*loader.PackageInfo
   100  	if *transitiveFlag {
   101  		for _, info := range iprog.AllPackages {
   102  			pkgs = append(pkgs, info)
   103  		}
   104  	} else {
   105  		pkgs = iprog.InitialPackages()
   106  	}
   107  	var hadErrors bool
   108  	for _, pkg := range pkgs {
   109  		if pkg == template {
   110  			continue
   111  		}
   112  		for _, file := range pkg.Files {
   113  			n := xform.Transform(&pkg.Info, pkg.Pkg, file)
   114  			if n == 0 {
   115  				continue
   116  			}
   117  			filename := iprog.Fset.File(file.Pos()).Name()
   118  			fmt.Fprintf(os.Stderr, "=== %s (%d matches)\n", filename, n)
   119  			if *writeFlag {
   120  				// Run the before-edit command (e.g. "chmod +w",  "checkout") if any.
   121  				if *beforeeditFlag != "" {
   122  					args := strings.Fields(*beforeeditFlag)
   123  					// Replace "{}" with the filename, like find(1).
   124  					for i := range args {
   125  						if i > 0 {
   126  							args[i] = strings.Replace(args[i], "{}", filename, -1)
   127  						}
   128  					}
   129  					cmd := exec.Command(args[0], args[1:]...)
   130  					cmd.Stdout = os.Stdout
   131  					cmd.Stderr = os.Stderr
   132  					if err := cmd.Run(); err != nil {
   133  						fmt.Fprintf(os.Stderr, "Warning: edit hook %q failed (%s)\n",
   134  							args, err)
   135  					}
   136  				}
   137  				if err := eg.WriteAST(iprog.Fset, filename, file); err != nil {
   138  					fmt.Fprintf(os.Stderr, "eg: %s\n", err)
   139  					hadErrors = true
   140  				}
   141  			} else {
   142  				format.Node(os.Stdout, iprog.Fset, file)
   143  			}
   144  		}
   145  	}
   146  	if hadErrors {
   147  		os.Exit(1)
   148  	}
   149  	return nil
   150  }