tangled
alpha
login
or
join now
fuwn.net
/
iku
0
fork
atom
๐ Grammar-Aware Code Formatter: Structure through separation (supports Go, JavaScript, TypeScript, JSX, and TSX)
go
formatter
code-formatter
javascript
typescript
jsx
tsx
0
fork
atom
overview
issues
pulls
pipelines
refactor: Rename variables to be fully self-documenting
fuwn.net
1 month ago
e9374544
2e53255d
verified
This commit was signed with the committer's
known signature
.
fuwn.net
SSH Key Fingerprint:
SHA256:VPdFPyPbd6JkoMyWUdZ/kkTcIAt3sxjXD2XSAZ7FYC4=
+309
-309
4 changed files
expand all
collapse all
unified
split
formatter.go
formatter_bench_test.go
formatter_test.go
main.go
+146
-146
formatter.go
···
16
16
caseLabelPattern = regexp.MustCompile(`^\s*(case\s|default\s*:)|(^\s+.*:\s*$)`)
17
17
)
18
18
19
19
-
func isCommentOnly(line string) bool {
20
20
-
for index := range len(line) {
21
21
-
character := line[index]
19
19
+
func isCommentOnly(sourceLine string) bool {
20
20
+
for characterIndex := range len(sourceLine) {
21
21
+
character := sourceLine[characterIndex]
22
22
23
23
if character == ' ' || character == '\t' {
24
24
continue
25
25
}
26
26
27
27
-
return len(line) > index+1 && line[index] == '/' && line[index+1] == '/'
27
27
+
return len(sourceLine) > characterIndex+1 && sourceLine[characterIndex] == '/' && sourceLine[characterIndex+1] == '/'
28
28
}
29
29
30
30
return false
31
31
}
32
32
33
33
-
func isPackageLine(trimmed string) bool {
34
34
-
return len(trimmed) > 8 && trimmed[:8] == "package "
33
33
+
func isPackageLine(trimmedLine string) bool {
34
34
+
return len(trimmedLine) > 8 && trimmedLine[:8] == "package "
35
35
}
36
36
37
37
-
func countRawStringDelimiters(line string) int {
38
38
-
count := 0
39
39
-
inString := false
40
40
-
inCharacter := false
37
37
+
func countRawStringDelimiters(sourceLine string) int {
38
38
+
delimiterCount := 0
39
39
+
insideDoubleQuotedString := false
40
40
+
insideCharacterLiteral := false
41
41
42
42
-
for index := 0; index < len(line); index++ {
43
43
-
character := line[index]
42
42
+
for characterIndex := 0; characterIndex < len(sourceLine); characterIndex++ {
43
43
+
character := sourceLine[characterIndex]
44
44
45
45
-
if inCharacter {
46
46
-
if character == '\\' && index+1 < len(line) {
47
47
-
index++
45
45
+
if insideCharacterLiteral {
46
46
+
if character == '\\' && characterIndex+1 < len(sourceLine) {
47
47
+
characterIndex++
48
48
49
49
continue
50
50
}
51
51
52
52
if character == '\'' {
53
53
-
inCharacter = false
53
53
+
insideCharacterLiteral = false
54
54
}
55
55
56
56
continue
57
57
}
58
58
59
59
-
if inString {
60
60
-
if character == '\\' && index+1 < len(line) {
61
61
-
index++
59
59
+
if insideDoubleQuotedString {
60
60
+
if character == '\\' && characterIndex+1 < len(sourceLine) {
61
61
+
characterIndex++
62
62
63
63
continue
64
64
}
65
65
66
66
if character == '"' {
67
67
-
inString = false
67
67
+
insideDoubleQuotedString = false
68
68
}
69
69
70
70
continue
71
71
}
72
72
73
73
if character == '\'' {
74
74
-
inCharacter = true
74
74
+
insideCharacterLiteral = true
75
75
76
76
continue
77
77
}
78
78
79
79
if character == '"' {
80
80
-
inString = true
80
80
+
insideDoubleQuotedString = true
81
81
82
82
continue
83
83
}
84
84
85
85
if character == '`' {
86
86
-
count++
86
86
+
delimiterCount++
87
87
}
88
88
}
89
89
90
90
-
return count
90
90
+
return delimiterCount
91
91
}
92
92
93
93
type CommentMode int
···
102
102
CommentMode CommentMode
103
103
}
104
104
105
105
-
type lineInfo struct {
105
105
+
type lineInformation struct {
106
106
statementType string
107
107
isTopLevel bool
108
108
isScoped bool
···
110
110
}
111
111
112
112
func (f *Formatter) Format(source []byte) ([]byte, error) {
113
113
-
formatted, err := format.Source(source)
113
113
+
formattedSource, err := format.Source(source)
114
114
115
115
if err != nil {
116
116
return nil, err
117
117
}
118
118
119
119
-
fileSet := token.NewFileSet()
120
120
-
file, err := parser.ParseFile(fileSet, "", formatted, parser.ParseComments)
119
119
+
tokenFileSet := token.NewFileSet()
120
120
+
parsedFile, err := parser.ParseFile(tokenFileSet, "", formattedSource, parser.ParseComments)
121
121
122
122
if err != nil {
123
123
return nil, err
124
124
}
125
125
126
126
-
lineInfoMap := f.buildLineInfo(fileSet, file)
126
126
+
lineInformationMap := f.buildLineInfo(tokenFileSet, parsedFile)
127
127
128
128
-
return f.rewrite(formatted, lineInfoMap), nil
128
128
+
return f.rewrite(formattedSource, lineInformationMap), nil
129
129
}
130
130
131
131
-
func isGenDeclScoped(genDecl *ast.GenDecl) bool {
132
132
-
for _, spec := range genDecl.Specs {
133
133
-
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
134
134
-
switch typeSpec.Type.(type) {
131
131
+
func isGeneralDeclarationScoped(generalDeclaration *ast.GenDecl) bool {
132
132
+
for _, specification := range generalDeclaration.Specs {
133
133
+
if typeSpecification, isTypeSpecification := specification.(*ast.TypeSpec); isTypeSpecification {
134
134
+
switch typeSpecification.Type.(type) {
135
135
case *ast.StructType, *ast.InterfaceType:
136
136
return true
137
137
}
···
141
141
return false
142
142
}
143
143
144
144
-
func (f *Formatter) buildLineInfo(fileSet *token.FileSet, file *ast.File) map[int]*lineInfo {
145
145
-
lineInfoMap := make(map[int]*lineInfo)
146
146
-
tokenFile := fileSet.File(file.Pos())
144
144
+
func (f *Formatter) buildLineInfo(tokenFileSet *token.FileSet, parsedFile *ast.File) map[int]*lineInformation {
145
145
+
lineInformationMap := make(map[int]*lineInformation)
146
146
+
tokenFile := tokenFileSet.File(parsedFile.Pos())
147
147
148
148
if tokenFile == nil {
149
149
-
return lineInfoMap
149
149
+
return lineInformationMap
150
150
}
151
151
152
152
-
for _, declaration := range file.Decls {
152
152
+
for _, declaration := range parsedFile.Decls {
153
153
startLine := tokenFile.Line(declaration.Pos())
154
154
endLine := tokenFile.Line(declaration.End())
155
155
-
typeName := ""
155
155
+
statementType := ""
156
156
isScoped := false
157
157
158
158
-
switch declarationType := declaration.(type) {
158
158
+
switch typedDeclaration := declaration.(type) {
159
159
case *ast.GenDecl:
160
160
-
typeName = declarationType.Tok.String()
161
161
-
isScoped = isGenDeclScoped(declarationType)
160
160
+
statementType = typedDeclaration.Tok.String()
161
161
+
isScoped = isGeneralDeclarationScoped(typedDeclaration)
162
162
case *ast.FuncDecl:
163
163
-
typeName = "func"
163
163
+
statementType = "func"
164
164
isScoped = true
165
165
default:
166
166
-
typeName = reflect.TypeOf(declaration).String()
166
166
+
statementType = reflect.TypeOf(declaration).String()
167
167
}
168
168
169
169
-
lineInfoMap[startLine] = &lineInfo{statementType: typeName, isTopLevel: true, isScoped: isScoped, isStartLine: true}
170
170
-
lineInfoMap[endLine] = &lineInfo{statementType: typeName, isTopLevel: true, isScoped: isScoped, isStartLine: false}
169
169
+
lineInformationMap[startLine] = &lineInformation{statementType: statementType, isTopLevel: true, isScoped: isScoped, isStartLine: true}
170
170
+
lineInformationMap[endLine] = &lineInformation{statementType: statementType, isTopLevel: true, isScoped: isScoped, isStartLine: false}
171
171
}
172
172
173
173
-
ast.Inspect(file, func(node ast.Node) bool {
174
174
-
if node == nil {
173
173
+
ast.Inspect(parsedFile, func(astNode ast.Node) bool {
174
174
+
if astNode == nil {
175
175
return true
176
176
}
177
177
178
178
-
switch typedNode := node.(type) {
178
178
+
switch typedNode := astNode.(type) {
179
179
case *ast.BlockStmt:
180
180
-
f.processBlock(tokenFile, typedNode, lineInfoMap)
180
180
+
f.processBlock(tokenFile, typedNode, lineInformationMap)
181
181
case *ast.CaseClause:
182
182
-
f.processStatementList(tokenFile, typedNode.Body, lineInfoMap)
182
182
+
f.processStatementList(tokenFile, typedNode.Body, lineInformationMap)
183
183
case *ast.CommClause:
184
184
-
f.processStatementList(tokenFile, typedNode.Body, lineInfoMap)
184
184
+
f.processStatementList(tokenFile, typedNode.Body, lineInformationMap)
185
185
}
186
186
187
187
return true
188
188
})
189
189
190
190
-
return lineInfoMap
190
190
+
return lineInformationMap
191
191
}
192
192
193
193
-
func (f *Formatter) processBlock(tokenFile *token.File, block *ast.BlockStmt, lineInfoMap map[int]*lineInfo) {
194
194
-
if block == nil {
193
193
+
func (f *Formatter) processBlock(tokenFile *token.File, blockStatement *ast.BlockStmt, lineInformationMap map[int]*lineInformation) {
194
194
+
if blockStatement == nil {
195
195
return
196
196
}
197
197
198
198
-
f.processStatementList(tokenFile, block.List, lineInfoMap)
198
198
+
f.processStatementList(tokenFile, blockStatement.List, lineInformationMap)
199
199
}
200
200
201
201
-
func (f *Formatter) processStatementList(tokenFile *token.File, statements []ast.Stmt, lineInfoMap map[int]*lineInfo) {
201
201
+
func (f *Formatter) processStatementList(tokenFile *token.File, statements []ast.Stmt, lineInformationMap map[int]*lineInformation) {
202
202
for _, statement := range statements {
203
203
startLine := tokenFile.Line(statement.Pos())
204
204
endLine := tokenFile.Line(statement.End())
205
205
-
typeName := ""
205
205
+
statementType := ""
206
206
isScoped := false
207
207
208
208
-
switch statementType := statement.(type) {
208
208
+
switch typedStatement := statement.(type) {
209
209
case *ast.DeclStmt:
210
210
-
if genericDeclaration, ok := statementType.Decl.(*ast.GenDecl); ok {
211
211
-
typeName = genericDeclaration.Tok.String()
210
210
+
if generalDeclaration, isGeneralDeclaration := typedStatement.Decl.(*ast.GenDecl); isGeneralDeclaration {
211
211
+
statementType = generalDeclaration.Tok.String()
212
212
} else {
213
213
-
typeName = reflect.TypeOf(statement).String()
213
213
+
statementType = reflect.TypeOf(statement).String()
214
214
}
215
215
case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt,
216
216
*ast.TypeSwitchStmt, *ast.SelectStmt, *ast.BlockStmt:
217
217
-
typeName = reflect.TypeOf(statement).String()
217
217
+
statementType = reflect.TypeOf(statement).String()
218
218
isScoped = true
219
219
default:
220
220
-
typeName = reflect.TypeOf(statement).String()
220
220
+
statementType = reflect.TypeOf(statement).String()
221
221
}
222
222
223
223
-
existingStart := lineInfoMap[startLine]
223
223
+
existingStart := lineInformationMap[startLine]
224
224
225
225
if existingStart == nil || !existingStart.isStartLine {
226
226
-
lineInfoMap[startLine] = &lineInfo{statementType: typeName, isTopLevel: false, isScoped: isScoped, isStartLine: true}
226
226
+
lineInformationMap[startLine] = &lineInformation{statementType: statementType, isTopLevel: false, isScoped: isScoped, isStartLine: true}
227
227
}
228
228
229
229
-
existingEnd := lineInfoMap[endLine]
229
229
+
existingEnd := lineInformationMap[endLine]
230
230
231
231
if existingEnd == nil || !existingEnd.isStartLine {
232
232
-
lineInfoMap[endLine] = &lineInfo{statementType: typeName, isTopLevel: false, isScoped: isScoped, isStartLine: false}
232
232
+
lineInformationMap[endLine] = &lineInformation{statementType: statementType, isTopLevel: false, isScoped: isScoped, isStartLine: false}
233
233
}
234
234
235
235
switch typedStatement := statement.(type) {
236
236
case *ast.IfStmt:
237
237
-
f.processBlock(tokenFile, typedStatement.Body, lineInfoMap)
237
237
+
f.processBlock(tokenFile, typedStatement.Body, lineInformationMap)
238
238
239
239
if typedStatement.Else != nil {
240
240
if elseBlock, isBlockStatement := typedStatement.Else.(*ast.BlockStmt); isBlockStatement {
241
241
-
f.processBlock(tokenFile, elseBlock, lineInfoMap)
241
241
+
f.processBlock(tokenFile, elseBlock, lineInformationMap)
242
242
} else if elseIfStatement, isIfStatement := typedStatement.Else.(*ast.IfStmt); isIfStatement {
243
243
-
f.processIfStatement(tokenFile, elseIfStatement, lineInfoMap)
243
243
+
f.processIfStatement(tokenFile, elseIfStatement, lineInformationMap)
244
244
}
245
245
}
246
246
case *ast.ForStmt:
247
247
-
f.processBlock(tokenFile, typedStatement.Body, lineInfoMap)
247
247
+
f.processBlock(tokenFile, typedStatement.Body, lineInformationMap)
248
248
case *ast.RangeStmt:
249
249
-
f.processBlock(tokenFile, typedStatement.Body, lineInfoMap)
249
249
+
f.processBlock(tokenFile, typedStatement.Body, lineInformationMap)
250
250
case *ast.SwitchStmt:
251
251
-
f.processBlock(tokenFile, typedStatement.Body, lineInfoMap)
251
251
+
f.processBlock(tokenFile, typedStatement.Body, lineInformationMap)
252
252
case *ast.TypeSwitchStmt:
253
253
-
f.processBlock(tokenFile, typedStatement.Body, lineInfoMap)
253
253
+
f.processBlock(tokenFile, typedStatement.Body, lineInformationMap)
254
254
case *ast.SelectStmt:
255
255
-
f.processBlock(tokenFile, typedStatement.Body, lineInfoMap)
255
255
+
f.processBlock(tokenFile, typedStatement.Body, lineInformationMap)
256
256
case *ast.BlockStmt:
257
257
-
f.processBlock(tokenFile, typedStatement, lineInfoMap)
257
257
+
f.processBlock(tokenFile, typedStatement, lineInformationMap)
258
258
}
259
259
}
260
260
}
261
261
262
262
-
func (f *Formatter) processIfStatement(tokenFile *token.File, ifStatement *ast.IfStmt, lineInfoMap map[int]*lineInfo) {
262
262
+
func (f *Formatter) processIfStatement(tokenFile *token.File, ifStatement *ast.IfStmt, lineInformationMap map[int]*lineInformation) {
263
263
startLine := tokenFile.Line(ifStatement.Pos())
264
264
endLine := tokenFile.Line(ifStatement.End())
265
265
-
existingStart := lineInfoMap[startLine]
265
265
+
existingStart := lineInformationMap[startLine]
266
266
267
267
if existingStart == nil || !existingStart.isStartLine {
268
268
-
lineInfoMap[startLine] = &lineInfo{statementType: "*ast.IfStmt", isTopLevel: false, isScoped: true, isStartLine: true}
268
268
+
lineInformationMap[startLine] = &lineInformation{statementType: "*ast.IfStmt", isTopLevel: false, isScoped: true, isStartLine: true}
269
269
}
270
270
271
271
-
existingEnd := lineInfoMap[endLine]
271
271
+
existingEnd := lineInformationMap[endLine]
272
272
273
273
if existingEnd == nil || !existingEnd.isStartLine {
274
274
-
lineInfoMap[endLine] = &lineInfo{statementType: "*ast.IfStmt", isTopLevel: false, isScoped: true, isStartLine: false}
274
274
+
lineInformationMap[endLine] = &lineInformation{statementType: "*ast.IfStmt", isTopLevel: false, isScoped: true, isStartLine: false}
275
275
}
276
276
277
277
-
f.processBlock(tokenFile, ifStatement.Body, lineInfoMap)
277
277
+
f.processBlock(tokenFile, ifStatement.Body, lineInformationMap)
278
278
279
279
if ifStatement.Else != nil {
280
280
if elseBlock, isBlockStatement := ifStatement.Else.(*ast.BlockStmt); isBlockStatement {
281
281
-
f.processBlock(tokenFile, elseBlock, lineInfoMap)
281
281
+
f.processBlock(tokenFile, elseBlock, lineInformationMap)
282
282
} else if elseIfStatement, isIfStatement := ifStatement.Else.(*ast.IfStmt); isIfStatement {
283
283
-
f.processIfStatement(tokenFile, elseIfStatement, lineInfoMap)
283
283
+
f.processIfStatement(tokenFile, elseIfStatement, lineInformationMap)
284
284
}
285
285
}
286
286
}
287
287
288
288
-
func (f *Formatter) rewrite(source []byte, lineInfoMap map[int]*lineInfo) []byte {
289
289
-
lines := strings.Split(string(source), "\n")
290
290
-
result := make([]string, 0, len(lines))
288
288
+
func (f *Formatter) rewrite(formattedSource []byte, lineInformationMap map[int]*lineInformation) []byte {
289
289
+
sourceLines := strings.Split(string(formattedSource), "\n")
290
290
+
resultLines := make([]string, 0, len(sourceLines))
291
291
previousWasOpenBrace := false
292
292
-
previousType := ""
292
292
+
previousStatementType := ""
293
293
previousWasComment := false
294
294
previousWasTopLevel := false
295
295
previousWasScoped := false
296
296
insideRawString := false
297
297
298
298
-
for index, line := range lines {
299
299
-
backtickCount := countRawStringDelimiters(line)
298
298
+
for lineIndex, currentLine := range sourceLines {
299
299
+
backtickCount := countRawStringDelimiters(currentLine)
300
300
wasInsideRawString := insideRawString
301
301
302
302
if backtickCount%2 == 1 {
···
304
304
}
305
305
306
306
if wasInsideRawString {
307
307
-
result = append(result, line)
307
307
+
resultLines = append(resultLines, currentLine)
308
308
309
309
continue
310
310
}
311
311
312
312
-
lineNumber := index + 1
313
313
-
trimmed := strings.TrimSpace(line)
312
312
+
lineNumber := lineIndex + 1
313
313
+
trimmedLine := strings.TrimSpace(currentLine)
314
314
315
315
-
if trimmed == "" {
315
315
+
if trimmedLine == "" {
316
316
continue
317
317
}
318
318
319
319
-
isClosingBrace := closingBracePattern.MatchString(line)
320
320
-
isOpeningBrace := openingBracePattern.MatchString(line)
321
321
-
isCaseLabel := caseLabelPattern.MatchString(line)
322
322
-
isCommentOnlyLine := isCommentOnly(line)
323
323
-
isPackageLine := isPackageLine(trimmed)
324
324
-
info := lineInfoMap[lineNumber]
325
325
-
currentType := ""
319
319
+
isClosingBrace := closingBracePattern.MatchString(currentLine)
320
320
+
isOpeningBrace := openingBracePattern.MatchString(currentLine)
321
321
+
isCaseLabel := caseLabelPattern.MatchString(currentLine)
322
322
+
isCommentOnlyLine := isCommentOnly(currentLine)
323
323
+
isPackageDeclaration := isPackageLine(trimmedLine)
324
324
+
currentInformation := lineInformationMap[lineNumber]
325
325
+
currentStatementType := ""
326
326
327
327
-
if info != nil {
328
328
-
currentType = info.statementType
327
327
+
if currentInformation != nil {
328
328
+
currentStatementType = currentInformation.statementType
329
329
}
330
330
331
331
-
if isPackageLine {
332
332
-
currentType = "package"
331
331
+
if isPackageDeclaration {
332
332
+
currentStatementType = "package"
333
333
}
334
334
335
335
-
needsBlank := false
336
336
-
currentIsTopLevel := info != nil && info.isTopLevel
337
337
-
currentIsScoped := info != nil && info.isScoped
335
335
+
needsBlankLine := false
336
336
+
currentIsTopLevel := currentInformation != nil && currentInformation.isTopLevel
337
337
+
currentIsScoped := currentInformation != nil && currentInformation.isScoped
338
338
339
339
-
if len(result) > 0 && !previousWasOpenBrace && !isClosingBrace && !isCaseLabel {
340
340
-
if currentIsTopLevel && previousWasTopLevel && currentType != previousType {
339
339
+
if len(resultLines) > 0 && !previousWasOpenBrace && !isClosingBrace && !isCaseLabel {
340
340
+
if currentIsTopLevel && previousWasTopLevel && currentStatementType != previousStatementType {
341
341
if f.CommentMode == CommentsFollow && previousWasComment {
342
342
} else {
343
343
-
needsBlank = true
343
343
+
needsBlankLine = true
344
344
}
345
345
-
} else if info != nil && (currentIsScoped || previousWasScoped) {
345
345
+
} else if currentInformation != nil && (currentIsScoped || previousWasScoped) {
346
346
if f.CommentMode == CommentsFollow && previousWasComment {
347
347
} else {
348
348
-
needsBlank = true
348
348
+
needsBlankLine = true
349
349
}
350
350
-
} else if currentType != "" && previousType != "" && currentType != previousType {
350
350
+
} else if currentStatementType != "" && previousStatementType != "" && currentStatementType != previousStatementType {
351
351
if f.CommentMode == CommentsFollow && previousWasComment {
352
352
} else {
353
353
-
needsBlank = true
353
353
+
needsBlankLine = true
354
354
}
355
355
}
356
356
357
357
if f.CommentMode == CommentsFollow && isCommentOnlyLine && !previousWasComment {
358
358
-
nextLineNumber := f.findNextNonCommentLine(lines, index+1)
358
358
+
nextLineNumber := f.findNextNonCommentLine(sourceLines, lineIndex+1)
359
359
360
360
if nextLineNumber > 0 {
361
361
-
nextInfo := lineInfoMap[nextLineNumber]
361
361
+
nextInformation := lineInformationMap[nextLineNumber]
362
362
363
363
-
if nextInfo != nil {
364
364
-
nextIsTopLevel := nextInfo.isTopLevel
365
365
-
nextIsScoped := nextInfo.isScoped
363
363
+
if nextInformation != nil {
364
364
+
nextIsTopLevel := nextInformation.isTopLevel
365
365
+
nextIsScoped := nextInformation.isScoped
366
366
367
367
-
if nextIsTopLevel && previousWasTopLevel && nextInfo.statementType != previousType {
368
368
-
needsBlank = true
367
367
+
if nextIsTopLevel && previousWasTopLevel && nextInformation.statementType != previousStatementType {
368
368
+
needsBlankLine = true
369
369
} else if nextIsScoped || previousWasScoped {
370
370
-
needsBlank = true
371
371
-
} else if nextInfo.statementType != "" && previousType != "" && nextInfo.statementType != previousType {
372
372
-
needsBlank = true
370
370
+
needsBlankLine = true
371
371
+
} else if nextInformation.statementType != "" && previousStatementType != "" && nextInformation.statementType != previousStatementType {
372
372
+
needsBlankLine = true
373
373
}
374
374
}
375
375
}
376
376
}
377
377
}
378
378
379
379
-
if needsBlank {
380
380
-
result = append(result, "")
379
379
+
if needsBlankLine {
380
380
+
resultLines = append(resultLines, "")
381
381
}
382
382
383
383
-
result = append(result, line)
383
383
+
resultLines = append(resultLines, currentLine)
384
384
previousWasOpenBrace = isOpeningBrace || isCaseLabel
385
385
previousWasComment = isCommentOnlyLine
386
386
387
387
-
if info != nil {
388
388
-
previousType = info.statementType
389
389
-
previousWasTopLevel = info.isTopLevel
390
390
-
previousWasScoped = info.isScoped
391
391
-
} else if currentType != "" {
392
392
-
previousType = currentType
387
387
+
if currentInformation != nil {
388
388
+
previousStatementType = currentInformation.statementType
389
389
+
previousWasTopLevel = currentInformation.isTopLevel
390
390
+
previousWasScoped = currentInformation.isScoped
391
391
+
} else if currentStatementType != "" {
392
392
+
previousStatementType = currentStatementType
393
393
previousWasTopLevel = false
394
394
previousWasScoped = false
395
395
}
396
396
}
397
397
398
398
-
output := strings.Join(result, "\n")
398
398
+
outputString := strings.Join(resultLines, "\n")
399
399
400
400
-
if !strings.HasSuffix(output, "\n") {
401
401
-
output += "\n"
400
400
+
if !strings.HasSuffix(outputString, "\n") {
401
401
+
outputString += "\n"
402
402
}
403
403
404
404
-
return []byte(output)
404
404
+
return []byte(outputString)
405
405
}
406
406
407
407
-
func (f *Formatter) findNextNonCommentLine(lines []string, startIndex int) int {
408
408
-
for index := startIndex; index < len(lines); index++ {
409
409
-
trimmed := strings.TrimSpace(lines[index])
407
407
+
func (f *Formatter) findNextNonCommentLine(sourceLines []string, startLineIndex int) int {
408
408
+
for lineIndex := startLineIndex; lineIndex < len(sourceLines); lineIndex++ {
409
409
+
trimmedLine := strings.TrimSpace(sourceLines[lineIndex])
410
410
411
411
-
if trimmed == "" {
411
411
+
if trimmedLine == "" {
412
412
continue
413
413
}
414
414
415
415
-
if isCommentOnly(lines[index]) {
415
415
+
if isCommentOnly(sourceLines[lineIndex]) {
416
416
continue
417
417
}
418
418
419
419
-
return index + 1
419
419
+
return lineIndex + 1
420
420
}
421
421
422
422
return 0
+22
-22
formatter_bench_test.go
···
5
5
"testing"
6
6
)
7
7
8
8
-
func BenchmarkFormat_Small(b *testing.B) {
9
9
-
input := []byte(`package main
8
8
+
func BenchmarkFormat_Small(benchmarkRunner *testing.B) {
9
9
+
inputSource := []byte(`package main
10
10
func main() {
11
11
x := 1
12
12
y := 2
···
16
16
a := 4
17
17
}
18
18
`)
19
19
-
f := &Formatter{CommentMode: CommentsFollow}
19
19
+
formatter := &Formatter{CommentMode: CommentsFollow}
20
20
21
21
-
for b.Loop() {
22
22
-
_, _ = f.Format(input)
21
21
+
for benchmarkRunner.Loop() {
22
22
+
_, _ = formatter.Format(inputSource)
23
23
}
24
24
}
25
25
26
26
-
func BenchmarkFormat_Large(b *testing.B) {
27
27
-
var sb strings.Builder
26
26
+
func BenchmarkFormat_Large(benchmarkRunner *testing.B) {
27
27
+
var sourceBuilder strings.Builder
28
28
29
29
-
sb.WriteString("package main\n\n")
29
29
+
sourceBuilder.WriteString("package main\n\n")
30
30
31
31
-
for i := range 100 {
32
32
-
sb.WriteString("func foo")
33
33
-
sb.WriteString(string(rune('A' + i%26)))
34
34
-
sb.WriteString("() {\n")
35
35
-
sb.WriteString("\tx := 1\n")
36
36
-
sb.WriteString("\tif x > 0 {\n")
37
37
-
sb.WriteString("\t\ty := 2\n")
38
38
-
sb.WriteString("\t}\n")
39
39
-
sb.WriteString("\tz := 3\n")
40
40
-
sb.WriteString("}\n\n")
31
31
+
for functionIndex := range 100 {
32
32
+
sourceBuilder.WriteString("func foo")
33
33
+
sourceBuilder.WriteString(string(rune('A' + functionIndex%26)))
34
34
+
sourceBuilder.WriteString("() {\n")
35
35
+
sourceBuilder.WriteString("\tx := 1\n")
36
36
+
sourceBuilder.WriteString("\tif x > 0 {\n")
37
37
+
sourceBuilder.WriteString("\t\ty := 2\n")
38
38
+
sourceBuilder.WriteString("\t}\n")
39
39
+
sourceBuilder.WriteString("\tz := 3\n")
40
40
+
sourceBuilder.WriteString("}\n\n")
41
41
}
42
42
43
43
-
input := []byte(sb.String())
44
44
-
f := &Formatter{CommentMode: CommentsFollow}
43
43
+
inputSource := []byte(sourceBuilder.String())
44
44
+
formatter := &Formatter{CommentMode: CommentsFollow}
45
45
46
46
-
for b.Loop() {
47
47
-
_, _ = f.Format(input)
46
46
+
for benchmarkRunner.Loop() {
47
47
+
_, _ = formatter.Format(inputSource)
48
48
}
49
49
}
+96
-96
formatter_test.go
···
4
4
"testing"
5
5
)
6
6
7
7
-
func TestFormat_RemovesExtraBlankLines(t *testing.T) {
8
8
-
input := `package main
7
7
+
func TestFormat_RemovesExtraBlankLines(testRunner *testing.T) {
8
8
+
inputSource := `package main
9
9
10
10
func main() {
11
11
x := 1
···
14
14
y := 2
15
15
}
16
16
`
17
17
-
expected := `package main
17
17
+
expectedOutput := `package main
18
18
19
19
func main() {
20
20
x := 1
21
21
y := 2
22
22
}
23
23
`
24
24
-
f := &Formatter{CommentMode: CommentsFollow}
25
25
-
result, err := f.Format([]byte(input))
24
24
+
formatter := &Formatter{CommentMode: CommentsFollow}
25
25
+
formattedResult, err := formatter.Format([]byte(inputSource))
26
26
27
27
if err != nil {
28
28
-
t.Fatalf("Format error: %v", err)
28
28
+
testRunner.Fatalf("Format error: %v", err)
29
29
}
30
30
31
31
-
if string(result) != expected {
32
32
-
t.Errorf("got:\n%s\nwant:\n%s", result, expected)
31
31
+
if string(formattedResult) != expectedOutput {
32
32
+
testRunner.Errorf("got:\n%s\nwant:\n%s", formattedResult, expectedOutput)
33
33
}
34
34
}
35
35
36
36
-
func TestFormat_AddsBlankLineAroundScopedStatements(t *testing.T) {
37
37
-
input := `package main
36
36
+
func TestFormat_AddsBlankLineAroundScopedStatements(testRunner *testing.T) {
37
37
+
inputSource := `package main
38
38
39
39
func main() {
40
40
x := 1
···
44
44
z := 3
45
45
}
46
46
`
47
47
-
expected := `package main
47
47
+
expectedOutput := `package main
48
48
49
49
func main() {
50
50
x := 1
···
56
56
z := 3
57
57
}
58
58
`
59
59
-
f := &Formatter{CommentMode: CommentsFollow}
60
60
-
result, err := f.Format([]byte(input))
59
59
+
formatter := &Formatter{CommentMode: CommentsFollow}
60
60
+
formattedResult, err := formatter.Format([]byte(inputSource))
61
61
62
62
if err != nil {
63
63
-
t.Fatalf("Format error: %v", err)
63
63
+
testRunner.Fatalf("Format error: %v", err)
64
64
}
65
65
66
66
-
if string(result) != expected {
67
67
-
t.Errorf("got:\n%s\nwant:\n%s", result, expected)
66
66
+
if string(formattedResult) != expectedOutput {
67
67
+
testRunner.Errorf("got:\n%s\nwant:\n%s", formattedResult, expectedOutput)
68
68
}
69
69
}
70
70
71
71
-
func TestFormat_NestedScopes(t *testing.T) {
72
72
-
input := `package main
71
71
+
func TestFormat_NestedScopes(testRunner *testing.T) {
72
72
+
inputSource := `package main
73
73
74
74
func main() {
75
75
if true {
···
81
81
}
82
82
}
83
83
`
84
84
-
expected := `package main
84
84
+
expectedOutput := `package main
85
85
86
86
func main() {
87
87
if true {
···
95
95
}
96
96
}
97
97
`
98
98
-
f := &Formatter{CommentMode: CommentsFollow}
99
99
-
result, err := f.Format([]byte(input))
98
98
+
formatter := &Formatter{CommentMode: CommentsFollow}
99
99
+
formattedResult, err := formatter.Format([]byte(inputSource))
100
100
101
101
if err != nil {
102
102
-
t.Fatalf("Format error: %v", err)
102
102
+
testRunner.Fatalf("Format error: %v", err)
103
103
}
104
104
105
105
-
if string(result) != expected {
106
106
-
t.Errorf("got:\n%s\nwant:\n%s", result, expected)
105
105
+
if string(formattedResult) != expectedOutput {
106
106
+
testRunner.Errorf("got:\n%s\nwant:\n%s", formattedResult, expectedOutput)
107
107
}
108
108
}
109
109
110
110
-
func TestFormat_ForLoop(t *testing.T) {
111
111
-
input := `package main
110
110
+
func TestFormat_ForLoop(testRunner *testing.T) {
111
111
+
inputSource := `package main
112
112
113
113
func main() {
114
114
x := 1
···
118
118
z := 2
119
119
}
120
120
`
121
121
-
expected := `package main
121
121
+
expectedOutput := `package main
122
122
123
123
func main() {
124
124
x := 1
···
130
130
z := 2
131
131
}
132
132
`
133
133
-
f := &Formatter{CommentMode: CommentsFollow}
134
134
-
result, err := f.Format([]byte(input))
133
133
+
formatter := &Formatter{CommentMode: CommentsFollow}
134
134
+
formattedResult, err := formatter.Format([]byte(inputSource))
135
135
136
136
if err != nil {
137
137
-
t.Fatalf("Format error: %v", err)
137
137
+
testRunner.Fatalf("Format error: %v", err)
138
138
}
139
139
140
140
-
if string(result) != expected {
141
141
-
t.Errorf("got:\n%s\nwant:\n%s", result, expected)
140
140
+
if string(formattedResult) != expectedOutput {
141
141
+
testRunner.Errorf("got:\n%s\nwant:\n%s", formattedResult, expectedOutput)
142
142
}
143
143
}
144
144
145
145
-
func TestFormat_Switch(t *testing.T) {
146
146
-
input := `package main
145
145
+
func TestFormat_Switch(testRunner *testing.T) {
146
146
+
inputSource := `package main
147
147
148
148
func main() {
149
149
x := 1
···
154
154
z := 3
155
155
}
156
156
`
157
157
-
expected := `package main
157
157
+
expectedOutput := `package main
158
158
159
159
func main() {
160
160
x := 1
···
167
167
z := 3
168
168
}
169
169
`
170
170
-
f := &Formatter{CommentMode: CommentsFollow}
171
171
-
result, err := f.Format([]byte(input))
170
170
+
formatter := &Formatter{CommentMode: CommentsFollow}
171
171
+
formattedResult, err := formatter.Format([]byte(inputSource))
172
172
173
173
if err != nil {
174
174
-
t.Fatalf("Format error: %v", err)
174
174
+
testRunner.Fatalf("Format error: %v", err)
175
175
}
176
176
177
177
-
if string(result) != expected {
178
178
-
t.Errorf("got:\n%s\nwant:\n%s", result, expected)
177
177
+
if string(formattedResult) != expectedOutput {
178
178
+
testRunner.Errorf("got:\n%s\nwant:\n%s", formattedResult, expectedOutput)
179
179
}
180
180
}
181
181
182
182
-
func TestFormat_MultipleFunctions(t *testing.T) {
183
183
-
input := `package main
182
182
+
func TestFormat_MultipleFunctions(testRunner *testing.T) {
183
183
+
inputSource := `package main
184
184
185
185
func foo() {
186
186
x := 1
···
191
191
y := 2
192
192
}
193
193
`
194
194
-
expected := `package main
194
194
+
expectedOutput := `package main
195
195
196
196
func foo() {
197
197
x := 1
···
201
201
y := 2
202
202
}
203
203
`
204
204
-
f := &Formatter{CommentMode: CommentsFollow}
205
205
-
result, err := f.Format([]byte(input))
204
204
+
formatter := &Formatter{CommentMode: CommentsFollow}
205
205
+
formattedResult, err := formatter.Format([]byte(inputSource))
206
206
207
207
if err != nil {
208
208
-
t.Fatalf("Format error: %v", err)
208
208
+
testRunner.Fatalf("Format error: %v", err)
209
209
}
210
210
211
211
-
if string(result) != expected {
212
212
-
t.Errorf("got:\n%s\nwant:\n%s", result, expected)
211
211
+
if string(formattedResult) != expectedOutput {
212
212
+
testRunner.Errorf("got:\n%s\nwant:\n%s", formattedResult, expectedOutput)
213
213
}
214
214
}
215
215
216
216
-
func TestFormat_TypeStruct(t *testing.T) {
217
217
-
input := `package main
216
216
+
func TestFormat_TypeStruct(testRunner *testing.T) {
217
217
+
inputSource := `package main
218
218
219
219
type Foo struct {
220
220
X int
221
221
}
222
222
var x = 1
223
223
`
224
224
-
expected := `package main
224
224
+
expectedOutput := `package main
225
225
226
226
type Foo struct {
227
227
X int
···
229
229
230
230
var x = 1
231
231
`
232
232
-
f := &Formatter{CommentMode: CommentsFollow}
233
233
-
result, err := f.Format([]byte(input))
232
232
+
formatter := &Formatter{CommentMode: CommentsFollow}
233
233
+
formattedResult, err := formatter.Format([]byte(inputSource))
234
234
235
235
if err != nil {
236
236
-
t.Fatalf("Format error: %v", err)
236
236
+
testRunner.Fatalf("Format error: %v", err)
237
237
}
238
238
239
239
-
if string(result) != expected {
240
240
-
t.Errorf("got:\n%s\nwant:\n%s", result, expected)
239
239
+
if string(formattedResult) != expectedOutput {
240
240
+
testRunner.Errorf("got:\n%s\nwant:\n%s", formattedResult, expectedOutput)
241
241
}
242
242
}
243
243
244
244
-
func TestFormat_DifferentStatementTypes(t *testing.T) {
245
245
-
input := `package main
244
244
+
func TestFormat_DifferentStatementTypes(testRunner *testing.T) {
245
245
+
inputSource := `package main
246
246
247
247
func main() {
248
248
x := 1
···
254
254
return
255
255
}
256
256
`
257
257
-
expected := `package main
257
257
+
expectedOutput := `package main
258
258
259
259
func main() {
260
260
x := 1
···
270
270
return
271
271
}
272
272
`
273
273
-
f := &Formatter{CommentMode: CommentsFollow}
274
274
-
result, err := f.Format([]byte(input))
273
273
+
formatter := &Formatter{CommentMode: CommentsFollow}
274
274
+
formattedResult, err := formatter.Format([]byte(inputSource))
275
275
276
276
if err != nil {
277
277
-
t.Fatalf("Format error: %v", err)
277
277
+
testRunner.Fatalf("Format error: %v", err)
278
278
}
279
279
280
280
-
if string(result) != expected {
281
281
-
t.Errorf("got:\n%s\nwant:\n%s", result, expected)
280
280
+
if string(formattedResult) != expectedOutput {
281
281
+
testRunner.Errorf("got:\n%s\nwant:\n%s", formattedResult, expectedOutput)
282
282
}
283
283
}
284
284
285
285
-
func TestFormat_ConsecutiveIfs(t *testing.T) {
286
286
-
input := `package main
285
285
+
func TestFormat_ConsecutiveIfs(testRunner *testing.T) {
286
286
+
inputSource := `package main
287
287
288
288
func main() {
289
289
if err != nil {
···
294
294
}
295
295
}
296
296
`
297
297
-
expected := `package main
297
297
+
expectedOutput := `package main
298
298
299
299
func main() {
300
300
if err != nil {
···
306
306
}
307
307
}
308
308
`
309
309
-
f := &Formatter{CommentMode: CommentsFollow}
310
310
-
result, err := f.Format([]byte(input))
309
309
+
formatter := &Formatter{CommentMode: CommentsFollow}
310
310
+
formattedResult, err := formatter.Format([]byte(inputSource))
311
311
312
312
if err != nil {
313
313
-
t.Fatalf("Format error: %v", err)
313
313
+
testRunner.Fatalf("Format error: %v", err)
314
314
}
315
315
316
316
-
if string(result) != expected {
317
317
-
t.Errorf("got:\n%s\nwant:\n%s", result, expected)
316
316
+
if string(formattedResult) != expectedOutput {
317
317
+
testRunner.Errorf("got:\n%s\nwant:\n%s", formattedResult, expectedOutput)
318
318
}
319
319
}
320
320
321
321
-
func TestFormat_CaseClauseStatements(t *testing.T) {
322
322
-
input := `package main
321
321
+
func TestFormat_CaseClauseStatements(testRunner *testing.T) {
322
322
+
inputSource := `package main
323
323
324
324
func main() {
325
325
switch x {
···
331
331
}
332
332
}
333
333
`
334
334
-
expected := `package main
334
334
+
expectedOutput := `package main
335
335
336
336
func main() {
337
337
switch x {
···
344
344
}
345
345
}
346
346
`
347
347
-
f := &Formatter{CommentMode: CommentsFollow}
348
348
-
result, err := f.Format([]byte(input))
347
347
+
formatter := &Formatter{CommentMode: CommentsFollow}
348
348
+
formattedResult, err := formatter.Format([]byte(inputSource))
349
349
350
350
if err != nil {
351
351
-
t.Fatalf("Format error: %v", err)
351
351
+
testRunner.Fatalf("Format error: %v", err)
352
352
}
353
353
354
354
-
if string(result) != expected {
355
355
-
t.Errorf("got:\n%s\nwant:\n%s", result, expected)
354
354
+
if string(formattedResult) != expectedOutput {
355
355
+
testRunner.Errorf("got:\n%s\nwant:\n%s", formattedResult, expectedOutput)
356
356
}
357
357
}
358
358
359
359
-
func TestFormat_DeferWithInlineFunc(t *testing.T) {
360
360
-
input := `package main
359
359
+
func TestFormat_DeferWithInlineFunc(testRunner *testing.T) {
360
360
+
inputSource := `package main
361
361
362
362
func main() {
363
363
defer func() { _ = file.Close() }()
364
364
fileInfo, err := file.Stat()
365
365
}
366
366
`
367
367
-
expected := `package main
367
367
+
expectedOutput := `package main
368
368
369
369
func main() {
370
370
defer func() { _ = file.Close() }()
···
372
372
fileInfo, err := file.Stat()
373
373
}
374
374
`
375
375
-
f := &Formatter{CommentMode: CommentsFollow}
376
376
-
result, err := f.Format([]byte(input))
375
375
+
formatter := &Formatter{CommentMode: CommentsFollow}
376
376
+
formattedResult, err := formatter.Format([]byte(inputSource))
377
377
378
378
if err != nil {
379
379
-
t.Fatalf("Format error: %v", err)
379
379
+
testRunner.Fatalf("Format error: %v", err)
380
380
}
381
381
382
382
-
if string(result) != expected {
383
383
-
t.Errorf("got:\n%s\nwant:\n%s", result, expected)
382
382
+
if string(formattedResult) != expectedOutput {
383
383
+
testRunner.Errorf("got:\n%s\nwant:\n%s", formattedResult, expectedOutput)
384
384
}
385
385
}
386
386
387
387
-
func TestFormat_CaseClauseConsecutiveAssignments(t *testing.T) {
388
388
-
input := `package main
387
387
+
func TestFormat_CaseClauseConsecutiveAssignments(testRunner *testing.T) {
388
388
+
inputSource := `package main
389
389
390
390
func main() {
391
391
switch x {
···
398
398
}
399
399
}
400
400
`
401
401
-
expected := `package main
401
401
+
expectedOutput := `package main
402
402
403
403
func main() {
404
404
switch x {
···
411
411
}
412
412
}
413
413
`
414
414
-
f := &Formatter{CommentMode: CommentsFollow}
415
415
-
result, err := f.Format([]byte(input))
414
414
+
formatter := &Formatter{CommentMode: CommentsFollow}
415
415
+
formattedResult, err := formatter.Format([]byte(inputSource))
416
416
417
417
if err != nil {
418
418
-
t.Fatalf("Format error: %v", err)
418
418
+
testRunner.Fatalf("Format error: %v", err)
419
419
}
420
420
421
421
-
if string(result) != expected {
422
422
-
t.Errorf("got:\n%s\nwant:\n%s", result, expected)
421
421
+
if string(formattedResult) != expectedOutput {
422
422
+
testRunner.Errorf("got:\n%s\nwant:\n%s", formattedResult, expectedOutput)
423
423
}
424
424
}
+45
-45
main.go
···
59
59
60
60
exitCode := 0
61
61
62
62
-
for _, path := range flag.Args() {
63
63
-
switch info, err := os.Stat(path); {
62
62
+
for _, argumentPath := range flag.Args() {
63
63
+
switch fileInfo, err := os.Stat(argumentPath); {
64
64
case err != nil:
65
65
fmt.Fprintf(os.Stderr, "iku: %v\n", err)
66
66
67
67
exitCode = 1
68
68
-
case info.IsDir():
69
69
-
if err := processDir(formatter, path, &exitCode); err != nil {
68
68
+
case fileInfo.IsDir():
69
69
+
if err := processDirectory(formatter, argumentPath, &exitCode); err != nil {
70
70
fmt.Fprintf(os.Stderr, "iku: %v\n", err)
71
71
72
72
exitCode = 1
73
73
}
74
74
default:
75
75
-
if err := processFilePath(formatter, path, &exitCode); err != nil {
75
75
+
if err := processFilePath(formatter, argumentPath, &exitCode); err != nil {
76
76
fmt.Fprintf(os.Stderr, "iku: %v\n", err)
77
77
78
78
exitCode = 1
···
83
83
os.Exit(exitCode)
84
84
}
85
85
86
86
-
func parseCommentMode(mode string) (CommentMode, error) {
87
87
-
switch strings.ToLower(mode) {
86
86
+
func parseCommentMode(commentModeString string) (CommentMode, error) {
87
87
+
switch strings.ToLower(commentModeString) {
88
88
case "follow":
89
89
return CommentsFollow, nil
90
90
case "precede":
···
92
92
case "standalone":
93
93
return CommentsStandalone, nil
94
94
default:
95
95
-
return 0, fmt.Errorf("invalid comment mode: %q (use follow, precede, or standalone)", mode)
95
95
+
return 0, fmt.Errorf("invalid comment mode: %q (use follow, precede, or standalone)", commentModeString)
96
96
}
97
97
}
98
98
99
99
-
func processDir(formatter *Formatter, directory string, exitCode *int) error {
100
100
-
var files []string
99
99
+
func processDirectory(formatter *Formatter, directoryPath string, exitCode *int) error {
100
100
+
var goFilePaths []string
101
101
102
102
-
err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
102
102
+
err := filepath.Walk(directoryPath, func(currentPath string, fileInfo os.FileInfo, err error) error {
103
103
if err != nil {
104
104
return err
105
105
}
106
106
107
107
-
if !info.IsDir() && strings.HasSuffix(path, ".go") {
108
108
-
files = append(files, path)
107
107
+
if !fileInfo.IsDir() && strings.HasSuffix(currentPath, ".go") {
108
108
+
goFilePaths = append(goFilePaths, currentPath)
109
109
}
110
110
111
111
return nil
···
120
120
121
121
semaphore := make(chan struct{}, runtime.NumCPU())
122
122
123
123
-
for _, path := range files {
123
123
+
for _, filePath := range goFilePaths {
124
124
waitGroup.Add(1)
125
125
126
126
-
go func(filePath string) {
126
126
+
go func(currentFilePath string) {
127
127
defer waitGroup.Done()
128
128
129
129
semaphore <- struct{}{}
130
130
131
131
defer func() { <-semaphore }()
132
132
133
133
-
if err := processFilePath(formatter, filePath, exitCode); err != nil {
133
133
+
if err := processFilePath(formatter, currentFilePath, exitCode); err != nil {
134
134
mutex.Lock()
135
135
fmt.Fprintf(os.Stderr, "iku: %v\n", err)
136
136
···
138
138
139
139
mutex.Unlock()
140
140
}
141
141
-
}(path)
141
141
+
}(filePath)
142
142
}
143
143
144
144
waitGroup.Wait()
···
146
146
return nil
147
147
}
148
148
149
149
-
func processFilePath(formatter *Formatter, path string, _ *int) error {
150
150
-
file, err := os.Open(path)
149
149
+
func processFilePath(formatter *Formatter, filePath string, _ *int) error {
150
150
+
sourceFile, err := os.Open(filePath)
151
151
152
152
if err != nil {
153
153
return err
154
154
}
155
155
156
156
-
defer func() { _ = file.Close() }()
156
156
+
defer func() { _ = sourceFile.Close() }()
157
157
158
158
-
var output io.Writer = os.Stdout
158
158
+
var outputDestination io.Writer = os.Stdout
159
159
160
160
if *writeFlag {
161
161
-
output = nil
161
161
+
outputDestination = nil
162
162
}
163
163
164
164
-
return processFile(formatter, path, file, output, true)
164
164
+
return processFile(formatter, filePath, sourceFile, outputDestination, true)
165
165
}
166
166
167
167
-
func processFile(formatter *Formatter, filename string, input io.Reader, outputWriter io.Writer, isFile bool) error {
168
168
-
source, err := io.ReadAll(input)
167
167
+
func processFile(formatter *Formatter, filename string, inputReader io.Reader, outputWriter io.Writer, isFile bool) error {
168
168
+
sourceContent, err := io.ReadAll(inputReader)
169
169
170
170
if err != nil {
171
171
return fmt.Errorf("%s: %v", filename, err)
172
172
}
173
173
174
174
-
result, err := formatter.Format(source)
174
174
+
formattedResult, err := formatter.Format(sourceContent)
175
175
176
176
if err != nil {
177
177
return fmt.Errorf("%s: %v", filename, err)
178
178
}
179
179
180
180
if *listFlag {
181
181
-
if !bytes.Equal(source, result) {
181
181
+
if !bytes.Equal(sourceContent, formattedResult) {
182
182
fmt.Println(filename)
183
183
}
184
184
···
186
186
}
187
187
188
188
if *diffFlag {
189
189
-
if !bytes.Equal(source, result) {
190
190
-
difference := unifiedDiff(filename, source, result)
191
191
-
_, _ = os.Stdout.Write(difference)
189
189
+
if !bytes.Equal(sourceContent, formattedResult) {
190
190
+
diffOutput := unifiedDiff(filename, sourceContent, formattedResult)
191
191
+
_, _ = os.Stdout.Write(diffOutput)
192
192
}
193
193
194
194
return nil
195
195
}
196
196
197
197
if *writeFlag && isFile {
198
198
-
if !bytes.Equal(source, result) {
199
199
-
return os.WriteFile(filename, result, 0644)
198
198
+
if !bytes.Equal(sourceContent, formattedResult) {
199
199
+
return os.WriteFile(filename, formattedResult, 0644)
200
200
}
201
201
202
202
return nil
203
203
}
204
204
205
205
if outputWriter != nil {
206
206
-
_, err = outputWriter.Write(result)
206
206
+
_, err = outputWriter.Write(formattedResult)
207
207
208
208
return err
209
209
}
···
211
211
return nil
212
212
}
213
213
214
214
-
func unifiedDiff(filename string, original, formatted []byte) []byte {
215
215
-
var buffer bytes.Buffer
214
214
+
func unifiedDiff(filename string, originalSource, formattedSource []byte) []byte {
215
215
+
var outputBuffer bytes.Buffer
216
216
217
217
-
fmt.Fprintf(&buffer, "--- %s\n", filename)
218
218
-
fmt.Fprintf(&buffer, "+++ %s\n", filename)
217
217
+
fmt.Fprintf(&outputBuffer, "--- %s\n", filename)
218
218
+
fmt.Fprintf(&outputBuffer, "+++ %s\n", filename)
219
219
220
220
-
originalLines := strings.Split(string(original), "\n")
221
221
-
formattedLines := strings.Split(string(formatted), "\n")
220
220
+
originalSourceLines := strings.Split(string(originalSource), "\n")
221
221
+
formattedSourceLines := strings.Split(string(formattedSource), "\n")
222
222
223
223
-
fmt.Fprintf(&buffer, "@@ -1,%d +1,%d @@\n", len(originalLines), len(formattedLines))
223
223
+
fmt.Fprintf(&outputBuffer, "@@ -1,%d +1,%d @@\n", len(originalSourceLines), len(formattedSourceLines))
224
224
225
225
-
for _, line := range originalLines {
226
226
-
fmt.Fprintf(&buffer, "-%s\n", line)
225
225
+
for _, currentLine := range originalSourceLines {
226
226
+
fmt.Fprintf(&outputBuffer, "-%s\n", currentLine)
227
227
}
228
228
229
229
-
for _, line := range formattedLines {
230
230
-
fmt.Fprintf(&buffer, "+%s\n", line)
229
229
+
for _, currentLine := range formattedSourceLines {
230
230
+
fmt.Fprintf(&outputBuffer, "+%s\n", currentLine)
231
231
}
232
232
233
233
-
return buffer.Bytes()
233
233
+
return outputBuffer.Bytes()
234
234
}