format.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. package format
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "go/format"
  7. "go/scanner"
  8. "io/ioutil"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "github.com/tal-tech/go-zero/core/errorx"
  13. "github.com/tal-tech/go-zero/tools/goctl/api/parser"
  14. "github.com/tal-tech/go-zero/tools/goctl/api/util"
  15. ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
  16. "github.com/urfave/cli"
  17. )
  18. const (
  19. leftParenthesis = "("
  20. rightParenthesis = ")"
  21. leftBrace = "{"
  22. rightBrace = "}"
  23. )
  24. // GoFormatApi format api file
  25. func GoFormatApi(c *cli.Context) error {
  26. useStdin := c.Bool("stdin")
  27. var be errorx.BatchError
  28. if useStdin {
  29. if err := apiFormatByStdin(); err != nil {
  30. be.Add(err)
  31. }
  32. } else {
  33. dir := c.String("dir")
  34. if len(dir) == 0 {
  35. return errors.New("missing -dir")
  36. }
  37. _, err := os.Lstat(dir)
  38. if err != nil {
  39. return errors.New(dir + ": No such file or directory")
  40. }
  41. err = filepath.Walk(dir, func(path string, fi os.FileInfo, errBack error) (err error) {
  42. if strings.HasSuffix(path, ".api") {
  43. if err := ApiFormatByPath(path); err != nil {
  44. be.Add(util.WrapErr(err, fi.Name()))
  45. }
  46. }
  47. return nil
  48. })
  49. be.Add(err)
  50. }
  51. if be.NotNil() {
  52. scanner.PrintError(os.Stderr, be.Err())
  53. os.Exit(1)
  54. }
  55. return be.Err()
  56. }
  57. func apiFormatByStdin() error {
  58. data, err := ioutil.ReadAll(os.Stdin)
  59. if err != nil {
  60. return err
  61. }
  62. result, err := apiFormat(string(data))
  63. if err != nil {
  64. return err
  65. }
  66. _, err = fmt.Print(result)
  67. return err
  68. }
  69. // ApiFormatByPath format api from file path
  70. func ApiFormatByPath(apiFilePath string) error {
  71. data, err := ioutil.ReadFile(apiFilePath)
  72. if err != nil {
  73. return err
  74. }
  75. result, err := apiFormat(string(data))
  76. if err != nil {
  77. return err
  78. }
  79. _, err = parser.ParseContent(result)
  80. if err != nil {
  81. return err
  82. }
  83. return ioutil.WriteFile(apiFilePath, []byte(result), os.ModePerm)
  84. }
  85. func apiFormat(data string) (string, error) {
  86. _, err := parser.ParseContent(data)
  87. if err != nil {
  88. return "", err
  89. }
  90. var builder strings.Builder
  91. s := bufio.NewScanner(strings.NewReader(data))
  92. var tapCount = 0
  93. var newLineCount = 0
  94. var preLine string
  95. for s.Scan() {
  96. line := strings.TrimSpace(s.Text())
  97. if len(line) == 0 {
  98. if newLineCount > 0 {
  99. continue
  100. }
  101. newLineCount++
  102. } else {
  103. if preLine == rightBrace {
  104. builder.WriteString(ctlutil.NL)
  105. }
  106. newLineCount = 0
  107. }
  108. if tapCount == 0 {
  109. format, err := formatGoTypeDef(line, s, &builder)
  110. if err != nil {
  111. return "", err
  112. }
  113. if format {
  114. continue
  115. }
  116. }
  117. noCommentLine := util.RemoveComment(line)
  118. if noCommentLine == rightParenthesis || noCommentLine == rightBrace {
  119. tapCount--
  120. }
  121. if tapCount < 0 {
  122. line := strings.TrimSuffix(noCommentLine, rightBrace)
  123. line = strings.TrimSpace(line)
  124. if strings.HasSuffix(line, leftBrace) {
  125. tapCount++
  126. }
  127. }
  128. util.WriteIndent(&builder, tapCount)
  129. builder.WriteString(line + ctlutil.NL)
  130. if strings.HasSuffix(noCommentLine, leftParenthesis) || strings.HasSuffix(noCommentLine, leftBrace) {
  131. tapCount++
  132. }
  133. preLine = line
  134. }
  135. return strings.TrimSpace(builder.String()), nil
  136. }
  137. func formatGoTypeDef(line string, scanner *bufio.Scanner, builder *strings.Builder) (bool, error) {
  138. noCommentLine := util.RemoveComment(line)
  139. tokenCount := 0
  140. if strings.HasPrefix(noCommentLine, "type") && (strings.HasSuffix(noCommentLine, leftParenthesis) ||
  141. strings.HasSuffix(noCommentLine, leftBrace)) {
  142. var typeBuilder strings.Builder
  143. typeBuilder.WriteString(mayInsertStructKeyword(line, &tokenCount) + ctlutil.NL)
  144. for scanner.Scan() {
  145. noCommentLine := util.RemoveComment(scanner.Text())
  146. typeBuilder.WriteString(mayInsertStructKeyword(scanner.Text(), &tokenCount) + ctlutil.NL)
  147. if noCommentLine == rightBrace || noCommentLine == rightParenthesis {
  148. tokenCount--
  149. }
  150. if tokenCount == 0 {
  151. ts, err := format.Source([]byte(typeBuilder.String()))
  152. if err != nil {
  153. return false, errors.New("error format \n" + typeBuilder.String())
  154. }
  155. result := strings.ReplaceAll(string(ts), " struct ", " ")
  156. result = strings.ReplaceAll(result, "type ()", "")
  157. builder.WriteString(result)
  158. break
  159. }
  160. }
  161. return true, nil
  162. }
  163. return false, nil
  164. }
  165. func mayInsertStructKeyword(line string, token *int) string {
  166. insertStruct := func() string {
  167. if strings.Contains(line, " struct") {
  168. return line
  169. }
  170. index := strings.Index(line, leftBrace)
  171. return line[:index] + " struct " + line[index:]
  172. }
  173. noCommentLine := util.RemoveComment(line)
  174. if strings.HasSuffix(noCommentLine, leftBrace) {
  175. *token++
  176. return insertStruct()
  177. }
  178. if strings.HasSuffix(noCommentLine, rightBrace) {
  179. noCommentLine = strings.TrimSuffix(noCommentLine, rightBrace)
  180. noCommentLine = util.RemoveComment(noCommentLine)
  181. if strings.HasSuffix(noCommentLine, leftBrace) {
  182. return insertStruct()
  183. }
  184. }
  185. if strings.HasSuffix(noCommentLine, leftParenthesis) {
  186. *token++
  187. }
  188. if strings.Contains(noCommentLine, "`") {
  189. return util.UpperFirst(strings.TrimSpace(line))
  190. }
  191. return line
  192. }