140 lines
2.8 KiB
Go
140 lines
2.8 KiB
Go
|
|
package htmltogo
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"fmt"
|
||
|
|
"go/ast"
|
||
|
|
"go/format"
|
||
|
|
"go/parser"
|
||
|
|
"go/printer"
|
||
|
|
"go/token"
|
||
|
|
"golang.org/x/tools/go/ast/astutil"
|
||
|
|
"slices"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
func Indent(input string) string {
|
||
|
|
fset := token.NewFileSet()
|
||
|
|
// Parse the code string into an AST
|
||
|
|
f, err := parser.ParseFile(fset, "", input, parser.ParseComments)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return input
|
||
|
|
}
|
||
|
|
|
||
|
|
htmgoComponentTypes := []string{
|
||
|
|
"h.Element",
|
||
|
|
"h.Page",
|
||
|
|
"h.Partial",
|
||
|
|
"h.Ren",
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, decl := range f.Decls {
|
||
|
|
switch c := decl.(type) {
|
||
|
|
case *ast.FuncDecl:
|
||
|
|
|
||
|
|
if c.Type.Results == nil || len(c.Type.Results.List) == 0 {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
returnType := c.Type.Results.List[0].Type
|
||
|
|
|
||
|
|
isHtmgoComponent := false
|
||
|
|
if v, ok := returnType.(*ast.StarExpr); ok {
|
||
|
|
if x, ok := v.X.(*ast.SelectorExpr); ok {
|
||
|
|
name := x.X.(*ast.Ident).Name
|
||
|
|
str := name + "." + x.Sel.Name
|
||
|
|
isHtmgoComponent = slices.Contains(htmgoComponentTypes, str)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if !isHtmgoComponent {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
var isHTag = func(n ast.Expr) bool {
|
||
|
|
switch argc := n.(type) {
|
||
|
|
// If the first argument is another node, add an indent
|
||
|
|
case *ast.CallExpr:
|
||
|
|
if v, ok := argc.Fun.(*ast.SelectorExpr); ok {
|
||
|
|
if v2, ok := v.X.(*ast.Ident); ok {
|
||
|
|
if v2.Name == "h" || v2.Name == "js" {
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
var indent = func(children []ast.Expr) []ast.Expr {
|
||
|
|
children = append(children, ast.NewIdent("INDENTME"))
|
||
|
|
return children
|
||
|
|
}
|
||
|
|
|
||
|
|
astutil.Apply(c.Body, nil, func(cursor *astutil.Cursor) bool {
|
||
|
|
switch n := cursor.Node().(type) {
|
||
|
|
case *ast.CallExpr:
|
||
|
|
newChildren := make([]ast.Expr, 0)
|
||
|
|
|
||
|
|
hasAnyHElements := false
|
||
|
|
|
||
|
|
for _, arg := range n.Args {
|
||
|
|
if isHTag(arg) {
|
||
|
|
hasAnyHElements = true
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for i, arg := range n.Args {
|
||
|
|
|
||
|
|
if len(n.Args) == 1 && isHTag(arg) {
|
||
|
|
newChildren = indent(newChildren)
|
||
|
|
newChildren = append(newChildren, arg)
|
||
|
|
newChildren = indent(newChildren)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
if !hasAnyHElements {
|
||
|
|
newChildren = append(newChildren, arg)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(n.Args) > 1 {
|
||
|
|
if i == 0 {
|
||
|
|
newChildren = indent(newChildren)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
newChildren = append(newChildren, arg)
|
||
|
|
if len(n.Args) > 1 {
|
||
|
|
newChildren = indent(newChildren)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
n.Args = newChildren
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
return true
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Convert the AST node to a string
|
||
|
|
var buf bytes.Buffer
|
||
|
|
if err := printer.Fprint(&buf, fset, f); err != nil {
|
||
|
|
fmt.Println("Error printing AST:", err)
|
||
|
|
return input
|
||
|
|
}
|
||
|
|
|
||
|
|
// Output the formatted code
|
||
|
|
indented := strings.ReplaceAll(buf.String(), "INDENTME,", "\n\t\t")
|
||
|
|
indented = strings.ReplaceAll(indented, ", INDENTME", ", \n\t\t")
|
||
|
|
|
||
|
|
formatted, err := format.Source([]byte(indented))
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return input
|
||
|
|
}
|
||
|
|
|
||
|
|
return string(formatted)
|
||
|
|
}
|