Skip to content

Commit f328430

Browse files
committed
cmd/goimports: permit complete filename for -srcdir
Updates dominikh/go-mode.el#146 Updates golang/go#7463 (for https://golang.org/cl/23444) Change-Id: Ieb769329531050b803528acce0c50d02786533b6 Reviewed-on: https://go-review.googlesource.com/25140 Reviewed-by: Alan Donovan <adonovan@google.com>
1 parent 5b59ce8 commit f328430

File tree

1 file changed

+64
-9
lines changed

1 file changed

+64
-9
lines changed

cmd/goimports/goimports.go

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package main
77
import (
88
"bufio"
99
"bytes"
10+
"errors"
1011
"flag"
1112
"fmt"
1213
"go/scanner"
@@ -29,7 +30,7 @@ var (
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

132170
func 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

Comments
 (0)