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

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

Issue 142360043: code review 142360043: go/format, cmd/gofmt: fix issues with partial Go code w... (Closed)
Left Patch Set: diff -r 48355ec55578b156b58c7df974acf132ab2e9d73 https://code.google.com/p/go Created 10 years, 5 months ago
Right Patch Set: diff -r 8f36a32a9d036f86fb956c8538142eb384bd1a4a https://code.google.com/p/go Created 10 years, 5 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:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « src/cmd/gofmt/testdata/stdin7.input ('k') | src/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
1 // Copyright 2012 The Go Authors. All rights reserved. 1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 // Package format implements standard formatting of Go source. 5 // Package format implements standard formatting of Go source.
6 package format 6 package format
7 7
8 import ( 8 import (
9 "bytes" 9 "bytes"
10 "fmt" 10 "fmt"
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
75 // or an (I/O or syntax) error. src is expected to be a syntactically 75 // or an (I/O or syntax) error. src is expected to be a syntactically
76 // correct Go source file, or a list of Go declarations or statements. 76 // correct Go source file, or a list of Go declarations or statements.
77 // 77 //
78 // If src is a partial source file, the leading and trailing space of src 78 // If src is a partial source file, the leading and trailing space of src
79 // is applied to the result (such that it has the same leading and trailing 79 // is applied to the result (such that it has the same leading and trailing
80 // space as src), and the result is indented by the same amount as the first 80 // space as src), and the result is indented by the same amount as the first
81 // line of src containing code. Imports are not sorted for partial source files. 81 // line of src containing code. Imports are not sorted for partial source files.
82 // 82 //
83 func Source(src []byte) ([]byte, error) { 83 func Source(src []byte) ([]byte, error) {
84 fset := token.NewFileSet() 84 fset := token.NewFileSet()
85 » file, adjust, err := parse(fset, "", src, true) 85 » file, sourceAdj, indentAdj, err := parse(fset, "", src, true)
86 if err != nil { 86 if err != nil {
87 return nil, err 87 return nil, err
88 } 88 }
89 89
90 » var res []byte 90 » return format(fset, file, sourceAdj, indentAdj, src)
91 » if adjust == nil {
92 » » // Complete source file.
93 » » ast.SortImports(fset, file)
94 » » var buf bytes.Buffer
95 » » err := config.Fprint(&buf, fset, file)
96 » » if err != nil {
97 » » » return nil, err
98 » » }
99 » » res = buf.Bytes()
100
101 » } else {
102 » » // Partial source file.
103 » » // Determine and prepend leading space.
104 » » i, j := 0, 0
105 » » for ; j < len(src) && isSpace(src[j]); j++ {
106 » » » if src[j] == '\n' {
107 » » » » i = j + 1 // index of last line in leading space
108 » » » }
109 » » }
110 » » res = append(res, src[:i]...)
111
112 » » // Determine and prepend indentation of first code line.
113 » » // Spaces are ignored unless there are no tabs,
114 » » // in which case spaces count as one tab.
115 » » indent := 0
116 » » hasSpace := false
117 » » for _, b := range src[i:j] {
118 » » » switch b {
119 » » » case ' ':
120 » » » » hasSpace = true
121 » » » case '\t':
122 » » » » indent++
123 » » » }
124 » » }
125 » » if indent == 0 && hasSpace {
126 » » » indent = 1
127 » » }
128 » » for i := 0; i < indent; i++ {
129 » » » res = append(res, '\t')
130 » » }
131
132 » » // Format the source.
133 » » // Write it without any leading and trailing space.
134 » » cfg := config
135 » » cfg.Indent = indent
136 » » var buf bytes.Buffer
137 » » err := cfg.Fprint(&buf, fset, file)
138 » » if err != nil {
139 » » » return nil, err
140 » » }
141 » » res = append(res, adjust(buf.Bytes(), indent)...)
142
143 » » // Determine and append trailing space.
144 » » i = len(src)
145 » » for i > 0 && isSpace(src[i-1]) {
146 » » » i--
147 » » }
148 » » res = append(res, src[i:]...)
149 » }
150
151 » return res, nil
152 } 91 }
153 92
154 func hasUnsortedImports(file *ast.File) bool { 93 func hasUnsortedImports(file *ast.File) bool {
155 for _, d := range file.Decls { 94 for _, d := range file.Decls {
156 d, ok := d.(*ast.GenDecl) 95 d, ok := d.(*ast.GenDecl)
157 if !ok || d.Tok != token.IMPORT { 96 if !ok || d.Tok != token.IMPORT {
158 // Not an import declaration, so we're done. 97 // Not an import declaration, so we're done.
159 // Imports are always first. 98 // Imports are always first.
160 return false 99 return false
161 } 100 }
162 if d.Lparen.IsValid() { 101 if d.Lparen.IsValid() {
163 // For now assume all grouped imports are unsorted. 102 // For now assume all grouped imports are unsorted.
164 // TODO(gri) Should check if they are sorted already. 103 // TODO(gri) Should check if they are sorted already.
165 return true 104 return true
166 } 105 }
167 // Ungrouped imports are sorted by default. 106 // Ungrouped imports are sorted by default.
168 } 107 }
169 return false 108 return false
170 } 109 }
171 110
172 // parse parses src, which was read from filename, 111 // parse parses src, which was read from filename,
173 // as a Go source file or statement list. 112 // as a Go source file or statement list.
174 func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.F ile, func(src []byte, indent int) []byte, error) { 113 func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) (
114 » file *ast.File,
115 » sourceAdj func(src []byte, indent int) []byte,
116 » indentAdj int,
117 » err error,
118 ) {
175 // Try as whole source file. 119 // Try as whole source file.
176 » file, err := parser.ParseFile(fset, filename, src, parserMode) 120 » file, err = parser.ParseFile(fset, filename, src, parserMode)
177 » if err == nil { 121 » // If there's no error, return. If the error is that the source file di dn't begin with a
178 » » return file, nil, nil 122 » // package line and source fragments are ok, fall through to
179 » }
180 » // If the error is that the source file didn't begin with a
181 » // package line and this is standard input, fall through to
182 // try as a source fragment. Stop and return on any other error. 123 // try as a source fragment. Stop and return on any other error.
183 » if !stdin || !strings.Contains(err.Error(), "expected 'package'") { 124 » if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") {
184 » » return nil, nil, err 125 » » return
185 } 126 }
186 127
187 // If this is a declaration list, make it a source file 128 // If this is a declaration list, make it a source file
188 // by inserting a package clause. 129 // by inserting a package clause.
189 // Insert using a ;, not a newline, so that the line numbers 130 // Insert using a ;, not a newline, so that the line numbers
190 // in psrc match the ones in src. 131 // in psrc match the ones in src.
191 psrc := append([]byte("package p;"), src...) 132 psrc := append([]byte("package p;"), src...)
192 file, err = parser.ParseFile(fset, filename, psrc, parserMode) 133 file, err = parser.ParseFile(fset, filename, psrc, parserMode)
193 if err == nil { 134 if err == nil {
194 » » adjust := func(src []byte, indent int) []byte { 135 » » sourceAdj = func(src []byte, indent int) []byte {
195 // Remove the package clause. 136 // Remove the package clause.
196 // Gofmt has turned the ; into a \n. 137 // Gofmt has turned the ; into a \n.
197 src = src[indent+len("package p\n"):] 138 src = src[indent+len("package p\n"):]
198 return bytes.TrimSpace(src) 139 return bytes.TrimSpace(src)
199 } 140 }
200 » » return file, adjust, nil 141 » » return
201 } 142 }
202 // If the error is that the source file didn't begin with a 143 // If the error is that the source file didn't begin with a
203 // declaration, fall through to try as a statement list. 144 // declaration, fall through to try as a statement list.
204 // Stop and return on any other error. 145 // Stop and return on any other error.
205 if !strings.Contains(err.Error(), "expected declaration") { 146 if !strings.Contains(err.Error(), "expected declaration") {
206 » » return nil, nil, err 147 » » return
207 } 148 }
208 149
209 // If this is a statement list, make it a source file 150 // If this is a statement list, make it a source file
210 // by inserting a package clause and turning the list 151 // by inserting a package clause and turning the list
211 // into a function body. This handles expressions too. 152 // into a function body. This handles expressions too.
212 // Insert using a ;, not a newline, so that the line numbers 153 // Insert using a ;, not a newline, so that the line numbers
213 // in fsrc match the ones in src. 154 // in fsrc match the ones in src.
214 fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '} ') 155 fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '} ')
215 file, err = parser.ParseFile(fset, filename, fsrc, parserMode) 156 file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
216 if err == nil { 157 if err == nil {
217 » » adjust := func(src []byte, indent int) []byte { 158 » » sourceAdj = func(src []byte, indent int) []byte {
159 » » » // Cap adjusted indent to zero.
160 » » » if indent < 0 {
161 » » » » indent = 0
162 » » » }
218 // Remove the wrapping. 163 // Remove the wrapping.
219 // Gofmt has turned the ; into a \n\n. 164 // Gofmt has turned the ; into a \n\n.
165 // There will be two non-blank lines with indent, hence 2*indent.
220 src = src[2*indent+len("package p\n\nfunc _() {"):] 166 src = src[2*indent+len("package p\n\nfunc _() {"):]
221 src = src[:len(src)-(indent+len("\n}\n"))] 167 src = src[:len(src)-(indent+len("\n}\n"))]
222 // Gofmt has also indented the function body one level.
223 // Remove that indent.
224 src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), - 1)
225 return bytes.TrimSpace(src) 168 return bytes.TrimSpace(src)
226 } 169 }
227 » » return file, adjust, nil 170 » » // Gofmt has also indented the function body one level.
228 » } 171 » » // Adjust that with indentAdj.
229 172 » » indentAdj = -1
230 » // Failed, and out of options. 173 » }
231 » return nil, nil, err 174
175 » // Succeeded, or out of options.
176 » return
177 }
178
179 func format(fset *token.FileSet, file *ast.File, sourceAdj func(src []byte, inde nt int) []byte, indentAdj int, src []byte) ([]byte, error) {
180 » if sourceAdj == nil {
181 » » // Complete source file.
182 » » ast.SortImports(fset, file)
183 » » var buf bytes.Buffer
184 » » err := config.Fprint(&buf, fset, file)
185 » » if err != nil {
186 » » » return nil, err
187 » » }
188 » » return buf.Bytes(), nil
189 » }
190
191 » // Partial source file.
192 » // Determine and prepend leading space.
193 » i, j := 0, 0
194 » for j < len(src) && isSpace(src[j]) {
195 » » if src[j] == '\n' {
196 » » » i = j + 1 // byte offset of last line in leading space
197 » » }
198 » » j++
199 » }
200 » var res []byte
201 » res = append(res, src[:i]...)
202
203 » // Determine and prepend indentation of first code line.
204 » // Spaces are ignored unless there are no tabs,
205 » // in which case spaces count as one tab.
206 » indent := 0
207 » hasSpace := false
208 » for _, b := range src[i:j] {
209 » » switch b {
210 » » case ' ':
211 » » » hasSpace = true
212 » » case '\t':
213 » » » indent++
214 » » }
215 » }
216 » if indent == 0 && hasSpace {
217 » » indent = 1
218 » }
219 » for i := 0; i < indent; i++ {
220 » » res = append(res, '\t')
221 » }
222
223 » // Format the source.
224 » // Write it without any leading and trailing space.
225 » cfg := config
226 » cfg.Indent = indent + indentAdj
227 » var buf bytes.Buffer
228 » err := cfg.Fprint(&buf, fset, file)
229 » if err != nil {
230 » » return nil, err
231 » }
232 » res = append(res, sourceAdj(buf.Bytes(), cfg.Indent)...)
233
234 » // Determine and append trailing space.
235 » i = len(src)
236 » for i > 0 && isSpace(src[i-1]) {
237 » » i--
238 » }
239 » return append(res, src[i:]...), nil
232 } 240 }
233 241
234 func isSpace(b byte) bool { 242 func isSpace(b byte) bool {
235 return b == ' ' || b == '\t' || b == '\n' || b == '\r' 243 return b == ' ' || b == '\t' || b == '\n' || b == '\r'
236 } 244 }
LEFTRIGHT

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