馃殌 Grammar-Aware Code Formatter: Structure through separation (supports Go, JavaScript, TypeScript, JSX, and TSX)
go
formatter
code-formatter
javascript
typescript
jsx
tsx
1package main
2
3import (
4 "bytes"
5 "github.com/Fuwn/iku/engine"
6 "go/format"
7 "go/parser"
8 "go/token"
9)
10
11type GoAdapter struct{}
12
13func (a *GoAdapter) Analyze(source []byte) ([]byte, []engine.LineEvent, error) {
14 formattedSource, err := format.Source(source)
15
16 if err != nil {
17 return nil, nil, err
18 }
19
20 tokenFileSet := token.NewFileSet()
21 parsedFile, err := parser.ParseFile(tokenFileSet, "", formattedSource, parser.ParseComments)
22
23 if err != nil {
24 return nil, nil, err
25 }
26
27 formatter := &Formatter{}
28 lineInformationMap := formatter.buildLineInfo(tokenFileSet, parsedFile)
29 sourceByteLines := bytes.Split(formattedSource, []byte("\n"))
30 events := make([]engine.LineEvent, len(sourceByteLines))
31 insideRawString := false
32
33 for lineIndex, currentLineBytes := range sourceByteLines {
34 currentLine := string(currentLineBytes)
35 backtickCount := countRawStringDelimiters(currentLine)
36 wasInsideRawString := insideRawString
37
38 if backtickCount%2 == 1 {
39 insideRawString = !insideRawString
40 }
41
42 event := engine.NewLineEvent(currentLine)
43
44 if wasInsideRawString {
45 event.InRawString = true
46 events[lineIndex] = event
47
48 continue
49 }
50
51 if event.IsBlank {
52 events[lineIndex] = event
53
54 continue
55 }
56
57 lineNumber := lineIndex + 1
58 currentInformation := lineInformationMap[lineNumber]
59
60 if currentInformation != nil {
61 event.HasASTInfo = true
62 event.StatementType = currentInformation.statementType
63 event.IsTopLevel = currentInformation.isTopLevel
64 event.IsScoped = currentInformation.isScoped
65 event.IsStartLine = currentInformation.isStartLine
66 }
67
68 event.IsClosingBrace = isClosingBrace(currentLine)
69 event.IsOpeningBrace = isOpeningBrace(currentLine)
70 event.IsCaseLabel = isCaseLabel(currentLine)
71 event.IsCommentOnly = isCommentOnly(currentLine)
72 event.IsPackageDecl = isPackageLine(event.TrimmedContent)
73 events[lineIndex] = event
74 }
75
76 return formattedSource, events, nil
77}
78
79func MapCommentMode(mode CommentMode) engine.CommentMode {
80 switch mode {
81 case CommentsFollow:
82 return engine.CommentsFollow
83 case CommentsPrecede:
84 return engine.CommentsPrecede
85 case CommentsStandalone:
86 return engine.CommentsStandalone
87 default:
88 return engine.CommentsFollow
89 }
90}