imports.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. // Copyright 2013 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //go:generate go run mkstdlib.go
  5. // Package imports implements a Go pretty-printer (like package "go/format")
  6. // that also adds or removes import statements as necessary.
  7. package imports // import "golang.org/x/tools/imports"
  8. import (
  9. "bufio"
  10. "bytes"
  11. "fmt"
  12. "go/ast"
  13. "go/format"
  14. "go/parser"
  15. "go/printer"
  16. "go/token"
  17. "io"
  18. "regexp"
  19. "strconv"
  20. "strings"
  21. "golang.org/x/tools/go/ast/astutil"
  22. )
  23. // Options specifies options for processing files.
  24. type Options struct {
  25. Fragment bool // Accept fragment of a source file (no package statement)
  26. AllErrors bool // Report all errors (not just the first 10 on different lines)
  27. Comments bool // Print comments (true if nil *Options provided)
  28. TabIndent bool // Use tabs for indent (true if nil *Options provided)
  29. TabWidth int // Tab width (8 if nil *Options provided)
  30. FormatOnly bool // Disable the insertion and deletion of imports
  31. }
  32. // Process formats and adjusts imports for the provided file.
  33. // If opt is nil the defaults are used.
  34. //
  35. // Note that filename's directory influences which imports can be chosen,
  36. // so it is important that filename be accurate.
  37. // To process data ``as if'' it were in filename, pass the data as a non-nil src.
  38. func Process(filename string, src []byte, opt *Options) ([]byte, error) {
  39. if opt == nil {
  40. opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
  41. }
  42. fileSet := token.NewFileSet()
  43. file, adjust, err := parse(fileSet, filename, src, opt)
  44. if err != nil {
  45. return nil, err
  46. }
  47. if !opt.FormatOnly {
  48. _, err = fixImports(fileSet, file, filename)
  49. if err != nil {
  50. return nil, err
  51. }
  52. }
  53. sortImports(fileSet, file)
  54. imps := astutil.Imports(fileSet, file)
  55. var spacesBefore []string // import paths we need spaces before
  56. for _, impSection := range imps {
  57. // Within each block of contiguous imports, see if any
  58. // import lines are in different group numbers. If so,
  59. // we'll need to put a space between them so it's
  60. // compatible with gofmt.
  61. lastGroup := -1
  62. for _, importSpec := range impSection {
  63. importPath, _ := strconv.Unquote(importSpec.Path.Value)
  64. groupNum := importGroup(importPath)
  65. if groupNum != lastGroup && lastGroup != -1 {
  66. spacesBefore = append(spacesBefore, importPath)
  67. }
  68. lastGroup = groupNum
  69. }
  70. }
  71. printerMode := printer.UseSpaces
  72. if opt.TabIndent {
  73. printerMode |= printer.TabIndent
  74. }
  75. printConfig := &printer.Config{Mode: printerMode, Tabwidth: opt.TabWidth}
  76. var buf bytes.Buffer
  77. err = printConfig.Fprint(&buf, fileSet, file)
  78. if err != nil {
  79. return nil, err
  80. }
  81. out := buf.Bytes()
  82. if adjust != nil {
  83. out = adjust(src, out)
  84. }
  85. if len(spacesBefore) > 0 {
  86. out = addImportSpaces(bytes.NewReader(out), spacesBefore)
  87. }
  88. out, err = format.Source(out)
  89. if err != nil {
  90. return nil, err
  91. }
  92. return out, nil
  93. }
  94. // parse parses src, which was read from filename,
  95. // as a Go source file or statement list.
  96. func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast.File, func(orig, src []byte) []byte, error) {
  97. parserMode := parser.Mode(0)
  98. if opt.Comments {
  99. parserMode |= parser.ParseComments
  100. }
  101. if opt.AllErrors {
  102. parserMode |= parser.AllErrors
  103. }
  104. // Try as whole source file.
  105. file, err := parser.ParseFile(fset, filename, src, parserMode)
  106. if err == nil {
  107. return file, nil, nil
  108. }
  109. // If the error is that the source file didn't begin with a
  110. // package line and we accept fragmented input, fall through to
  111. // try as a source fragment. Stop and return on any other error.
  112. if !opt.Fragment || !strings.Contains(err.Error(), "expected 'package'") {
  113. return nil, nil, err
  114. }
  115. // If this is a declaration list, make it a source file
  116. // by inserting a package clause.
  117. // Insert using a ;, not a newline, so that the line numbers
  118. // in psrc match the ones in src.
  119. psrc := append([]byte("package main;"), src...)
  120. file, err = parser.ParseFile(fset, filename, psrc, parserMode)
  121. if err == nil {
  122. // If a main function exists, we will assume this is a main
  123. // package and leave the file.
  124. if containsMainFunc(file) {
  125. return file, nil, nil
  126. }
  127. adjust := func(orig, src []byte) []byte {
  128. // Remove the package clause.
  129. // Gofmt has turned the ; into a \n.
  130. src = src[len("package main\n"):]
  131. return matchSpace(orig, src)
  132. }
  133. return file, adjust, nil
  134. }
  135. // If the error is that the source file didn't begin with a
  136. // declaration, fall through to try as a statement list.
  137. // Stop and return on any other error.
  138. if !strings.Contains(err.Error(), "expected declaration") {
  139. return nil, nil, err
  140. }
  141. // If this is a statement list, make it a source file
  142. // by inserting a package clause and turning the list
  143. // into a function body. This handles expressions too.
  144. // Insert using a ;, not a newline, so that the line numbers
  145. // in fsrc match the ones in src.
  146. fsrc := append(append([]byte("package p; func _() {"), src...), '}')
  147. file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
  148. if err == nil {
  149. adjust := func(orig, src []byte) []byte {
  150. // Remove the wrapping.
  151. // Gofmt has turned the ; into a \n\n.
  152. src = src[len("package p\n\nfunc _() {"):]
  153. src = src[:len(src)-len("}\n")]
  154. // Gofmt has also indented the function body one level.
  155. // Remove that indent.
  156. src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1)
  157. return matchSpace(orig, src)
  158. }
  159. return file, adjust, nil
  160. }
  161. // Failed, and out of options.
  162. return nil, nil, err
  163. }
  164. // containsMainFunc checks if a file contains a function declaration with the
  165. // function signature 'func main()'
  166. func containsMainFunc(file *ast.File) bool {
  167. for _, decl := range file.Decls {
  168. if f, ok := decl.(*ast.FuncDecl); ok {
  169. if f.Name.Name != "main" {
  170. continue
  171. }
  172. if len(f.Type.Params.List) != 0 {
  173. continue
  174. }
  175. if f.Type.Results != nil && len(f.Type.Results.List) != 0 {
  176. continue
  177. }
  178. return true
  179. }
  180. }
  181. return false
  182. }
  183. func cutSpace(b []byte) (before, middle, after []byte) {
  184. i := 0
  185. for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') {
  186. i++
  187. }
  188. j := len(b)
  189. for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') {
  190. j--
  191. }
  192. if i <= j {
  193. return b[:i], b[i:j], b[j:]
  194. }
  195. return nil, nil, b[j:]
  196. }
  197. // matchSpace reformats src to use the same space context as orig.
  198. // 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src.
  199. // 2) matchSpace copies the indentation of the first non-blank line in orig
  200. // to every non-blank line in src.
  201. // 3) matchSpace copies the trailing space from orig and uses it in place
  202. // of src's trailing space.
  203. func matchSpace(orig []byte, src []byte) []byte {
  204. before, _, after := cutSpace(orig)
  205. i := bytes.LastIndex(before, []byte{'\n'})
  206. before, indent := before[:i+1], before[i+1:]
  207. _, src, _ = cutSpace(src)
  208. var b bytes.Buffer
  209. b.Write(before)
  210. for len(src) > 0 {
  211. line := src
  212. if i := bytes.IndexByte(line, '\n'); i >= 0 {
  213. line, src = line[:i+1], line[i+1:]
  214. } else {
  215. src = nil
  216. }
  217. if len(line) > 0 && line[0] != '\n' { // not blank
  218. b.Write(indent)
  219. }
  220. b.Write(line)
  221. }
  222. b.Write(after)
  223. return b.Bytes()
  224. }
  225. var impLine = regexp.MustCompile(`^\s+(?:[\w\.]+\s+)?"(.+)"`)
  226. func addImportSpaces(r io.Reader, breaks []string) []byte {
  227. var out bytes.Buffer
  228. sc := bufio.NewScanner(r)
  229. inImports := false
  230. done := false
  231. for sc.Scan() {
  232. s := sc.Text()
  233. if !inImports && !done && strings.HasPrefix(s, "import") {
  234. inImports = true
  235. }
  236. if inImports && (strings.HasPrefix(s, "var") ||
  237. strings.HasPrefix(s, "func") ||
  238. strings.HasPrefix(s, "const") ||
  239. strings.HasPrefix(s, "type")) {
  240. done = true
  241. inImports = false
  242. }
  243. if inImports && len(breaks) > 0 {
  244. if m := impLine.FindStringSubmatch(s); m != nil {
  245. if m[1] == breaks[0] {
  246. out.WriteByte('\n')
  247. breaks = breaks[1:]
  248. }
  249. }
  250. }
  251. fmt.Fprintln(&out, s)
  252. }
  253. return out.Bytes()
  254. }