kosx

Google CMD goimports.go

Dec 30th, 2020
883
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.  
  2. // Copyright 2013 The Go Authors. All rights reserved.
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. package main
  6. import (
  7.     "bufio"
  8.     "bytes"
  9.     "errors"
  10.     "flag"
  11.     "fmt"
  12.     "go/build"
  13.     "go/scanner"
  14.     "io"
  15.     "io/ioutil"
  16.     "log"
  17.     "os"
  18.     "os/exec"
  19.     "path/filepath"
  20.     "runtime"
  21.     "runtime/pprof"
  22.     "strings"
  23.     "golang.org/x/tools/internal/imports"
  24. )
  25. var (
  26.     // main operation modes
  27.     list   = flag.Bool("l", false, "list files whose formatting differs from goimport's")
  28.     write  = flag.Bool("w", false, "write result to (source) file instead of stdout")
  29.     doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
  30.     srcdir = flag.String("srcdir", "", "choose imports as if source code is from `dir`. When operating on a single file, dir may instead be the complete file name.")
  31.     verbose bool // verbose logging
  32.     cpuProfile     = flag.String("cpuprofile", "", "CPU profile output")
  33.     memProfile     = flag.String("memprofile", "", "memory profile output")
  34.     memProfileRate = flag.Int("memrate", 0, "if > 0, sets runtime.MemProfileRate")
  35.     options = &imports.Options{
  36.         TabWidth:  8,
  37.         TabIndent: true,
  38.         Comments:  true,
  39.         Fragment:  true,
  40.         // This environment, and its caches, will be reused for the whole run.
  41.         Env: &imports.ProcessEnv{
  42.             GOPATH:      build.Default.GOPATH,
  43.             GOROOT:      build.Default.GOROOT,
  44.             GOFLAGS:     os.Getenv("GOFLAGS"),
  45.             GO111MODULE: os.Getenv("GO111MODULE"),
  46.             GOPROXY:     os.Getenv("GOPROXY"),
  47.             GOSUMDB:     os.Getenv("GOSUMDB"),
  48.         },
  49.     }
  50.     exitCode = 0
  51. )
  52. func init() {
  53.     flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
  54.     flag.StringVar(&options.Env.LocalPrefix, "local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list")
  55.     flag.BoolVar(&options.FormatOnly, "format-only", false, "if true, don't fix imports and only format. In this mode, goimports is effectively gofmt, with the addition that imports are grouped into sections.")
  56. }
  57. func report(err error) {
  58.     scanner.PrintError(os.Stderr, err)
  59.     exitCode = 2
  60. }
  61. func usage() {
  62.     fmt.Fprintf(os.Stderr, "usage: goimports [flags] [path ...]\n")
  63.     flag.PrintDefaults()
  64.     os.Exit(2)
  65. }
  66. func isGoFile(f os.FileInfo) bool {
  67.     // ignore non-Go files
  68.     name := f.Name()
  69.     return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
  70. }
  71. // argumentType is which mode goimports was invoked as.
  72. type argumentType int
  73. const (
  74.     // fromStdin means the user is piping their source into goimports.
  75.     fromStdin argumentType = iota
  76.     // singleArg is the common case from editors, when goimports is run on
  77.     // a single file.
  78.     singleArg
  79.     // multipleArg is when the user ran "goimports file1.go file2.go"
  80.     // or ran goimports on a directory tree.
  81.     multipleArg
  82. )
  83. func processFile(filename string, in io.Reader, out io.Writer, argType argumentType) error {
  84.     opt := options
  85.     if argType == fromStdin {
  86.         nopt := *options
  87.         nopt.Fragment = true
  88.         opt = &nopt
  89.     }
  90.     if in == nil {
  91.         f, err := os.Open(filename)
  92.         if err != nil {
  93.             return err
  94.         }
  95.         defer f.Close()
  96.         in = f
  97.     }
  98.     src, err := ioutil.ReadAll(in)
  99.     if err != nil {
  100.         return err
  101.     }
  102.     target := filename
  103.     if *srcdir != "" {
  104.         // Determine whether the provided -srcdirc is a directory or file
  105.         // and then use it to override the target.
  106.         //
  107.         // See https://github.com/dominikh/go-mode.el/issues/146
  108.         if isFile(*srcdir) {
  109.             if argType == multipleArg {
  110.                 return errors.New("-srcdir value can't be a file when passing multiple arguments or when walking directories")
  111.             }
  112.             target = *srcdir
  113.         } else if argType == singleArg && strings.HasSuffix(*srcdir, ".go") && !isDir(*srcdir) {
  114.             // For a file which doesn't exist on disk yet, but might shortly.
  115.             // e.g. user in editor opens $DIR/newfile.go and newfile.go doesn't yet exist on disk.
  116.             // The goimports on-save hook writes the buffer to a temp file
  117.             // first and runs goimports before the actual save to newfile.go.
  118.             // The editor's buffer is named "newfile.go" so that is passed to goimports as:
  119.             //      goimports -srcdir=/gopath/src/pkg/newfile.go /tmp/gofmtXXXXXXXX.go
  120.             // and then the editor reloads the result from the tmp file and writes
  121.             // it to newfile.go.
  122.             target = *srcdir
  123.         } else {
  124.             // Pretend that file is from *srcdir in order to decide
  125.             // visible imports correctly.
  126.             target = filepath.Join(*srcdir, filepath.Base(filename))
  127.         }
  128.     }
  129.     res, err := imports.Process(target, src, opt)
  130.     if err != nil {
  131.         return err
  132.     }
  133.     if !bytes.Equal(src, res) {
  134.         // formatting has changed
  135.         if *list {
  136.             fmt.Fprintln(out, filename)
  137.         }
  138.         if *write {
  139.             if argType == fromStdin {
  140.                 // filename is "<standard input>"
  141.                 return errors.New("can't use -w on stdin")
  142.             }
  143.             // On Windows, we need to re-set the permissions from the file. See golang/go#38225.
  144.             var perms os.FileMode
  145.             if fi, err := os.Stat(filename); err == nil {
  146.                 perms = fi.Mode() & os.ModePerm
  147.             }
  148.             err = ioutil.WriteFile(filename, res, perms)
  149.             if err != nil {
  150.                 return err
  151.             }
  152.         }
  153.         if *doDiff {
  154.             if argType == fromStdin {
  155.                 filename = "stdin.go" // because <standard input>.orig looks silly
  156.             }
  157.             data, err := diff(src, res, filename)
  158.             if err != nil {
  159.                 return fmt.Errorf("computing diff: %s", err)
  160.             }
  161.             fmt.Printf("diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename))
  162.             out.Write(data)
  163.         }
  164.     }
  165.     if !*list && !*write && !*doDiff {
  166.         _, err = out.Write(res)
  167.     }
  168.     return err
  169. }
  170. func visitFile(path string, f os.FileInfo, err error) error {
  171.     if err == nil && isGoFile(f) {
  172.         err = processFile(path, nil, os.Stdout, multipleArg)
  173.     }
  174.     if err != nil {
  175.         report(err)
  176.     }
  177.     return nil
  178. }
  179. func walkDir(path string) {
  180.     filepath.Walk(path, visitFile)
  181. }
  182. func main() {
  183.     runtime.GOMAXPROCS(runtime.NumCPU())
  184.     // call gofmtMain in a separate function
  185.     // so that it can use defer and have them
  186.     // run before the exit.
  187.     gofmtMain()
  188.     os.Exit(exitCode)
  189. }
  190. // parseFlags parses command line flags and returns the paths to process.
  191. // It's a var so that custom implementations can replace it in other files.
  192. var parseFlags = func() []string {
  193.     flag.BoolVar(&verbose, "v", false, "verbose logging")
  194.     flag.Parse()
  195.     return flag.Args()
  196. }
  197. func bufferedFileWriter(dest string) (w io.Writer, close func()) {
  198.     f, err := os.Create(dest)
  199.     if err != nil {
  200.         log.Fatal(err)
  201.     }
  202.     bw := bufio.NewWriter(f)
  203.     return bw, func() {
  204.         if err := bw.Flush(); err != nil {
  205.             log.Fatalf("error flushing %v: %v", dest, err)
  206.         }
  207.         if err := f.Close(); err != nil {
  208.             log.Fatal(err)
  209.         }
  210.     }
  211. }
  212. func gofmtMain() {
  213.     flag.Usage = usage
  214.     paths := parseFlags()
  215.     if *cpuProfile != "" {
  216.         bw, flush := bufferedFileWriter(*cpuProfile)
  217.         pprof.StartCPUProfile(bw)
  218.         defer flush()
  219.         defer pprof.StopCPUProfile()
  220.     }
  221.     // doTrace is a conditionally compiled wrapper around runtime/trace. It is
  222.     // used to allow goimports to compile under gccgo, which does not support
  223.     // runtime/trace. See https://golang.org/issue/15544.
  224.     defer doTrace()()
  225.     if *memProfileRate > 0 {
  226.         runtime.MemProfileRate = *memProfileRate
  227.         bw, flush := bufferedFileWriter(*memProfile)
  228.         defer func() {
  229.             runtime.GC() // materialize all statistics
  230.             if err := pprof.WriteHeapProfile(bw); err != nil {
  231.                 log.Fatal(err)
  232.             }
  233.             flush()
  234.         }()
  235.     }
  236.     if verbose {
  237.         log.SetFlags(log.LstdFlags | log.Lmicroseconds)
  238.         options.Env.Logf = log.Printf
  239.     }
  240.     if options.TabWidth < 0 {
  241.         fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", options.TabWidth)
  242.         exitCode = 2
  243.         return
  244.     }
  245.     if len(paths) == 0 {
  246.         if err := processFile("<standard input>", os.Stdin, os.Stdout, fromStdin); err != nil {
  247.             report(err)
  248.         }
  249.         return
  250.     }
  251.     argType := singleArg
  252.     if len(paths) > 1 {
  253.         argType = multipleArg
  254.     }
  255.     for _, path := range paths {
  256.         switch dir, err := os.Stat(path); {
  257.         case err != nil:
  258.             report(err)
  259.         case dir.IsDir():
  260.             walkDir(path)
  261.         default:
  262.             if err := processFile(path, nil, os.Stdout, argType); err != nil {
  263.                 report(err)
  264.             }
  265.         }
  266.     }
  267. }
  268. func writeTempFile(dir, prefix string, data []byte) (string, error) {
  269.     file, err := ioutil.TempFile(dir, prefix)
  270.     if err != nil {
  271.         return "", err
  272.     }
  273.     _, err = file.Write(data)
  274.     if err1 := file.Close(); err == nil {
  275.         err = err1
  276.     }
  277.     if err != nil {
  278.         os.Remove(file.Name())
  279.         return "", err
  280.     }
  281.     return file.Name(), nil
  282. }
  283. func diff(b1, b2 []byte, filename string) (data []byte, err error) {
  284.     f1, err := writeTempFile("", "gofmt", b1)
  285.     if err != nil {
  286.         return
  287.     }
  288.     defer os.Remove(f1)
  289.     f2, err := writeTempFile("", "gofmt", b2)
  290.     if err != nil {
  291.         return
  292.     }
  293.     defer os.Remove(f2)
  294.     cmd := "diff"
  295.     if runtime.GOOS == "plan9" {
  296.         cmd = "/bin/ape/diff"
  297.     }
  298.     data, err = exec.Command(cmd, "-u", f1, f2).CombinedOutput()
  299.     if len(data) > 0 {
  300.         // diff exits with a non-zero status when the files don't match.
  301.         // Ignore that failure as long as we get output.
  302.         return replaceTempFilename(data, filename)
  303.     }
  304.     return
  305. }
  306. // replaceTempFilename replaces temporary filenames in diff with actual one.
  307. //
  308. // --- /tmp/gofmt316145376  2017-02-03 19:13:00.280468375 -0500
  309. // +++ /tmp/gofmt617882815  2017-02-03 19:13:00.280468375 -0500
  310. // ...
  311. // ->
  312. // --- path/to/file.go.orig 2017-02-03 19:13:00.280468375 -0500
  313. // +++ path/to/file.go  2017-02-03 19:13:00.280468375 -0500
  314. // ...
  315. func replaceTempFilename(diff []byte, filename string) ([]byte, error) {
  316.     bs := bytes.SplitN(diff, []byte{'\n'}, 3)
  317.     if len(bs) < 3 {
  318.         return nil, fmt.Errorf("got unexpected diff for %s", filename)
  319.     }
  320.     // Preserve timestamps.
  321.     var t0, t1 []byte
  322.     if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 {
  323.         t0 = bs[0][i:]
  324.     }
  325.     if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 {
  326.         t1 = bs[1][i:]
  327.     }
  328.     // Always print filepath with slash separator.
  329.     f := filepath.ToSlash(filename)
  330.     bs[0] = []byte(fmt.Sprintf("--- %s%s", f+".orig", t0))
  331.     bs[1] = []byte(fmt.Sprintf("+++ %s%s", f, t1))
  332.     return bytes.Join(bs, []byte{'\n'}), nil
  333. }
  334. // isFile reports whether name is a file.
  335. func isFile(name string) bool {
  336.     fi, err := os.Stat(name)
  337.     return err == nil && fi.Mode().IsRegular()
  338. }
  339. // isDir reports whether name is a directory.
  340. func isDir(name string) bool {
  341.     fi, err := os.Stat(name)
  342.     return err == nil && fi.IsDir()
  343. }
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×