@@ -7,6 +7,7 @@ package main
77import (
88 "bufio"
99 "bytes"
10+ "errors"
1011 "flag"
1112 "fmt"
1213 "go/scanner"
2930 list = flag .Bool ("l" , false , "list files whose formatting differs from goimport's" )
3031 write = flag .Bool ("w" , false , "write result to (source) file instead of stdout" )
3132 doDiff = flag .Bool ("d" , false , "display diffs instead of rewriting files" )
32- srcdir = flag .String ("srcdir" , "" , "choose imports as if source code is from `dir`" )
33+ 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. " )
3334 verbose = flag .Bool ("v" , false , "verbose logging" )
3435
3536 cpuProfile = flag .String ("cpuprofile" , "" , "CPU profile output" )
@@ -67,9 +68,25 @@ func isGoFile(f os.FileInfo) bool {
6768 return ! f .IsDir () && ! strings .HasPrefix (name , "." ) && strings .HasSuffix (name , ".go" )
6869}
6970
70- func processFile (filename string , in io.Reader , out io.Writer , stdin bool ) error {
71+ // argumentType is which mode goimports was invoked as.
72+ type argumentType int
73+
74+ const (
75+ // fromStdin means the user is piping their source into goimports.
76+ fromStdin argumentType = iota
77+
78+ // singleArg is the common case from editors, when goimports is run on
79+ // a single file.
80+ singleArg
81+
82+ // multipleArg is when the user ran "goimports file1.go file2.go"
83+ // or ran goimports on a directory tree.
84+ multipleArg
85+ )
86+
87+ func processFile (filename string , in io.Reader , out io.Writer , argType argumentType ) error {
7188 opt := options
72- if stdin {
89+ if argType == fromStdin {
7390 nopt := * options
7491 nopt .Fragment = true
7592 opt = & nopt
@@ -91,9 +108,30 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
91108
92109 target := filename
93110 if * srcdir != "" {
94- // Pretend that file is from *srcdir in order to decide
95- // visible imports correctly.
96- target = filepath .Join (* srcdir , filepath .Base (filename ))
111+ // Determine whether the provided -srcdirc is a directory or file
112+ // and then use it to override the target.
113+ //
114+ // See https://github.com/dominikh/go-mode.el/issues/146
115+ if isFile (* srcdir ) {
116+ if argType == multipleArg {
117+ return errors .New ("-srcdir value can't be a file when passing multiple arguments or when walking directories" )
118+ }
119+ target = * srcdir
120+ } else if argType == singleArg && strings .HasSuffix (* srcdir , ".go" ) && ! isDir (* srcdir ) {
121+ // For a file which doesn't exist on disk yet, but might shortly.
122+ // e.g. user in editor opens $DIR/newfile.go and newfile.go doesn't yet exist on disk.
123+ // The goimports on-save hook writes the buffer to a temp file
124+ // first and runs goimports before the actual save to newfile.go.
125+ // The editor's buffer is named "newfile.go" so that is passed to goimports as:
126+ // goimports -srcdir=/gopath/src/pkg/newfile.go /tmp/gofmtXXXXXXXX.go
127+ // and then the editor reloads the result from the tmp file and writes
128+ // it to newfile.go.
129+ target = * srcdir
130+ } else {
131+ // Pretend that file is from *srcdir in order to decide
132+ // visible imports correctly.
133+ target = filepath .Join (* srcdir , filepath .Base (filename ))
134+ }
97135 }
98136
99137 res , err := imports .Process (target , src , opt )
@@ -131,7 +169,7 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
131169
132170func visitFile (path string , f os.FileInfo , err error ) error {
133171 if err == nil && isGoFile (f ) {
134- err = processFile (path , nil , os .Stdout , false )
172+ err = processFile (path , nil , os .Stdout , multipleArg )
135173 }
136174 if err != nil {
137175 report (err )
@@ -215,20 +253,25 @@ func gofmtMain() {
215253 }
216254
217255 if len (paths ) == 0 {
218- if err := processFile ("<standard input>" , os .Stdin , os .Stdout , true ); err != nil {
256+ if err := processFile ("<standard input>" , os .Stdin , os .Stdout , fromStdin ); err != nil {
219257 report (err )
220258 }
221259 return
222260 }
223261
262+ argType := singleArg
263+ if len (paths ) > 1 {
264+ argType = multipleArg
265+ }
266+
224267 for _ , path := range paths {
225268 switch dir , err := os .Stat (path ); {
226269 case err != nil :
227270 report (err )
228271 case dir .IsDir ():
229272 walkDir (path )
230273 default :
231- if err := processFile (path , nil , os .Stdout , false ); err != nil {
274+ if err := processFile (path , nil , os .Stdout , argType ); err != nil {
232275 report (err )
233276 }
234277 }
@@ -261,3 +304,15 @@ func diff(b1, b2 []byte) (data []byte, err error) {
261304 }
262305 return
263306}
307+
308+ // isFile reports whether name is a file.
309+ func isFile (name string ) bool {
310+ fi , err := os .Stat (name )
311+ return err == nil && fi .Mode ().IsRegular ()
312+ }
313+
314+ // isDir reports whether name is a directory.
315+ func isDir (name string ) bool {
316+ fi , err := os .Stat (name )
317+ return err == nil && fi .IsDir ()
318+ }
0 commit comments