├── .gitignore ├── README ├── LICENSE └── goimports.go /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This tool updates your Go import lines, adding missing ones and 2 | removing unreferenced ones. 3 | 4 | $ go get golang.org/x/tools/cmd/goimports 5 | 6 | Note the new location. This project has moved to the official 7 | go.tools repo. Pull requests here will no longer be accepted. 8 | Please use the Go process: http://golang.org/doc/contribute.html 9 | 10 | It acts the same as gofmt (same flags, etc) but in addition to code 11 | formatting, also fixes imports. 12 | 13 | See usage and editor integration notes, now moved elsewhere: 14 | 15 | http://godoc.org/golang.org/x/tools/cmd/goimports 16 | 17 | Happy hacking! 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /goimports.go: -------------------------------------------------------------------------------- 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 | 5 | package main 6 | 7 | import ( 8 | "bytes" 9 | "flag" 10 | "fmt" 11 | "go/scanner" 12 | "io" 13 | "io/ioutil" 14 | "os" 15 | "os/exec" 16 | "path/filepath" 17 | "runtime" 18 | "strings" 19 | 20 | "golang.org/x/tools/imports" 21 | ) 22 | 23 | var ( 24 | // main operation modes 25 | list = flag.Bool("l", false, "list files whose formatting differs from goimport's") 26 | write = flag.Bool("w", false, "write result to (source) file instead of stdout") 27 | doDiff = flag.Bool("d", false, "display diffs instead of rewriting files") 28 | 29 | options = &imports.Options{ 30 | TabWidth: 8, 31 | TabIndent: true, 32 | Comments: true, 33 | Fragment: true, 34 | } 35 | exitCode = 0 36 | ) 37 | 38 | func init() { 39 | flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)") 40 | } 41 | 42 | func report(err error) { 43 | scanner.PrintError(os.Stderr, err) 44 | exitCode = 2 45 | } 46 | 47 | func usage() { 48 | fmt.Fprintf(os.Stderr, "usage: goimports [flags] [path ...]\n") 49 | flag.PrintDefaults() 50 | os.Exit(2) 51 | } 52 | 53 | func isGoFile(f os.FileInfo) bool { 54 | // ignore non-Go files 55 | name := f.Name() 56 | return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") 57 | } 58 | 59 | func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error { 60 | opt := options 61 | if stdin { 62 | nopt := *options 63 | nopt.Fragment = true 64 | opt = &nopt 65 | } 66 | 67 | if in == nil { 68 | f, err := os.Open(filename) 69 | if err != nil { 70 | return err 71 | } 72 | defer f.Close() 73 | in = f 74 | } 75 | 76 | src, err := ioutil.ReadAll(in) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | res, err := imports.Process(filename, src, opt) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | if !bytes.Equal(src, res) { 87 | // formatting has changed 88 | if *list { 89 | fmt.Fprintln(out, filename) 90 | } 91 | if *write { 92 | err = ioutil.WriteFile(filename, res, 0) 93 | if err != nil { 94 | return err 95 | } 96 | } 97 | if *doDiff { 98 | data, err := diff(src, res) 99 | if err != nil { 100 | return fmt.Errorf("computing diff: %s", err) 101 | } 102 | fmt.Printf("diff %s gofmt/%s\n", filename, filename) 103 | out.Write(data) 104 | } 105 | } 106 | 107 | if !*list && !*write && !*doDiff { 108 | _, err = out.Write(res) 109 | } 110 | 111 | return err 112 | } 113 | 114 | func visitFile(path string, f os.FileInfo, err error) error { 115 | if err == nil && isGoFile(f) { 116 | err = processFile(path, nil, os.Stdout, false) 117 | } 118 | if err != nil { 119 | report(err) 120 | } 121 | return nil 122 | } 123 | 124 | func walkDir(path string) { 125 | filepath.Walk(path, visitFile) 126 | } 127 | 128 | func main() { 129 | runtime.GOMAXPROCS(runtime.NumCPU()) 130 | 131 | // call gofmtMain in a separate function 132 | // so that it can use defer and have them 133 | // run before the exit. 134 | gofmtMain() 135 | os.Exit(exitCode) 136 | } 137 | 138 | func gofmtMain() { 139 | flag.Usage = usage 140 | flag.Parse() 141 | 142 | if options.TabWidth < 0 { 143 | fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", options.TabWidth) 144 | exitCode = 2 145 | return 146 | } 147 | 148 | if flag.NArg() == 0 { 149 | if err := processFile("", os.Stdin, os.Stdout, true); err != nil { 150 | report(err) 151 | } 152 | return 153 | } 154 | 155 | for i := 0; i < flag.NArg(); i++ { 156 | path := flag.Arg(i) 157 | switch dir, err := os.Stat(path); { 158 | case err != nil: 159 | report(err) 160 | case dir.IsDir(): 161 | walkDir(path) 162 | default: 163 | if err := processFile(path, nil, os.Stdout, false); err != nil { 164 | report(err) 165 | } 166 | } 167 | } 168 | } 169 | 170 | func diff(b1, b2 []byte) (data []byte, err error) { 171 | f1, err := ioutil.TempFile("", "gofmt") 172 | if err != nil { 173 | return 174 | } 175 | defer os.Remove(f1.Name()) 176 | defer f1.Close() 177 | 178 | f2, err := ioutil.TempFile("", "gofmt") 179 | if err != nil { 180 | return 181 | } 182 | defer os.Remove(f2.Name()) 183 | defer f2.Close() 184 | 185 | f1.Write(b1) 186 | f2.Write(b2) 187 | 188 | data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() 189 | if len(data) > 0 { 190 | // diff exits with a non-zero status when the files don't match. 191 | // Ignore that failure as long as we get output. 192 | err = nil 193 | } 194 | return 195 | } 196 | --------------------------------------------------------------------------------