1 // Copyright 2009 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.
26 // main operation modes
27 list
= flag
.Bool("l", false, "list files whose formatting differs from gofmt's")
28 write
= flag
.Bool("w", false, "write result to (source) file instead of stdout")
29 rewriteRule
= flag
.String("r", "", "rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')")
30 simplifyAST
= flag
.Bool("s", false, "simplify code")
31 doDiff
= flag
.Bool("d", false, "display diffs instead of rewriting files")
32 allErrors
= flag
.Bool("e", false, "report all errors (not just the first 10 on different lines)")
35 cpuprofile
= flag
.String("cpuprofile", "", "write cpu profile to this file")
40 printerMode
= printer
.UseSpaces | printer
.TabIndent
44 fileSet
= token
.NewFileSet() // per process FileSet
46 rewrite
func(*ast
.File
) *ast
.File
47 parserMode parser
.Mode
50 func report(err error
) {
51 scanner
.PrintError(os
.Stderr
, err
)
56 fmt
.Fprintf(os
.Stderr
, "usage: gofmt [flags] [path ...]\n")
61 func initParserMode() {
62 parserMode
= parser
.ParseComments
64 parserMode |
= parser
.AllErrors
68 func isGoFile(f os
.FileInfo
) bool {
69 // ignore non-Go files
71 return !f
.IsDir() && !strings
.HasPrefix(name
, ".") && strings
.HasSuffix(name
, ".go")
74 // If in == nil, the source is the contents of the file with the given filename.
75 func processFile(filename
string, in io
.Reader
, out io
.Writer
, stdin
bool) error
{
77 f
, err
:= os
.Open(filename
)
85 src
, err
:= ioutil
.ReadAll(in
)
90 file
, sourceAdj
, indentAdj
, err
:= parse(fileSet
, filename
, src
, stdin
)
99 fmt
.Fprintf(os
.Stderr
, "warning: rewrite ignored for incomplete programs\n")
103 ast
.SortImports(fileSet
, file
)
109 res
, err
:= format(fileSet
, file
, sourceAdj
, indentAdj
, src
, printer
.Config
{Mode
: printerMode
, Tabwidth
: tabWidth
})
114 if !bytes
.Equal(src
, res
) {
115 // formatting has changed
117 fmt
.Fprintln(out
, filename
)
120 err
= ioutil
.WriteFile(filename
, res
, 0644)
126 data
, err
:= diff(src
, res
)
128 return fmt
.Errorf("computing diff: %s", err
)
130 fmt
.Printf("diff %s gofmt/%s\n", filename
, filename
)
135 if !*list
&& !*write
&& !*doDiff
{
136 _
, err
= out
.Write(res
)
142 func visitFile(path
string, f os
.FileInfo
, err error
) error
{
143 if err
== nil && isGoFile(f
) {
144 err
= processFile(path
, nil, os
.Stdout
, false)
152 func walkDir(path
string) {
153 filepath
.Walk(path
, visitFile
)
157 // call gofmtMain in a separate function
158 // so that it can use defer and have them
159 // run before the exit.
168 if *cpuprofile
!= "" {
169 f
, err
:= os
.Create(*cpuprofile
)
171 fmt
.Fprintf(os
.Stderr
, "creating cpu profile: %s\n", err
)
176 pprof
.StartCPUProfile(f
)
177 defer pprof
.StopCPUProfile()
183 if flag
.NArg() == 0 {
185 fmt
.Fprintln(os
.Stderr
, "error: cannot use -w with standard input")
189 if err
:= processFile("<standard input>", os
.Stdin
, os
.Stdout
, true); err
!= nil {
195 for i
:= 0; i
< flag
.NArg(); i
++ {
197 switch dir
, err
:= os
.Stat(path
); {
203 if err
:= processFile(path
, nil, os
.Stdout
, false); err
!= nil {
210 func diff(b1
, b2
[]byte) (data
[]byte, err error
) {
211 f1
, err
:= ioutil
.TempFile("", "gofmt")
215 defer os
.Remove(f1
.Name())
218 f2
, err
:= ioutil
.TempFile("", "gofmt")
222 defer os
.Remove(f2
.Name())
228 data
, err
= exec
.Command("diff", "-u", f1
.Name(), f2
.Name()).CombinedOutput()
230 // diff exits with a non-zero status when the files don't match.
231 // Ignore that failure as long as we get output.
238 // ----------------------------------------------------------------------------
241 // The functions parse, format, and isSpace below are identical to the
242 // respective functions in src/go/format/format.go - keep them in sync!
244 // TODO(gri) Factor out this functionality, eventually.
246 // parse parses src, which was read from the named file,
247 // as a Go source file, declaration, or statement list.
248 func parse(fset
*token
.FileSet
, filename
string, src
[]byte, fragmentOk
bool) (
250 sourceAdj
func(src
[]byte, indent
int) []byte,
254 // Try as whole source file.
255 file
, err
= parser
.ParseFile(fset
, filename
, src
, parserMode
)
256 // If there's no error, return. If the error is that the source file didn't begin with a
257 // package line and source fragments are ok, fall through to
258 // try as a source fragment. Stop and return on any other error.
259 if err
== nil ||
!fragmentOk ||
!strings
.Contains(err
.Error(), "expected 'package'") {
263 // If this is a declaration list, make it a source file
264 // by inserting a package clause.
265 // Insert using a ;, not a newline, so that the line numbers
266 // in psrc match the ones in src.
267 psrc
:= append([]byte("package p;"), src
...)
268 file
, err
= parser
.ParseFile(fset
, filename
, psrc
, parserMode
)
270 sourceAdj
= func(src
[]byte, indent
int) []byte {
271 // Remove the package clause.
272 // Gofmt has turned the ; into a \n.
273 src
= src
[indent
+len("package p\n"):]
274 return bytes
.TrimSpace(src
)
278 // If the error is that the source file didn't begin with a
279 // declaration, fall through to try as a statement list.
280 // Stop and return on any other error.
281 if !strings
.Contains(err
.Error(), "expected declaration") {
285 // If this is a statement list, make it a source file
286 // by inserting a package clause and turning the list
287 // into a function body. This handles expressions too.
288 // Insert using a ;, not a newline, so that the line numbers
289 // in fsrc match the ones in src.
290 fsrc
:= append(append([]byte("package p; func _() {"), src
...), '\n', '}')
291 file
, err
= parser
.ParseFile(fset
, filename
, fsrc
, parserMode
)
293 sourceAdj
= func(src
[]byte, indent
int) []byte {
294 // Cap adjusted indent to zero.
298 // Remove the wrapping.
299 // Gofmt has turned the ; into a \n\n.
300 // There will be two non-blank lines with indent, hence 2*indent.
301 src
= src
[2*indent
+len("package p\n\nfunc _() {"):]
302 src
= src
[:len(src
)-(indent
+len("\n}\n"))]
303 return bytes
.TrimSpace(src
)
305 // Gofmt has also indented the function body one level.
306 // Adjust that with indentAdj.
310 // Succeeded, or out of options.
314 // format formats the given package file originally obtained from src
315 // and adjusts the result based on the original source via sourceAdj
320 sourceAdj
func(src
[]byte, indent
int) []byte,
325 if sourceAdj
== nil {
326 // Complete source file.
328 err
:= cfg
.Fprint(&buf
, fset
, file
)
332 return buf
.Bytes(), nil
335 // Partial source file.
336 // Determine and prepend leading space.
338 for j
< len(src
) && isSpace(src
[j
]) {
340 i
= j
+ 1 // byte offset of last line in leading space
345 res
= append(res
, src
[:i
]...)
347 // Determine and prepend indentation of first code line.
348 // Spaces are ignored unless there are no tabs,
349 // in which case spaces count as one tab.
352 for _
, b
:= range src
[i
:j
] {
360 if indent
== 0 && hasSpace
{
363 for i
:= 0; i
< indent
; i
++ {
364 res
= append(res
, '\t')
367 // Format the source.
368 // Write it without any leading and trailing space.
369 cfg
.Indent
= indent
+ indentAdj
371 err
:= cfg
.Fprint(&buf
, fset
, file
)
375 res
= append(res
, sourceAdj(buf
.Bytes(), cfg
.Indent
)...)
377 // Determine and append trailing space.
379 for i
> 0 && isSpace(src
[i
-1]) {
382 return append(res
, src
[i
:]...), nil
385 func isSpace(b
byte) bool {
386 return b
== ' ' || b
== '\t' || b
== '\n' || b
== '\r'