// 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] <args>...
|
|
|
|
-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
|
|
}
|