format.go 5.3 KB

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