2014-04-02 10:24:55 -06:00
// The eg command performs example-based refactoring.
2014-11-13 10:34:25 -07:00
// For documentation, run the command, or see Help in
2014-12-14 13:42:37 -07:00
// golang.org/x/tools/refactor/eg.
2014-12-08 21:00:58 -07:00
package main // import "golang.org/x/tools/cmd/eg"
2014-04-02 10:24:55 -06:00
import (
"flag"
"fmt"
2015-04-21 12:00:20 -06:00
"go/build"
2017-07-12 17:47:01 -06:00
"go/format"
2014-04-02 10:24:55 -06:00
"go/parser"
"go/token"
"os"
2014-08-26 13:52:40 -06:00
"os/exec"
"strings"
2014-04-02 10:24:55 -06:00
2015-04-21 12:00:20 -06:00
"golang.org/x/tools/go/buildutil"
2014-11-09 14:50:40 -07:00
"golang.org/x/tools/go/loader"
"golang.org/x/tools/refactor/eg"
2014-04-02 10:24:55 -06:00
)
var (
2014-08-26 13:52:40 -06:00
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." )
2014-04-02 10:24:55 -06:00
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" )
)
2015-04-21 12:00:20 -06:00
func init ( ) {
flag . Var ( ( * buildutil . TagsFlag ) ( & build . Default . BuildTags ) , "tags" , buildutil . TagsFlagDoc )
}
2014-04-02 10:24:55 -06:00
const usage = ` eg : an example - based refactoring tool .
Usage : eg - t template . go [ - w ] [ - transitive ] < args > ...
2015-03-05 12:55:52 -07:00
- 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 .
2014-04-02 10:24:55 -06:00
` + loader . FromArgsUsage
func main ( ) {
if err := doMain ( ) ; err != nil {
2014-11-17 10:58:28 -07:00
fmt . Fprintf ( os . Stderr , "eg: %s\n" , err )
2014-04-02 10:24:55 -06:00
os . Exit ( 1 )
}
}
func doMain ( ) error {
flag . Parse ( )
args := flag . Args ( )
if * helpFlag {
2014-04-25 10:03:38 -06:00
fmt . Fprint ( os . Stderr , eg . Help )
2014-04-02 10:24:55 -06:00
os . Exit ( 2 )
}
2015-03-05 12:55:52 -07:00
if len ( args ) == 0 {
fmt . Fprint ( os . Stderr , usage )
os . Exit ( 1 )
}
2014-04-02 10:24:55 -06:00
if * templateFlag == "" {
return fmt . Errorf ( "no -t template.go file specified" )
}
conf := loader . Config {
2015-02-23 15:03:22 -07:00
Fset : token . NewFileSet ( ) ,
ParserMode : parser . ParseComments ,
2014-04-02 10:24:55 -06:00
}
// The first Created package is the template.
2015-01-30 11:30:23 -07:00
conf . CreateFromFilenames ( "template" , * templateFlag )
2014-04-02 10:24:55 -06:00
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 ]
2015-06-01 13:21:03 -06:00
xform , err := eg . NewTransformer ( iprog . Fset , template . Pkg , template . Files [ 0 ] , & template . Info , * verboseFlag )
2014-04-02 10:24:55 -06:00
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 ( )
2014-08-26 13:52:40 -06:00
fmt . Fprintf ( os . Stderr , "=== %s (%d matches)\n" , filename , n )
2014-04-02 10:24:55 -06:00
if * writeFlag {
2014-08-26 13:52:40 -06:00
// 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 )
}
}
2014-04-02 10:24:55 -06:00
if err := eg . WriteAST ( iprog . Fset , filename , file ) ; err != nil {
2014-11-17 10:58:28 -07:00
fmt . Fprintf ( os . Stderr , "eg: %s\n" , err )
2014-04-02 10:24:55 -06:00
hadErrors = true
}
} else {
2017-07-12 17:47:01 -06:00
format . Node ( os . Stdout , iprog . Fset , file )
2014-04-02 10:24:55 -06:00
}
}
}
if hadErrors {
os . Exit ( 1 )
}
return nil
}