Skip to content

Instantly share code, notes, and snippets.

@soypat
Created December 2, 2021 19:40
Show Gist options
  • Save soypat/f29228d59e62472956e1fc8893386c3a to your computer and use it in GitHub Desktop.
Save soypat/f29228d59e62472956e1fc8893386c3a to your computer and use it in GitHub Desktop.
Interface documentation generation from methods.
package main
type Impl struct{}
func (Impl) X() {
}
type Xer interface{ X() } // X This is the xer implementation
// Second line
package main
type Impl struct{}
// X This is the xer implementation
// Second line
func (Impl) X() {}
type Xer interface {
X()
}
package main
import (
_ "embed"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"log"
"os"
"strings"
)
//go:embed in.go
var src string
func main() {
var comments = make(map[string]*ast.CommentGroup)
// var comments []*ast.CommentGroup
var funcs []*ast.FuncDecl
var typ []*ast.Ident
var iface []*ast.Ident
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, "in.go", src, parser.ParseComments)
if err != nil {
log.Fatal(err)
}
context := ""
ast.Inspect(node, func(n ast.Node) bool {
fmt.Printf("%T %+v\n", n, n)
// return true
switch n := n.(type) {
default:
context = ""
case *ast.File, *ast.GenDecl:
return true
case *ast.TypeSpec:
context = "type"
return true
case *ast.FuncDecl:
funcs = append(funcs, n)
if n.Doc != nil && n.Recv != nil {
comments[n.Name.Name] = n.Doc
}
return false
case *ast.InterfaceType:
if context == "type" {
iface = append(iface, typ[len(typ)-1])
typ = typ[:len(typ)-1]
}
context = "interface"
return true
case *ast.FieldList:
if context != "interface" {
return false
}
return true
case *ast.Field:
if len(n.Names) != 1 {
return false
}
com, ok := comments[n.Names[0].Name]
if ok {
n.Doc = cloneSingleCG(com)
// n.Comment = com
}
}
return false
})
fmt.Println("Comments:")
for name, c := range comments {
fmt.Println(name, c.Text())
}
fmt.Println("typ:")
for _, c := range typ {
fmt.Println(c.Name)
}
fmt.Println("iface:")
for _, c := range iface {
fmt.Println(c.Name)
}
fp, err := os.Create("_out.go")
if err != nil {
log.Fatal(err)
}
defer fp.Close()
fset2 := token.NewFileSet()
err = printer.Fprint(fp, fset2, node)
if err != nil {
log.Fatal(err)
}
}
func addComments(cm map[string]*ast.CommentGroup, comments []*ast.CommentGroup) {
for _, comment := range comments {
text := comment.Text()
if text == "" {
continue // no comment contents.
}
c := strings.TrimSpace(strings.SplitAfterN(text, " ", 2)[0])
cm[c] = comment
}
}
func cloneCommentGroup(src []*ast.CommentGroup) (dst []*ast.CommentGroup) {
dst = make([]*ast.CommentGroup, len(src))
for i := range src {
dst[i] = cloneSingleCG(src[i])
}
return dst
}
func cloneSingleCG(src *ast.CommentGroup) (dst *ast.CommentGroup) {
dst = &ast.CommentGroup{List: make([]*ast.Comment, len(src.List))}
for j := range src.List {
dst.List[j] = &ast.Comment{Slash: src.List[j].Slash, Text: src.List[j].Text}
}
return dst
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment