馃殌 Grammar-Aware Code Formatter: Structure through separation (supports Go, JavaScript, TypeScript, JSX, and TSX)
go formatter code-formatter javascript typescript jsx tsx
at main 202 lines 8.9 kB view raw
1package engine 2 3import ( 4 "strings" 5 "testing" 6) 7 8func formatResult(formattingEngine *Engine, events []LineEvent) string { 9 result := formattingEngine.FormatToString(events) 10 11 return strings.TrimSuffix(result, "\n") 12} 13 14func TestEngineCollapsesBlanks(t *testing.T) { 15 events := []LineEvent{ 16 {Content: "\tx := 1", TrimmedContent: "x := 1", HasASTInfo: true, StatementType: "*ast.AssignStmt", IsStartLine: true}, 17 {Content: "", TrimmedContent: "", IsBlank: true}, 18 {Content: "", TrimmedContent: "", IsBlank: true}, 19 {Content: "\ty := 2", TrimmedContent: "y := 2", HasASTInfo: true, StatementType: "*ast.AssignStmt", IsStartLine: true}, 20 } 21 formattingEngine := &Engine{CommentMode: CommentsFollow} 22 result := formatResult(formattingEngine, events) 23 24 if result != "\tx := 1\n\ty := 2" { 25 t.Errorf("expected blanks collapsed, got:\n%s", result) 26 } 27} 28 29func TestEngineScopeBoundary(t *testing.T) { 30 events := []LineEvent{ 31 {Content: "\tx := 1", TrimmedContent: "x := 1", HasASTInfo: true, StatementType: "*ast.AssignStmt"}, 32 {Content: "\tif x > 0 {", TrimmedContent: "if x > 0 {", HasASTInfo: true, StatementType: "*ast.IfStmt", IsScoped: true, IsStartLine: true, IsOpeningBrace: true}, 33 {Content: "\t\ty := 2", TrimmedContent: "y := 2", HasASTInfo: true, StatementType: "*ast.AssignStmt"}, 34 {Content: "\t}", TrimmedContent: "}", IsClosingBrace: true, HasASTInfo: true, StatementType: "*ast.IfStmt", IsScoped: true}, 35 {Content: "\tz := 3", TrimmedContent: "z := 3", HasASTInfo: true, StatementType: "*ast.AssignStmt"}, 36 } 37 formattingEngine := &Engine{CommentMode: CommentsFollow} 38 result := formatResult(formattingEngine, events) 39 expected := "\tx := 1\n\n\tif x > 0 {\n\t\ty := 2\n\t}\n\n\tz := 3" 40 41 if result != expected { 42 t.Errorf("expected scope boundaries, got:\n%s\nwant:\n%s", result, expected) 43 } 44} 45 46func TestEngineStatementTypeTransition(t *testing.T) { 47 events := []LineEvent{ 48 {Content: "\tx := 1", TrimmedContent: "x := 1", HasASTInfo: true, StatementType: "*ast.AssignStmt"}, 49 {Content: "\tvar a = 3", TrimmedContent: "var a = 3", HasASTInfo: true, StatementType: "var"}, 50 } 51 formattingEngine := &Engine{CommentMode: CommentsFollow} 52 result := formatResult(formattingEngine, events) 53 expected := "\tx := 1\n\n\tvar a = 3" 54 55 if result != expected { 56 t.Errorf("expected blank between different types, got:\n%s\nwant:\n%s", result, expected) 57 } 58} 59 60func TestEngineSuppressAfterOpenBrace(t *testing.T) { 61 events := []LineEvent{ 62 {Content: "func main() {", TrimmedContent: "func main() {", HasASTInfo: true, StatementType: "func", IsScoped: true, IsTopLevel: true, IsStartLine: true, IsOpeningBrace: true}, 63 {Content: "\tif true {", TrimmedContent: "if true {", HasASTInfo: true, StatementType: "*ast.IfStmt", IsScoped: true, IsStartLine: true, IsOpeningBrace: true}, 64 {Content: "\t\tx := 1", TrimmedContent: "x := 1", HasASTInfo: true, StatementType: "*ast.AssignStmt"}, 65 {Content: "\t}", TrimmedContent: "}", IsClosingBrace: true}, 66 } 67 formattingEngine := &Engine{CommentMode: CommentsFollow} 68 result := formatResult(formattingEngine, events) 69 expected := "func main() {\n\tif true {\n\t\tx := 1\n\t}" 70 71 if result != expected { 72 t.Errorf("should not insert blank after open brace, got:\n%s\nwant:\n%s", result, expected) 73 } 74} 75 76func TestEngineSuppressBeforeCloseBrace(t *testing.T) { 77 events := []LineEvent{ 78 {Content: "\tx := 1", TrimmedContent: "x := 1", HasASTInfo: true, StatementType: "*ast.AssignStmt", IsScoped: false}, 79 {Content: "}", TrimmedContent: "}", IsClosingBrace: true}, 80 } 81 formattingEngine := &Engine{CommentMode: CommentsFollow} 82 result := formatResult(formattingEngine, events) 83 expected := "\tx := 1\n}" 84 85 if result != expected { 86 t.Errorf("should not insert blank before close brace, got:\n%s\nwant:\n%s", result, expected) 87 } 88} 89 90func TestEngineSuppressCaseLabel(t *testing.T) { 91 events := []LineEvent{ 92 {Content: "\tcase 1:", TrimmedContent: "case 1:", HasASTInfo: true, StatementType: "*ast.AssignStmt", IsCaseLabel: true, IsOpeningBrace: false}, 93 {Content: "\t\tfoo()", TrimmedContent: "foo()", HasASTInfo: true, StatementType: "*ast.ExprStmt"}, 94 {Content: "\tcase 2:", TrimmedContent: "case 2:", HasASTInfo: true, StatementType: "*ast.AssignStmt", IsCaseLabel: true}, 95 } 96 formattingEngine := &Engine{CommentMode: CommentsFollow} 97 result := formatResult(formattingEngine, events) 98 expected := "\tcase 1:\n\t\tfoo()\n\tcase 2:" 99 100 if result != expected { 101 t.Errorf("should not insert blank before case label, got:\n%s\nwant:\n%s", result, expected) 102 } 103} 104 105func TestEngineRawStringPassthrough(t *testing.T) { 106 events := []LineEvent{ 107 {Content: "\tx := `", TrimmedContent: "x := `", HasASTInfo: true, StatementType: "*ast.AssignStmt"}, 108 {Content: "raw line 1", TrimmedContent: "raw line 1", InRawString: true}, 109 {Content: "", TrimmedContent: "", InRawString: true}, 110 {Content: "raw line 2`", TrimmedContent: "raw line 2`", InRawString: true}, 111 {Content: "\ty := 1", TrimmedContent: "y := 1", HasASTInfo: true, StatementType: "*ast.AssignStmt"}, 112 } 113 formattingEngine := &Engine{CommentMode: CommentsFollow} 114 result := formatResult(formattingEngine, events) 115 expected := "\tx := `\nraw line 1\n\nraw line 2`\n\ty := 1" 116 117 if result != expected { 118 t.Errorf("raw strings should pass through unchanged, got:\n%s\nwant:\n%s", result, expected) 119 } 120} 121 122func TestEngineTopLevelDifferentTypes(t *testing.T) { 123 events := []LineEvent{ 124 {Content: "type Foo struct {", TrimmedContent: "type Foo struct {", HasASTInfo: true, StatementType: "type", IsTopLevel: true, IsScoped: true, IsStartLine: true, IsOpeningBrace: true}, 125 {Content: "\tX int", TrimmedContent: "X int"}, 126 {Content: "}", TrimmedContent: "}", IsClosingBrace: true, HasASTInfo: true, StatementType: "type", IsTopLevel: true, IsScoped: true}, 127 {Content: "var x = 1", TrimmedContent: "var x = 1", HasASTInfo: true, StatementType: "var", IsTopLevel: true, IsStartLine: true}, 128 } 129 formattingEngine := &Engine{CommentMode: CommentsFollow} 130 result := formatResult(formattingEngine, events) 131 expected := "type Foo struct {\n\tX int\n}\n\nvar x = 1" 132 133 if result != expected { 134 t.Errorf("expected blank between different top-level types, got:\n%s\nwant:\n%s", result, expected) 135 } 136} 137 138func TestEngineCommentLookAhead(t *testing.T) { 139 events := []LineEvent{ 140 {Content: "\tx := 1", TrimmedContent: "x := 1", HasASTInfo: true, StatementType: "*ast.AssignStmt"}, 141 {Content: "\t// comment about if", TrimmedContent: "// comment about if", IsCommentOnly: true}, 142 {Content: "\tif true {", TrimmedContent: "if true {", HasASTInfo: true, StatementType: "*ast.IfStmt", IsScoped: true, IsStartLine: true, IsOpeningBrace: true}, 143 {Content: "\t\ty := 2", TrimmedContent: "y := 2", HasASTInfo: true, StatementType: "*ast.AssignStmt"}, 144 {Content: "\t}", TrimmedContent: "}", IsClosingBrace: true}, 145 } 146 formattingEngine := &Engine{CommentMode: CommentsFollow} 147 result := formatResult(formattingEngine, events) 148 expected := "\tx := 1\n\n\t// comment about if\n\tif true {\n\t\ty := 2\n\t}" 149 150 if result != expected { 151 t.Errorf("comment should trigger look-ahead blank, got:\n%s\nwant:\n%s", result, expected) 152 } 153} 154 155func TestEnginePackageDeclaration(t *testing.T) { 156 events := []LineEvent{ 157 {Content: "package main", TrimmedContent: "package main", IsPackageDecl: true}, 158 {Content: "", TrimmedContent: "", IsBlank: true}, 159 {Content: "func main() {", TrimmedContent: "func main() {", HasASTInfo: true, StatementType: "func", IsTopLevel: true, IsScoped: true, IsStartLine: true, IsOpeningBrace: true}, 160 {Content: "}", TrimmedContent: "}", IsClosingBrace: true, HasASTInfo: true, StatementType: "func", IsTopLevel: true, IsScoped: true}, 161 } 162 formattingEngine := &Engine{CommentMode: CommentsFollow} 163 result := formatResult(formattingEngine, events) 164 expected := "package main\n\nfunc main() {\n}" 165 166 if result != expected { 167 t.Errorf("package should separate from func, got:\n%s\nwant:\n%s", result, expected) 168 } 169} 170 171func TestEngineFormatToString(t *testing.T) { 172 events := []LineEvent{ 173 {Content: "package main", TrimmedContent: "package main", IsPackageDecl: true}, 174 } 175 formattingEngine := &Engine{CommentMode: CommentsFollow} 176 result := formattingEngine.FormatToString(events) 177 178 if result != "package main\n" { 179 t.Errorf("FormatToString should end with newline, got: %q", result) 180 } 181} 182 183func TestEngineFindNextNonComment(t *testing.T) { 184 events := []LineEvent{ 185 {Content: "x", TrimmedContent: "x"}, 186 {Content: "", TrimmedContent: "", IsBlank: true}, 187 {Content: "// comment", TrimmedContent: "// comment", IsCommentOnly: true}, 188 {Content: "y", TrimmedContent: "y"}, 189 } 190 formattingEngine := &Engine{} 191 index := formattingEngine.findNextNonComment(events, 1) 192 193 if index != 3 { 194 t.Errorf("expected index 3, got %d", index) 195 } 196 197 index = formattingEngine.findNextNonComment(events, 4) 198 199 if index != -1 { 200 t.Errorf("expected -1 when past end, got %d", index) 201 } 202}