// The eg command performs example-based refactoring. // For documentation, run the command, or see Help in // golang.org/x/tools/refactor/eg. package main // import "golang.org/x/tools/cmd/eg" import ( "flag" "fmt" "go/build" "go/format" "go/parser" "go/token" "os" "os/exec" "strings" "golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/loader" "golang.org/x/tools/refactor/eg" ) var ( 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.") helpFlag = flag.Bool("help", false, "show detailed help message") templateFlag = flag.String("t", "", "template.go file specifying the refactoring") transitiveFlag = flag.Bool("transitive", false, "apply refactoring to all dependencies too") writeFlag = flag.Bool("w", false, "rewrite input files in place (by default, the results are printed to standard output)") verboseFlag = flag.Bool("v", false, "show verbose matcher diagnostics") ) func init() { flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) } const usage = `eg: an example-based refactoring tool. Usage: eg -t template.go [-w] [-transitive] ... -help show detailed help message -t template.go specifies the template file (use -help to see explanation) -w causes files to be re-written in place. -transitive causes all dependencies to be refactored too. -v show verbose matcher diagnostics -beforeedit cmd a command to exec before each file is modified. "{}" represents the name of the file. ` + loader.FromArgsUsage func main() { if err := doMain(); err != nil { fmt.Fprintf(os.Stderr, "eg: %s\n", err) os.Exit(1) } } func doMain() error { flag.Parse() args := flag.Args() if *helpFlag { fmt.Fprint(os.Stderr, eg.Help) os.Exit(2) } if len(args) == 0 { fmt.Fprint(os.Stderr, usage) os.Exit(1) } if *templateFlag == "" { return fmt.Errorf("no -t template.go file specified") } conf := loader.Config{ Fset: token.NewFileSet(), ParserMode: parser.ParseComments, } // The first Created package is the template. conf.CreateFromFilenames("template", *templateFlag) if _, err := conf.FromArgs(args, true); err != nil { return err } // Load, parse and type-check the whole program. iprog, err := conf.Load() if err != nil { return err } // Analyze the template. template := iprog.Created[0] xform, err := eg.NewTransformer(iprog.Fset, template.Pkg, template.Files[0], &template.Info, *verboseFlag) if err != nil { return err } // Apply it to the input packages. var pkgs []*loader.PackageInfo if *transitiveFlag { for _, info := range iprog.AllPackages { pkgs = append(pkgs, info) } } else { pkgs = iprog.InitialPackages() } var hadErrors bool for _, pkg := range pkgs { if pkg == template { continue } for _, file := range pkg.Files { n := xform.Transform(&pkg.Info, pkg.Pkg, file) if n == 0 { continue } filename := iprog.Fset.File(file.Pos()).Name() fmt.Fprintf(os.Stderr, "=== %s (%d matches)\n", filename, n) if *writeFlag { // Run the before-edit command (e.g. "chmod +w", "checkout") if any. if *beforeeditFlag != "" { args := strings.Fields(*beforeeditFlag) // Replace "{}" with the filename, like find(1). for i := range args { if i > 0 { args[i] = strings.Replace(args[i], "{}", filename, -1) } } cmd := exec.Command(args[0], args[1:]...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Fprintf(os.Stderr, "Warning: edit hook %q failed (%s)\n", args, err) } } if err := eg.WriteAST(iprog.Fset, filename, file); err != nil { fmt.Fprintf(os.Stderr, "eg: %s\n", err) hadErrors = true } } else { format.Node(os.Stdout, iprog.Fset, file) } } } if hadErrors { os.Exit(1) } return nil }