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

Configure Feed

Select the types of activity you want to include in your feed.

refactor: Split formatter.go into separate files

Fuwn fa619e7b a1798d9b

+304 -297
-297
formatter.go
··· 1 1 package main 2 2 3 3 import ( 4 - "go/ast" 5 4 "go/format" 6 5 "go/parser" 7 6 "go/token" 8 - "reflect" 9 - "strings" 10 7 ) 11 8 12 9 type CommentMode int ··· 46 43 47 44 return f.rewrite(formattedSource, lineInformationMap), nil 48 45 } 49 - 50 - func isGeneralDeclarationScoped(generalDeclaration *ast.GenDecl) bool { 51 - for _, specification := range generalDeclaration.Specs { 52 - if typeSpecification, isTypeSpecification := specification.(*ast.TypeSpec); isTypeSpecification { 53 - switch typeSpecification.Type.(type) { 54 - case *ast.StructType, *ast.InterfaceType: 55 - return true 56 - } 57 - } 58 - } 59 - 60 - return false 61 - } 62 - 63 - func (f *Formatter) buildLineInfo(tokenFileSet *token.FileSet, parsedFile *ast.File) map[int]*lineInformation { 64 - lineInformationMap := make(map[int]*lineInformation) 65 - tokenFile := tokenFileSet.File(parsedFile.Pos()) 66 - 67 - if tokenFile == nil { 68 - return lineInformationMap 69 - } 70 - 71 - for _, declaration := range parsedFile.Decls { 72 - startLine := tokenFile.Line(declaration.Pos()) 73 - endLine := tokenFile.Line(declaration.End()) 74 - statementType := "" 75 - isScoped := false 76 - 77 - switch typedDeclaration := declaration.(type) { 78 - case *ast.GenDecl: 79 - statementType = typedDeclaration.Tok.String() 80 - isScoped = isGeneralDeclarationScoped(typedDeclaration) 81 - case *ast.FuncDecl: 82 - statementType = "func" 83 - isScoped = true 84 - default: 85 - statementType = reflect.TypeOf(declaration).String() 86 - } 87 - 88 - lineInformationMap[startLine] = &lineInformation{statementType: statementType, isTopLevel: true, isScoped: isScoped, isStartLine: true} 89 - lineInformationMap[endLine] = &lineInformation{statementType: statementType, isTopLevel: true, isScoped: isScoped, isStartLine: false} 90 - } 91 - 92 - ast.Inspect(parsedFile, func(astNode ast.Node) bool { 93 - if astNode == nil { 94 - return true 95 - } 96 - 97 - switch typedNode := astNode.(type) { 98 - case *ast.BlockStmt: 99 - f.processBlock(tokenFile, typedNode, lineInformationMap) 100 - case *ast.CaseClause: 101 - f.processStatementList(tokenFile, typedNode.Body, lineInformationMap) 102 - case *ast.CommClause: 103 - f.processStatementList(tokenFile, typedNode.Body, lineInformationMap) 104 - } 105 - 106 - return true 107 - }) 108 - 109 - return lineInformationMap 110 - } 111 - 112 - func (f *Formatter) processBlock(tokenFile *token.File, blockStatement *ast.BlockStmt, lineInformationMap map[int]*lineInformation) { 113 - if blockStatement == nil { 114 - return 115 - } 116 - 117 - f.processStatementList(tokenFile, blockStatement.List, lineInformationMap) 118 - } 119 - 120 - func (f *Formatter) processStatementList(tokenFile *token.File, statements []ast.Stmt, lineInformationMap map[int]*lineInformation) { 121 - for _, statement := range statements { 122 - startLine := tokenFile.Line(statement.Pos()) 123 - endLine := tokenFile.Line(statement.End()) 124 - statementType := "" 125 - isScoped := false 126 - 127 - switch typedStatement := statement.(type) { 128 - case *ast.DeclStmt: 129 - if generalDeclaration, isGeneralDeclaration := typedStatement.Decl.(*ast.GenDecl); isGeneralDeclaration { 130 - statementType = generalDeclaration.Tok.String() 131 - } else { 132 - statementType = reflect.TypeOf(statement).String() 133 - } 134 - case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, 135 - *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.BlockStmt: 136 - statementType = reflect.TypeOf(statement).String() 137 - isScoped = true 138 - default: 139 - statementType = reflect.TypeOf(statement).String() 140 - } 141 - 142 - existingStart := lineInformationMap[startLine] 143 - 144 - if existingStart == nil || !existingStart.isStartLine { 145 - lineInformationMap[startLine] = &lineInformation{statementType: statementType, isTopLevel: false, isScoped: isScoped, isStartLine: true} 146 - } 147 - 148 - existingEnd := lineInformationMap[endLine] 149 - 150 - if existingEnd == nil || !existingEnd.isStartLine { 151 - lineInformationMap[endLine] = &lineInformation{statementType: statementType, isTopLevel: false, isScoped: isScoped, isStartLine: false} 152 - } 153 - 154 - switch typedStatement := statement.(type) { 155 - case *ast.IfStmt: 156 - f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 157 - 158 - if typedStatement.Else != nil { 159 - if elseBlock, isBlockStatement := typedStatement.Else.(*ast.BlockStmt); isBlockStatement { 160 - f.processBlock(tokenFile, elseBlock, lineInformationMap) 161 - } else if elseIfStatement, isIfStatement := typedStatement.Else.(*ast.IfStmt); isIfStatement { 162 - f.processIfStatement(tokenFile, elseIfStatement, lineInformationMap) 163 - } 164 - } 165 - case *ast.ForStmt: 166 - f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 167 - case *ast.RangeStmt: 168 - f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 169 - case *ast.SwitchStmt: 170 - f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 171 - case *ast.TypeSwitchStmt: 172 - f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 173 - case *ast.SelectStmt: 174 - f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 175 - case *ast.BlockStmt: 176 - f.processBlock(tokenFile, typedStatement, lineInformationMap) 177 - } 178 - } 179 - } 180 - 181 - func (f *Formatter) processIfStatement(tokenFile *token.File, ifStatement *ast.IfStmt, lineInformationMap map[int]*lineInformation) { 182 - startLine := tokenFile.Line(ifStatement.Pos()) 183 - endLine := tokenFile.Line(ifStatement.End()) 184 - existingStart := lineInformationMap[startLine] 185 - 186 - if existingStart == nil || !existingStart.isStartLine { 187 - lineInformationMap[startLine] = &lineInformation{statementType: "*ast.IfStmt", isTopLevel: false, isScoped: true, isStartLine: true} 188 - } 189 - 190 - existingEnd := lineInformationMap[endLine] 191 - 192 - if existingEnd == nil || !existingEnd.isStartLine { 193 - lineInformationMap[endLine] = &lineInformation{statementType: "*ast.IfStmt", isTopLevel: false, isScoped: true, isStartLine: false} 194 - } 195 - 196 - f.processBlock(tokenFile, ifStatement.Body, lineInformationMap) 197 - 198 - if ifStatement.Else != nil { 199 - if elseBlock, isBlockStatement := ifStatement.Else.(*ast.BlockStmt); isBlockStatement { 200 - f.processBlock(tokenFile, elseBlock, lineInformationMap) 201 - } else if elseIfStatement, isIfStatement := ifStatement.Else.(*ast.IfStmt); isIfStatement { 202 - f.processIfStatement(tokenFile, elseIfStatement, lineInformationMap) 203 - } 204 - } 205 - } 206 - 207 - func (f *Formatter) rewrite(formattedSource []byte, lineInformationMap map[int]*lineInformation) []byte { 208 - sourceLines := strings.Split(string(formattedSource), "\n") 209 - resultLines := make([]string, 0, len(sourceLines)) 210 - previousWasOpenBrace := false 211 - previousStatementType := "" 212 - previousWasComment := false 213 - previousWasTopLevel := false 214 - previousWasScoped := false 215 - insideRawString := false 216 - 217 - for lineIndex, currentLine := range sourceLines { 218 - backtickCount := countRawStringDelimiters(currentLine) 219 - wasInsideRawString := insideRawString 220 - 221 - if backtickCount%2 == 1 { 222 - insideRawString = !insideRawString 223 - } 224 - 225 - if wasInsideRawString { 226 - resultLines = append(resultLines, currentLine) 227 - 228 - continue 229 - } 230 - 231 - lineNumber := lineIndex + 1 232 - trimmedLine := strings.TrimSpace(currentLine) 233 - 234 - if trimmedLine == "" { 235 - continue 236 - } 237 - 238 - isClosingBrace := closingBracePattern.MatchString(currentLine) 239 - isOpeningBrace := openingBracePattern.MatchString(currentLine) 240 - isCaseLabel := caseLabelPattern.MatchString(currentLine) 241 - isCommentOnlyLine := isCommentOnly(currentLine) 242 - isPackageDeclaration := isPackageLine(trimmedLine) 243 - currentInformation := lineInformationMap[lineNumber] 244 - currentStatementType := "" 245 - 246 - if currentInformation != nil { 247 - currentStatementType = currentInformation.statementType 248 - } 249 - 250 - if isPackageDeclaration { 251 - currentStatementType = "package" 252 - } 253 - 254 - needsBlankLine := false 255 - currentIsTopLevel := currentInformation != nil && currentInformation.isTopLevel 256 - currentIsScoped := currentInformation != nil && currentInformation.isScoped 257 - 258 - if len(resultLines) > 0 && !previousWasOpenBrace && !isClosingBrace && !isCaseLabel { 259 - if currentIsTopLevel && previousWasTopLevel && currentStatementType != previousStatementType { 260 - if f.CommentMode == CommentsFollow && previousWasComment { 261 - } else { 262 - needsBlankLine = true 263 - } 264 - } else if currentInformation != nil && (currentIsScoped || previousWasScoped) { 265 - if f.CommentMode == CommentsFollow && previousWasComment { 266 - } else { 267 - needsBlankLine = true 268 - } 269 - } else if currentStatementType != "" && previousStatementType != "" && currentStatementType != previousStatementType { 270 - if f.CommentMode == CommentsFollow && previousWasComment { 271 - } else { 272 - needsBlankLine = true 273 - } 274 - } 275 - 276 - if f.CommentMode == CommentsFollow && isCommentOnlyLine && !previousWasComment { 277 - nextLineNumber := f.findNextNonCommentLine(sourceLines, lineIndex+1) 278 - 279 - if nextLineNumber > 0 { 280 - nextInformation := lineInformationMap[nextLineNumber] 281 - 282 - if nextInformation != nil { 283 - nextIsTopLevel := nextInformation.isTopLevel 284 - nextIsScoped := nextInformation.isScoped 285 - 286 - if nextIsTopLevel && previousWasTopLevel && nextInformation.statementType != previousStatementType { 287 - needsBlankLine = true 288 - } else if nextIsScoped || previousWasScoped { 289 - needsBlankLine = true 290 - } else if nextInformation.statementType != "" && previousStatementType != "" && nextInformation.statementType != previousStatementType { 291 - needsBlankLine = true 292 - } 293 - } 294 - } 295 - } 296 - } 297 - 298 - if needsBlankLine { 299 - resultLines = append(resultLines, "") 300 - } 301 - 302 - resultLines = append(resultLines, currentLine) 303 - previousWasOpenBrace = isOpeningBrace || isCaseLabel 304 - previousWasComment = isCommentOnlyLine 305 - 306 - if currentInformation != nil { 307 - previousStatementType = currentInformation.statementType 308 - previousWasTopLevel = currentInformation.isTopLevel 309 - previousWasScoped = currentInformation.isScoped 310 - } else if currentStatementType != "" { 311 - previousStatementType = currentStatementType 312 - previousWasTopLevel = false 313 - previousWasScoped = false 314 - } 315 - } 316 - 317 - outputString := strings.Join(resultLines, "\n") 318 - 319 - if !strings.HasSuffix(outputString, "\n") { 320 - outputString += "\n" 321 - } 322 - 323 - return []byte(outputString) 324 - } 325 - 326 - func (f *Formatter) findNextNonCommentLine(sourceLines []string, startLineIndex int) int { 327 - for lineIndex := startLineIndex; lineIndex < len(sourceLines); lineIndex++ { 328 - trimmedLine := strings.TrimSpace(sourceLines[lineIndex]) 329 - 330 - if trimmedLine == "" { 331 - continue 332 - } 333 - 334 - if isCommentOnly(sourceLines[lineIndex]) { 335 - continue 336 - } 337 - 338 - return lineIndex + 1 339 - } 340 - 341 - return 0 342 - }
+164
inspect.go
··· 1 + package main 2 + 3 + import ( 4 + "go/ast" 5 + "go/token" 6 + "reflect" 7 + ) 8 + 9 + func isGeneralDeclarationScoped(generalDeclaration *ast.GenDecl) bool { 10 + for _, specification := range generalDeclaration.Specs { 11 + if typeSpecification, isTypeSpecification := specification.(*ast.TypeSpec); isTypeSpecification { 12 + switch typeSpecification.Type.(type) { 13 + case *ast.StructType, *ast.InterfaceType: 14 + return true 15 + } 16 + } 17 + } 18 + 19 + return false 20 + } 21 + 22 + func (f *Formatter) buildLineInfo(tokenFileSet *token.FileSet, parsedFile *ast.File) map[int]*lineInformation { 23 + lineInformationMap := make(map[int]*lineInformation) 24 + tokenFile := tokenFileSet.File(parsedFile.Pos()) 25 + 26 + if tokenFile == nil { 27 + return lineInformationMap 28 + } 29 + 30 + for _, declaration := range parsedFile.Decls { 31 + startLine := tokenFile.Line(declaration.Pos()) 32 + endLine := tokenFile.Line(declaration.End()) 33 + statementType := "" 34 + isScoped := false 35 + 36 + switch typedDeclaration := declaration.(type) { 37 + case *ast.GenDecl: 38 + statementType = typedDeclaration.Tok.String() 39 + isScoped = isGeneralDeclarationScoped(typedDeclaration) 40 + case *ast.FuncDecl: 41 + statementType = "func" 42 + isScoped = true 43 + default: 44 + statementType = reflect.TypeOf(declaration).String() 45 + } 46 + 47 + lineInformationMap[startLine] = &lineInformation{statementType: statementType, isTopLevel: true, isScoped: isScoped, isStartLine: true} 48 + lineInformationMap[endLine] = &lineInformation{statementType: statementType, isTopLevel: true, isScoped: isScoped, isStartLine: false} 49 + } 50 + 51 + ast.Inspect(parsedFile, func(astNode ast.Node) bool { 52 + if astNode == nil { 53 + return true 54 + } 55 + 56 + switch typedNode := astNode.(type) { 57 + case *ast.BlockStmt: 58 + f.processBlock(tokenFile, typedNode, lineInformationMap) 59 + case *ast.CaseClause: 60 + f.processStatementList(tokenFile, typedNode.Body, lineInformationMap) 61 + case *ast.CommClause: 62 + f.processStatementList(tokenFile, typedNode.Body, lineInformationMap) 63 + } 64 + 65 + return true 66 + }) 67 + 68 + return lineInformationMap 69 + } 70 + 71 + func (f *Formatter) processBlock(tokenFile *token.File, blockStatement *ast.BlockStmt, lineInformationMap map[int]*lineInformation) { 72 + if blockStatement == nil { 73 + return 74 + } 75 + 76 + f.processStatementList(tokenFile, blockStatement.List, lineInformationMap) 77 + } 78 + 79 + func (f *Formatter) processStatementList(tokenFile *token.File, statements []ast.Stmt, lineInformationMap map[int]*lineInformation) { 80 + for _, statement := range statements { 81 + startLine := tokenFile.Line(statement.Pos()) 82 + endLine := tokenFile.Line(statement.End()) 83 + statementType := "" 84 + isScoped := false 85 + 86 + switch typedStatement := statement.(type) { 87 + case *ast.DeclStmt: 88 + if generalDeclaration, isGeneralDeclaration := typedStatement.Decl.(*ast.GenDecl); isGeneralDeclaration { 89 + statementType = generalDeclaration.Tok.String() 90 + } else { 91 + statementType = reflect.TypeOf(statement).String() 92 + } 93 + case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, 94 + *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.BlockStmt: 95 + statementType = reflect.TypeOf(statement).String() 96 + isScoped = true 97 + default: 98 + statementType = reflect.TypeOf(statement).String() 99 + } 100 + 101 + existingStart := lineInformationMap[startLine] 102 + 103 + if existingStart == nil || !existingStart.isStartLine { 104 + lineInformationMap[startLine] = &lineInformation{statementType: statementType, isTopLevel: false, isScoped: isScoped, isStartLine: true} 105 + } 106 + 107 + existingEnd := lineInformationMap[endLine] 108 + 109 + if existingEnd == nil || !existingEnd.isStartLine { 110 + lineInformationMap[endLine] = &lineInformation{statementType: statementType, isTopLevel: false, isScoped: isScoped, isStartLine: false} 111 + } 112 + 113 + switch typedStatement := statement.(type) { 114 + case *ast.IfStmt: 115 + f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 116 + 117 + if typedStatement.Else != nil { 118 + if elseBlock, isBlockStatement := typedStatement.Else.(*ast.BlockStmt); isBlockStatement { 119 + f.processBlock(tokenFile, elseBlock, lineInformationMap) 120 + } else if elseIfStatement, isIfStatement := typedStatement.Else.(*ast.IfStmt); isIfStatement { 121 + f.processIfStatement(tokenFile, elseIfStatement, lineInformationMap) 122 + } 123 + } 124 + case *ast.ForStmt: 125 + f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 126 + case *ast.RangeStmt: 127 + f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 128 + case *ast.SwitchStmt: 129 + f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 130 + case *ast.TypeSwitchStmt: 131 + f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 132 + case *ast.SelectStmt: 133 + f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 134 + case *ast.BlockStmt: 135 + f.processBlock(tokenFile, typedStatement, lineInformationMap) 136 + } 137 + } 138 + } 139 + 140 + func (f *Formatter) processIfStatement(tokenFile *token.File, ifStatement *ast.IfStmt, lineInformationMap map[int]*lineInformation) { 141 + startLine := tokenFile.Line(ifStatement.Pos()) 142 + endLine := tokenFile.Line(ifStatement.End()) 143 + existingStart := lineInformationMap[startLine] 144 + 145 + if existingStart == nil || !existingStart.isStartLine { 146 + lineInformationMap[startLine] = &lineInformation{statementType: "*ast.IfStmt", isTopLevel: false, isScoped: true, isStartLine: true} 147 + } 148 + 149 + existingEnd := lineInformationMap[endLine] 150 + 151 + if existingEnd == nil || !existingEnd.isStartLine { 152 + lineInformationMap[endLine] = &lineInformation{statementType: "*ast.IfStmt", isTopLevel: false, isScoped: true, isStartLine: false} 153 + } 154 + 155 + f.processBlock(tokenFile, ifStatement.Body, lineInformationMap) 156 + 157 + if ifStatement.Else != nil { 158 + if elseBlock, isBlockStatement := ifStatement.Else.(*ast.BlockStmt); isBlockStatement { 159 + f.processBlock(tokenFile, elseBlock, lineInformationMap) 160 + } else if elseIfStatement, isIfStatement := ifStatement.Else.(*ast.IfStmt); isIfStatement { 161 + f.processIfStatement(tokenFile, elseIfStatement, lineInformationMap) 162 + } 163 + } 164 + }
+140
rewrite.go
··· 1 + package main 2 + 3 + import "strings" 4 + 5 + func (f *Formatter) rewrite(formattedSource []byte, lineInformationMap map[int]*lineInformation) []byte { 6 + sourceLines := strings.Split(string(formattedSource), "\n") 7 + resultLines := make([]string, 0, len(sourceLines)) 8 + previousWasOpenBrace := false 9 + previousStatementType := "" 10 + previousWasComment := false 11 + previousWasTopLevel := false 12 + previousWasScoped := false 13 + insideRawString := false 14 + 15 + for lineIndex, currentLine := range sourceLines { 16 + backtickCount := countRawStringDelimiters(currentLine) 17 + wasInsideRawString := insideRawString 18 + 19 + if backtickCount%2 == 1 { 20 + insideRawString = !insideRawString 21 + } 22 + 23 + if wasInsideRawString { 24 + resultLines = append(resultLines, currentLine) 25 + 26 + continue 27 + } 28 + 29 + lineNumber := lineIndex + 1 30 + trimmedLine := strings.TrimSpace(currentLine) 31 + 32 + if trimmedLine == "" { 33 + continue 34 + } 35 + 36 + isClosingBrace := closingBracePattern.MatchString(currentLine) 37 + isOpeningBrace := openingBracePattern.MatchString(currentLine) 38 + isCaseLabel := caseLabelPattern.MatchString(currentLine) 39 + isCommentOnlyLine := isCommentOnly(currentLine) 40 + isPackageDeclaration := isPackageLine(trimmedLine) 41 + currentInformation := lineInformationMap[lineNumber] 42 + currentStatementType := "" 43 + 44 + if currentInformation != nil { 45 + currentStatementType = currentInformation.statementType 46 + } 47 + 48 + if isPackageDeclaration { 49 + currentStatementType = "package" 50 + } 51 + 52 + needsBlankLine := false 53 + currentIsTopLevel := currentInformation != nil && currentInformation.isTopLevel 54 + currentIsScoped := currentInformation != nil && currentInformation.isScoped 55 + 56 + if len(resultLines) > 0 && !previousWasOpenBrace && !isClosingBrace && !isCaseLabel { 57 + if currentIsTopLevel && previousWasTopLevel && currentStatementType != previousStatementType { 58 + if f.CommentMode == CommentsFollow && previousWasComment { 59 + } else { 60 + needsBlankLine = true 61 + } 62 + } else if currentInformation != nil && (currentIsScoped || previousWasScoped) { 63 + if f.CommentMode == CommentsFollow && previousWasComment { 64 + } else { 65 + needsBlankLine = true 66 + } 67 + } else if currentStatementType != "" && previousStatementType != "" && currentStatementType != previousStatementType { 68 + if f.CommentMode == CommentsFollow && previousWasComment { 69 + } else { 70 + needsBlankLine = true 71 + } 72 + } 73 + 74 + if f.CommentMode == CommentsFollow && isCommentOnlyLine && !previousWasComment { 75 + nextLineNumber := f.findNextNonCommentLine(sourceLines, lineIndex+1) 76 + 77 + if nextLineNumber > 0 { 78 + nextInformation := lineInformationMap[nextLineNumber] 79 + 80 + if nextInformation != nil { 81 + nextIsTopLevel := nextInformation.isTopLevel 82 + nextIsScoped := nextInformation.isScoped 83 + 84 + if nextIsTopLevel && previousWasTopLevel && nextInformation.statementType != previousStatementType { 85 + needsBlankLine = true 86 + } else if nextIsScoped || previousWasScoped { 87 + needsBlankLine = true 88 + } else if nextInformation.statementType != "" && previousStatementType != "" && nextInformation.statementType != previousStatementType { 89 + needsBlankLine = true 90 + } 91 + } 92 + } 93 + } 94 + } 95 + 96 + if needsBlankLine { 97 + resultLines = append(resultLines, "") 98 + } 99 + 100 + resultLines = append(resultLines, currentLine) 101 + previousWasOpenBrace = isOpeningBrace || isCaseLabel 102 + previousWasComment = isCommentOnlyLine 103 + 104 + if currentInformation != nil { 105 + previousStatementType = currentInformation.statementType 106 + previousWasTopLevel = currentInformation.isTopLevel 107 + previousWasScoped = currentInformation.isScoped 108 + } else if currentStatementType != "" { 109 + previousStatementType = currentStatementType 110 + previousWasTopLevel = false 111 + previousWasScoped = false 112 + } 113 + } 114 + 115 + outputString := strings.Join(resultLines, "\n") 116 + 117 + if !strings.HasSuffix(outputString, "\n") { 118 + outputString += "\n" 119 + } 120 + 121 + return []byte(outputString) 122 + } 123 + 124 + func (f *Formatter) findNextNonCommentLine(sourceLines []string, startLineIndex int) int { 125 + for lineIndex := startLineIndex; lineIndex < len(sourceLines); lineIndex++ { 126 + trimmedLine := strings.TrimSpace(sourceLines[lineIndex]) 127 + 128 + if trimmedLine == "" { 129 + continue 130 + } 131 + 132 + if isCommentOnly(sourceLines[lineIndex]) { 133 + continue 134 + } 135 + 136 + return lineIndex + 1 137 + } 138 + 139 + return 0 140 + }