You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
4.0 KiB

  1. // The eg command performs example-based refactoring.
  2. // For documentation, run the command, or see Help in
  3. // golang.org/x/tools/refactor/eg.
  4. package main // import "golang.org/x/tools/cmd/eg"
  5. import (
  6. "flag"
  7. "fmt"
  8. "go/build"
  9. "go/format"
  10. "go/parser"
  11. "go/token"
  12. "os"
  13. "os/exec"
  14. "strings"
  15. "golang.org/x/tools/go/buildutil"
  16. "golang.org/x/tools/go/loader"
  17. "golang.org/x/tools/refactor/eg"
  18. )
  19. var (
  20. 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.")
  21. helpFlag = flag.Bool("help", false, "show detailed help message")
  22. templateFlag = flag.String("t", "", "template.go file specifying the refactoring")
  23. transitiveFlag = flag.Bool("transitive", false, "apply refactoring to all dependencies too")
  24. writeFlag = flag.Bool("w", false, "rewrite input files in place (by default, the results are printed to standard output)")
  25. verboseFlag = flag.Bool("v", false, "show verbose matcher diagnostics")
  26. )
  27. func init() {
  28. flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
  29. }
  30. const usage = `eg: an example-based refactoring tool.
  31. Usage: eg -t template.go [-w] [-transitive] <args>...
  32. -help show detailed help message
  33. -t template.go specifies the template file (use -help to see explanation)
  34. -w causes files to be re-written in place.
  35. -transitive causes all dependencies to be refactored too.
  36. -v show verbose matcher diagnostics
  37. -beforeedit cmd a command to exec before each file is modified.
  38. "{}" represents the name of the file.
  39. ` + loader.FromArgsUsage
  40. func main() {
  41. if err := doMain(); err != nil {
  42. fmt.Fprintf(os.Stderr, "eg: %s\n", err)
  43. os.Exit(1)
  44. }
  45. }
  46. func doMain() error {
  47. flag.Parse()
  48. args := flag.Args()
  49. if *helpFlag {
  50. fmt.Fprint(os.Stderr, eg.Help)
  51. os.Exit(2)
  52. }
  53. if len(args) == 0 {
  54. fmt.Fprint(os.Stderr, usage)
  55. os.Exit(1)
  56. }
  57. if *templateFlag == "" {
  58. return fmt.Errorf("no -t template.go file specified")
  59. }
  60. conf := loader.Config{
  61. Fset: token.NewFileSet(),
  62. ParserMode: parser.ParseComments,
  63. }
  64. // The first Created package is the template.
  65. conf.CreateFromFilenames("template", *templateFlag)
  66. if _, err := conf.FromArgs(args, true); err != nil {
  67. return err
  68. }
  69. // Load, parse and type-check the whole program.
  70. iprog, err := conf.Load()
  71. if err != nil {
  72. return err
  73. }
  74. // Analyze the template.
  75. template := iprog.Created[0]
  76. xform, err := eg.NewTransformer(iprog.Fset, template.Pkg, template.Files[0], &template.Info, *verboseFlag)
  77. if err != nil {
  78. return err
  79. }
  80. // Apply it to the input packages.
  81. var pkgs []*loader.PackageInfo
  82. if *transitiveFlag {
  83. for _, info := range iprog.AllPackages {
  84. pkgs = append(pkgs, info)
  85. }
  86. } else {
  87. pkgs = iprog.InitialPackages()
  88. }
  89. var hadErrors bool
  90. for _, pkg := range pkgs {
  91. if pkg == template {
  92. continue
  93. }
  94. for _, file := range pkg.Files {
  95. n := xform.Transform(&pkg.Info, pkg.Pkg, file)
  96. if n == 0 {
  97. continue
  98. }
  99. filename := iprog.Fset.File(file.Pos()).Name()
  100. fmt.Fprintf(os.Stderr, "=== %s (%d matches)\n", filename, n)
  101. if *writeFlag {
  102. // Run the before-edit command (e.g. "chmod +w", "checkout") if any.
  103. if *beforeeditFlag != "" {
  104. args := strings.Fields(*beforeeditFlag)
  105. // Replace "{}" with the filename, like find(1).
  106. for i := range args {
  107. if i > 0 {
  108. args[i] = strings.Replace(args[i], "{}", filename, -1)
  109. }
  110. }
  111. cmd := exec.Command(args[0], args[1:]...)
  112. cmd.Stdout = os.Stdout
  113. cmd.Stderr = os.Stderr
  114. if err := cmd.Run(); err != nil {
  115. fmt.Fprintf(os.Stderr, "Warning: edit hook %q failed (%s)\n",
  116. args, err)
  117. }
  118. }
  119. if err := eg.WriteAST(iprog.Fset, filename, file); err != nil {
  120. fmt.Fprintf(os.Stderr, "eg: %s\n", err)
  121. hadErrors = true
  122. }
  123. } else {
  124. format.Node(os.Stdout, iprog.Fset, file)
  125. }
  126. }
  127. }
  128. if hadErrors {
  129. os.Exit(1)
  130. }
  131. return nil
  132. }