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 }