1 // Copyright 2014 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.
5 // The eg command performs example-based refactoring.
6 // For documentation, run the command, or see Help in
7 // golang.org/x/tools/refactor/eg.
8 package main // import "golang.org/x/tools/cmd/eg"
17 exec "golang.org/x/sys/execabs"
21 "golang.org/x/tools/go/buildutil"
22 "golang.org/x/tools/go/loader"
23 "golang.org/x/tools/refactor/eg"
27 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.")
28 helpFlag = flag.Bool("help", false, "show detailed help message")
29 templateFlag = flag.String("t", "", "template.go file specifying the refactoring")
30 transitiveFlag = flag.Bool("transitive", false, "apply refactoring to all dependencies too")
31 writeFlag = flag.Bool("w", false, "rewrite input files in place (by default, the results are printed to standard output)")
32 verboseFlag = flag.Bool("v", false, "show verbose matcher diagnostics")
36 flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
39 const usage = `eg: an example-based refactoring tool.
41 Usage: eg -t template.go [-w] [-transitive] <args>...
43 -help show detailed help message
44 -t template.go specifies the template file (use -help to see explanation)
45 -w causes files to be re-written in place.
46 -transitive causes all dependencies to be refactored too.
47 -v show verbose matcher diagnostics
48 -beforeedit cmd a command to exec before each file is modified.
49 "{}" represents the name of the file.
50 ` + loader.FromArgsUsage
53 if err := doMain(); err != nil {
54 fmt.Fprintf(os.Stderr, "eg: %s\n", err)
64 fmt.Fprint(os.Stderr, eg.Help)
69 fmt.Fprint(os.Stderr, usage)
73 if *templateFlag == "" {
74 return fmt.Errorf("no -t template.go file specified")
77 conf := loader.Config{
78 Fset: token.NewFileSet(),
79 ParserMode: parser.ParseComments,
82 // The first Created package is the template.
83 conf.CreateFromFilenames("template", *templateFlag)
85 if _, err := conf.FromArgs(args, true); err != nil {
89 // Load, parse and type-check the whole program.
90 iprog, err := conf.Load()
95 // Analyze the template.
96 template := iprog.Created[0]
97 xform, err := eg.NewTransformer(iprog.Fset, template.Pkg, template.Files[0], &template.Info, *verboseFlag)
102 // Apply it to the input packages.
103 var pkgs []*loader.PackageInfo
105 for _, info := range iprog.AllPackages {
106 pkgs = append(pkgs, info)
109 pkgs = iprog.InitialPackages()
112 for _, pkg := range pkgs {
116 for _, file := range pkg.Files {
117 n := xform.Transform(&pkg.Info, pkg.Pkg, file)
121 filename := iprog.Fset.File(file.Pos()).Name()
122 fmt.Fprintf(os.Stderr, "=== %s (%d matches)\n", filename, n)
124 // Run the before-edit command (e.g. "chmod +w", "checkout") if any.
125 if *beforeeditFlag != "" {
126 args := strings.Fields(*beforeeditFlag)
127 // Replace "{}" with the filename, like find(1).
128 for i := range args {
130 args[i] = strings.Replace(args[i], "{}", filename, -1)
133 cmd := exec.Command(args[0], args[1:]...)
134 cmd.Stdout = os.Stdout
135 cmd.Stderr = os.Stderr
136 if err := cmd.Run(); err != nil {
137 fmt.Fprintf(os.Stderr, "Warning: edit hook %q failed (%s)\n",
141 if err := eg.WriteAST(iprog.Fset, filename, file); err != nil {
142 fmt.Fprintf(os.Stderr, "eg: %s\n", err)
146 format.Node(os.Stdout, iprog.Fset, file)