htmgo/tools/html-to-htmgo/htmltogo/indent.go
maddalax 8736c00fd5
htmgo - custom formatter (#47)
* format htmgo elements on save

* formatter updates

* ensure we maintain comments
2024-10-25 10:33:48 -05:00

139 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)
}