@@ -7,6 +7,7 @@ package main
7
7
import (
8
8
"bufio"
9
9
"bytes"
10
+ "errors"
10
11
"flag"
11
12
"fmt"
12
13
"go/scanner"
29
30
list = flag .Bool ("l" , false , "list files whose formatting differs from goimport's" )
30
31
write = flag .Bool ("w" , false , "write result to (source) file instead of stdout" )
31
32
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. " )
33
34
verbose = flag .Bool ("v" , false , "verbose logging" )
34
35
35
36
cpuProfile = flag .String ("cpuprofile" , "" , "CPU profile output" )
@@ -67,9 +68,25 @@ func isGoFile(f os.FileInfo) bool {
67
68
return ! f .IsDir () && ! strings .HasPrefix (name , "." ) && strings .HasSuffix (name , ".go" )
68
69
}
69
70
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 {
71
88
opt := options
72
- if stdin {
89
+ if argType == fromStdin {
73
90
nopt := * options
74
91
nopt .Fragment = true
75
92
opt = & nopt
@@ -91,9 +108,30 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
91
108
92
109
target := filename
93
110
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
+ }
97
135
}
98
136
99
137
res , err := imports .Process (target , src , opt )
@@ -131,7 +169,7 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
131
169
132
170
func visitFile (path string , f os.FileInfo , err error ) error {
133
171
if err == nil && isGoFile (f ) {
134
- err = processFile (path , nil , os .Stdout , false )
172
+ err = processFile (path , nil , os .Stdout , multipleArg )
135
173
}
136
174
if err != nil {
137
175
report (err )
@@ -215,20 +253,25 @@ func gofmtMain() {
215
253
}
216
254
217
255
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 {
219
257
report (err )
220
258
}
221
259
return
222
260
}
223
261
262
+ argType := singleArg
263
+ if len (paths ) > 1 {
264
+ argType = multipleArg
265
+ }
266
+
224
267
for _ , path := range paths {
225
268
switch dir , err := os .Stat (path ); {
226
269
case err != nil :
227
270
report (err )
228
271
case dir .IsDir ():
229
272
walkDir (path )
230
273
default :
231
- if err := processFile (path , nil , os .Stdout , false ); err != nil {
274
+ if err := processFile (path , nil , os .Stdout , argType ); err != nil {
232
275
report (err )
233
276
}
234
277
}
@@ -261,3 +304,15 @@ func diff(b1, b2 []byte) (data []byte, err error) {
261
304
}
262
305
return
263
306
}
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