format.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  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. "git.i2edu.net/i2/go-zero/core/errorx"
  13. "git.i2edu.net/i2/go-zero/tools/goctl/api/parser"
  14. "git.i2edu.net/i2/go-zero/tools/goctl/api/util"
  15. ctlutil "git.i2edu.net/i2/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. abs, err := filepath.Abs(apiFilePath)
  76. if err != nil {
  77. return err
  78. }
  79. result, err := apiFormat(string(data), abs)
  80. if err != nil {
  81. return err
  82. }
  83. _, err = parser.ParseContent(result, abs)
  84. if err != nil {
  85. return err
  86. }
  87. return ioutil.WriteFile(apiFilePath, []byte(result), os.ModePerm)
  88. }
  89. func apiFormat(data string, filename ...string) (string, error) {
  90. _, err := parser.ParseContent(data, filename...)
  91. if err != nil {
  92. return "", err
  93. }
  94. var builder strings.Builder
  95. s := bufio.NewScanner(strings.NewReader(data))
  96. tapCount := 0
  97. newLineCount := 0
  98. var preLine string
  99. for s.Scan() {
  100. line := strings.TrimSpace(s.Text())
  101. if len(line) == 0 {
  102. if newLineCount > 0 {
  103. continue
  104. }
  105. newLineCount++
  106. } else {
  107. if preLine == rightBrace {
  108. builder.WriteString(ctlutil.NL)
  109. }
  110. newLineCount = 0
  111. }
  112. if tapCount == 0 {
  113. format, err := formatGoTypeDef(line, s, &builder)
  114. if err != nil {
  115. return "", err
  116. }
  117. if format {
  118. continue
  119. }
  120. }
  121. noCommentLine := util.RemoveComment(line)
  122. if noCommentLine == rightParenthesis || noCommentLine == rightBrace {
  123. tapCount--
  124. }
  125. if tapCount < 0 {
  126. line := strings.TrimSuffix(noCommentLine, rightBrace)
  127. line = strings.TrimSpace(line)
  128. if strings.HasSuffix(line, leftBrace) {
  129. tapCount++
  130. }
  131. }
  132. util.WriteIndent(&builder, tapCount)
  133. builder.WriteString(line + ctlutil.NL)
  134. if strings.HasSuffix(noCommentLine, leftParenthesis) || strings.HasSuffix(noCommentLine, leftBrace) {
  135. tapCount++
  136. }
  137. preLine = line
  138. }
  139. return strings.TrimSpace(builder.String()), nil
  140. }
  141. func formatGoTypeDef(line string, scanner *bufio.Scanner, builder *strings.Builder) (bool, error) {
  142. noCommentLine := util.RemoveComment(line)
  143. tokenCount := 0
  144. if strings.HasPrefix(noCommentLine, "type") && (strings.HasSuffix(noCommentLine, leftParenthesis) ||
  145. strings.HasSuffix(noCommentLine, leftBrace)) {
  146. var typeBuilder strings.Builder
  147. typeBuilder.WriteString(mayInsertStructKeyword(line, &tokenCount) + ctlutil.NL)
  148. for scanner.Scan() {
  149. noCommentLine := util.RemoveComment(scanner.Text())
  150. typeBuilder.WriteString(mayInsertStructKeyword(scanner.Text(), &tokenCount) + ctlutil.NL)
  151. if noCommentLine == rightBrace || noCommentLine == rightParenthesis {
  152. tokenCount--
  153. }
  154. if tokenCount == 0 {
  155. ts, err := format.Source([]byte(typeBuilder.String()))
  156. if err != nil {
  157. return false, errors.New("error format \n" + typeBuilder.String())
  158. }
  159. result := strings.ReplaceAll(string(ts), " struct ", " ")
  160. result = strings.ReplaceAll(result, "type ()", "")
  161. builder.WriteString(result)
  162. break
  163. }
  164. }
  165. return true, nil
  166. }
  167. return false, nil
  168. }
  169. func mayInsertStructKeyword(line string, token *int) string {
  170. insertStruct := func() string {
  171. if strings.Contains(line, " struct") {
  172. return line
  173. }
  174. index := strings.Index(line, leftBrace)
  175. return line[:index] + " struct " + line[index:]
  176. }
  177. noCommentLine := util.RemoveComment(line)
  178. if strings.HasSuffix(noCommentLine, leftBrace) {
  179. *token++
  180. return insertStruct()
  181. }
  182. if strings.HasSuffix(noCommentLine, rightBrace) {
  183. noCommentLine = strings.TrimSuffix(noCommentLine, rightBrace)
  184. noCommentLine = util.RemoveComment(noCommentLine)
  185. if strings.HasSuffix(noCommentLine, leftBrace) {
  186. return insertStruct()
  187. }
  188. }
  189. if strings.HasSuffix(noCommentLine, leftParenthesis) {
  190. *token++
  191. }
  192. if strings.Contains(noCommentLine, "`") {
  193. return util.UpperFirst(strings.TrimSpace(line))
  194. }
  195. return line
  196. }