Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(564)

Delta Between Two Patch Sets: src/pkg/go/format/format.go

Issue 6852075: code review 6852075: go/format: Package format implements standard formattin... (Closed)
Left Patch Set: Created 11 years, 4 months ago
Right Patch Set: diff -r 7646c94159a1 https://code.google.com/p/go Created 11 years, 4 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Right: Side by side diff | Download
« no previous file with change/comment | « src/cmd/godoc/template.go ('k') | src/pkg/go/format/format_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
(no file at all)
1 // Copyright 2012 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 format implements standard formatting of Go source.
6 package format
7
8 import (
9 "bytes"
10 "fmt"
11 "go/ast"
12 "go/parser"
13 "go/printer"
14 "go/token"
15 "io"
16 "strings"
17 )
18
19 var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidt h: 8}
20
21 // Node formats node in canonical gofmt style and writes the result to dst.
22 //
23 // The node type must be *ast.File, *printer.CommentedNode, []ast.Decl,
24 // []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec,
25 // or ast.Stmt. Node does not modify node. Imports are not sorted for
26 // nodes representing partial source files (i.e., if the node is not an
27 // *ast.File or a *printer.CommentedNode not wrapping an *ast.File).
28 //
29 // The function may return early (before the entire result is written)
30 // and return a formatting error, for instance due to an incorrect AST.
31 //
32 func Node(dst io.Writer, fset *token.FileSet, node interface{}) error {
33 // Determine if we have a complete source file (file != nil).
34 var file *ast.File
35 var cnode *printer.CommentedNode
36 switch n := node.(type) {
37 case *ast.File:
38 file = n
39 case *printer.CommentedNode:
40 if f, ok := n.Node.(*ast.File); ok {
41 file = f
42 cnode = n
43 }
44 }
45
46 // Sort imports if necessary.
47 if file != nil && hasUnsortedImports(file) {
48 // Make a copy of the AST because ast.SortImports is destructive .
49 // TODO(gri) Do this more efficently.
50 var buf bytes.Buffer
51 err := config.Fprint(&buf, fset, file)
52 if err != nil {
53 return err
54 }
55 file, err = parser.ParseFile(fset, "", buf.Bytes(), parser.Parse Comments)
56 if err != nil {
57 // We should never get here. If we do, provide good diag nostic.
58 return fmt.Errorf("format.Node internal error (%s)", err )
59 }
60 ast.SortImports(fset, file)
61
62 // Use new file with sorted imports.
63 node = file
64 if cnode != nil {
65 node = &printer.CommentedNode{Node: file, Comments: cnod e.Comments}
66 }
67 }
68
69 return config.Fprint(dst, fset, node)
70 }
71
72 // Source formats src in canonical gofmt style and writes the result to dst
73 // or returns an I/O or syntax error. src is expected to be a syntactically
74 // correct Go source file, or a list of Go declarations or statements.
75 //
76 // If src is a partial source file, the leading and trailing space of src
77 // is applied to the result (such that it has the same leading and trailing
78 // space as src), and the formatted src is indented by the same amount as
79 // the first line of src containing code. Imports are not sorted for partial
80 // source files.
81 //
82 func Source(src []byte) ([]byte, error) {
83 fset := token.NewFileSet()
84 node, err := parse(fset, src)
85 if err != nil {
86 return nil, err
87 }
88
89 var buf bytes.Buffer
90 if file, ok := node.(*ast.File); ok {
91 // Complete source file.
92 ast.SortImports(fset, file)
93 err := config.Fprint(&buf, fset, file)
94 if err != nil {
95 return nil, err
96 }
97
98 } else {
99 // Partial source file.
100 // Determine and prepend leading space.
101 i, j := 0, 0
102 for j < len(src) && isSpace(src[j]) {
103 if src[j] == '\n' {
104 i = j + 1 // index of last line in leading space
105 }
106 j++
107 }
108 buf.Write(src[:i])
109
110 // Determine indentation of first code line.
111 // Spaces are ignored unless there are no tabs,
112 // in which case spaces count as one tab.
113 indent := 0
114 hasSpace := false
115 for _, b := range src[i:j] {
116 switch b {
117 case ' ':
118 hasSpace = true
119 case '\t':
120 indent++
121 }
122 }
123 if indent == 0 && hasSpace {
124 indent = 1
125 }
126
127 // Format the source.
128 cfg := config
129 cfg.Indent = indent
130 err := cfg.Fprint(&buf, fset, node)
131 if err != nil {
132 return nil, err
133 }
134
135 // Determine and append trailing space.
136 i = len(src)
137 for i > 0 && isSpace(src[i-1]) {
138 i--
139 }
140 buf.Write(src[i:])
141 }
142
143 return buf.Bytes(), nil
144 }
145
146 func hasUnsortedImports(file *ast.File) bool {
147 for _, d := range file.Decls {
148 d, ok := d.(*ast.GenDecl)
149 if !ok || d.Tok != token.IMPORT {
150 // Not an import declaration, so we're done.
151 // Imports are always first.
152 return false
153 }
154 if d.Lparen.IsValid() {
155 // For now assume all grouped imports are unsorted.
156 // TODO(gri) Should check if they are sorted already.
157 return true
158 }
159 // Ungrouped imports are sorted by default.
160 }
161 return false
162 }
163
164 func isSpace(b byte) bool {
165 return b == ' ' || b == '\t' || b == '\n' || b == '\r'
166 }
167
168 func parse(fset *token.FileSet, src []byte) (interface{}, error) {
169 // Try as a complete source file.
170 file, err := parser.ParseFile(fset, "", src, parser.ParseComments)
171 if err == nil {
172 return file, nil
173 }
174 // If the source is missing a package clause, try as a source fragment; otherwise fail.
175 if !strings.Contains(err.Error(), "expected 'package'") {
176 return nil, err
177 }
178
179 // Try as a declaration list by prepending a package clause in front of src.
180 // Use ';' not '\n' to keep line numbers intact.
181 psrc := append([]byte("package p;"), src...)
182 file, err = parser.ParseFile(fset, "", psrc, parser.ParseComments)
183 if err == nil {
184 return file.Decls, nil
185 }
186 // If the source is missing a declaration, try as a statement list; othe rwise fail.
187 if !strings.Contains(err.Error(), "expected declaration") {
188 return nil, err
189 }
190
191 // Try as statement list by wrapping a function around src.
192 fsrc := append(append([]byte("package p; func _() {"), src...), '}')
193 file, err = parser.ParseFile(fset, "", fsrc, parser.ParseComments)
194 if err == nil {
195 return file.Decls[0].(*ast.FuncDecl).Body.List, nil
196 }
197
198 // Failed, and out of options.
199 return nil, err
200 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b