๐Ÿš€ Grammar-Aware Code Formatter: Structure through separation (supports Go, JavaScript, TypeScript, JSX, and TSX)
go formatter code-formatter javascript typescript jsx tsx

refactor: Rename variables to be fully self-documenting

fuwn.net e9374544 2e53255d

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