馃殌 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.

at main 167 lines 5.8 kB view raw
1package main 2 3import ( 4 "fmt" 5 "go/ast" 6 "go/token" 7) 8 9func 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 22func (f *Formatter) buildLineInfo(tokenFileSet *token.FileSet, parsedFile *ast.File) map[int]*lineInformation { 23 lineInformationMap := make(map[int]*lineInformation, 2*len(parsedFile.Decls)) 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 = fmt.Sprintf("%T", declaration) 45 } 46 47 lineInformationMap[startLine] = &lineInformation{statementType: statementType, isTopLevel: true, isScoped: isScoped, isStartLine: true} 48 49 if endLine != startLine { 50 lineInformationMap[endLine] = &lineInformation{statementType: statementType, isTopLevel: true, isScoped: isScoped, isStartLine: false} 51 } 52 } 53 54 ast.Inspect(parsedFile, func(astNode ast.Node) bool { 55 if astNode == nil { 56 return true 57 } 58 59 switch typedNode := astNode.(type) { 60 case *ast.BlockStmt: 61 f.processBlock(tokenFile, typedNode, lineInformationMap) 62 case *ast.CaseClause: 63 f.processStatementList(tokenFile, typedNode.Body, lineInformationMap) 64 case *ast.CommClause: 65 f.processStatementList(tokenFile, typedNode.Body, lineInformationMap) 66 } 67 68 return true 69 }) 70 71 return lineInformationMap 72} 73 74func (f *Formatter) processBlock(tokenFile *token.File, blockStatement *ast.BlockStmt, lineInformationMap map[int]*lineInformation) { 75 if blockStatement == nil { 76 return 77 } 78 79 f.processStatementList(tokenFile, blockStatement.List, lineInformationMap) 80} 81 82func (f *Formatter) processStatementList(tokenFile *token.File, statements []ast.Stmt, lineInformationMap map[int]*lineInformation) { 83 for _, statement := range statements { 84 startLine := tokenFile.Line(statement.Pos()) 85 endLine := tokenFile.Line(statement.End()) 86 statementType := "" 87 isScoped := false 88 89 switch typedStatement := statement.(type) { 90 case *ast.DeclStmt: 91 if generalDeclaration, isGeneralDeclaration := typedStatement.Decl.(*ast.GenDecl); isGeneralDeclaration { 92 statementType = generalDeclaration.Tok.String() 93 } else { 94 statementType = fmt.Sprintf("%T", statement) 95 } 96 case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, 97 *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.BlockStmt: 98 statementType = fmt.Sprintf("%T", statement) 99 isScoped = true 100 default: 101 statementType = fmt.Sprintf("%T", statement) 102 } 103 104 existingStart := lineInformationMap[startLine] 105 106 if existingStart == nil || !existingStart.isStartLine { 107 lineInformationMap[startLine] = &lineInformation{statementType: statementType, isTopLevel: false, isScoped: isScoped, isStartLine: true} 108 } 109 110 existingEnd := lineInformationMap[endLine] 111 112 if existingEnd == nil || !existingEnd.isStartLine { 113 lineInformationMap[endLine] = &lineInformation{statementType: statementType, isTopLevel: false, isScoped: isScoped, isStartLine: false} 114 } 115 116 switch typedStatement := statement.(type) { 117 case *ast.IfStmt: 118 f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 119 120 if typedStatement.Else != nil { 121 if elseBlock, isBlockStatement := typedStatement.Else.(*ast.BlockStmt); isBlockStatement { 122 f.processBlock(tokenFile, elseBlock, lineInformationMap) 123 } else if elseIfStatement, isIfStatement := typedStatement.Else.(*ast.IfStmt); isIfStatement { 124 f.processIfStatement(tokenFile, elseIfStatement, lineInformationMap) 125 } 126 } 127 case *ast.ForStmt: 128 f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 129 case *ast.RangeStmt: 130 f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 131 case *ast.SwitchStmt: 132 f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 133 case *ast.TypeSwitchStmt: 134 f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 135 case *ast.SelectStmt: 136 f.processBlock(tokenFile, typedStatement.Body, lineInformationMap) 137 case *ast.BlockStmt: 138 f.processBlock(tokenFile, typedStatement, lineInformationMap) 139 } 140 } 141} 142 143func (f *Formatter) processIfStatement(tokenFile *token.File, ifStatement *ast.IfStmt, lineInformationMap map[int]*lineInformation) { 144 startLine := tokenFile.Line(ifStatement.Pos()) 145 endLine := tokenFile.Line(ifStatement.End()) 146 existingStart := lineInformationMap[startLine] 147 148 if existingStart == nil || !existingStart.isStartLine { 149 lineInformationMap[startLine] = &lineInformation{statementType: "*ast.IfStmt", isTopLevel: false, isScoped: true, isStartLine: true} 150 } 151 152 existingEnd := lineInformationMap[endLine] 153 154 if existingEnd == nil || !existingEnd.isStartLine { 155 lineInformationMap[endLine] = &lineInformation{statementType: "*ast.IfStmt", isTopLevel: false, isScoped: true, isStartLine: false} 156 } 157 158 f.processBlock(tokenFile, ifStatement.Body, lineInformationMap) 159 160 if ifStatement.Else != nil { 161 if elseBlock, isBlockStatement := ifStatement.Else.(*ast.BlockStmt); isBlockStatement { 162 f.processBlock(tokenFile, elseBlock, lineInformationMap) 163 } else if elseIfStatement, isIfStatement := ifStatement.Else.(*ast.IfStmt); isIfStatement { 164 f.processIfStatement(tokenFile, elseIfStatement, lineInformationMap) 165 } 166 } 167}