Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docextractor: correct docs, type signatures, and links #2595

Merged
merged 1 commit into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 133 additions & 35 deletions internal/repotools/cmd/docextractor/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,11 @@ func Extract(servicePath string, serviceDir fs.DirEntry, items map[string]jewelr
// extractType iterates through
func extractTypes(packageName string, types map[string]*ast.TypeSpec, items map[string]jewelryItem) error {
for kt, vt := range types {
summary := ""
if vt.Doc != nil {
summary = vt.Doc.Text()
}
typeName := vt.Name.Name

item := jewelryItem{
Name: typeName,
Summary: summary,
Summary: formatComment(vt.Doc),
Members: []jewelryItem{},
Tags: []string{},
OtherBlocks: map[string]string{},
Expand Down Expand Up @@ -134,26 +130,13 @@ func extractTypes(packageName string, types map[string]*ast.TypeSpec, items map[
break
}
fieldName := vf.Names[i].Name
var fieldItem jewelryItem
if vf.Doc == nil || vf.Doc.List == nil || vf.Doc.List[i] == nil {
fieldItem = jewelryItem{
Name: fieldName,
Tags: []string{},
OtherBlocks: map[string]string{},
Params: []jewelryParam{},
Members: []jewelryItem{},
Summary: "",
}

} else {
fieldItem = jewelryItem{
Name: fieldName,
Tags: []string{},
OtherBlocks: map[string]string{},
Params: []jewelryParam{},
Members: []jewelryItem{},
Summary: vf.Doc.List[i].Text,
}
fieldItem := jewelryItem{
Name: fieldName,
Tags: []string{},
OtherBlocks: map[string]string{},
Params: []jewelryParam{},
Members: []jewelryItem{},
Summary: formatComment(vf.Doc),
}
fieldItem.Type = jewelryItemKindField
fieldItem.BreadCrumbs = []breadCrumb{
Expand All @@ -170,15 +153,11 @@ func extractTypes(packageName string, types map[string]*ast.TypeSpec, items map[
Kind: jewelryItemKindField,
},
}
se, ok := vf.Type.(*ast.StarExpr)
if ok {
ident, ok := se.X.(*ast.Ident)
if ok {
fieldItem.Signature = typeSignature{
Signature: ident.Name,
}
}
fieldItem.Signature = typeSignature{
Signature: toSignature(vf.Type, packageName),
// Location is unused - links have to be embedded in signature
}

members = append(members, fieldItem)
}
}
Expand All @@ -189,6 +168,125 @@ func extractTypes(packageName string, types map[string]*ast.TypeSpec, items map[
return nil
}

// We've already converted the model's HTML to Go docs, now for ref docs we
// must convert back. We can't use the model's original docs directly because
// that doesn't include extra content we may inject at codegen.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you have an example of where we would add extra content at codegen on top of what we get from the service?

//
// Practically this is just a matter of converting lists and paragraphs, since
// that's really all Go docs do in terms of formatting. Links are left as-is.
// Note we don't bother with <ul> on lists since our API ref docs generator
// doesn't require them to render.
func formatComment(cg *ast.CommentGroup) string {
if cg == nil {
return ""
}

var inlist bool
var html, currp, currli string
for _, c := range cg.List {
line := c.Text
if line == "//" {
flushp(&html, &currp)
continue
}

line = strings.TrimPrefix(line, "// ")
if strings.HasPrefix(line, " ") && !inlist {
inlist = true
flushp(&html, &currp)
}
if !strings.HasPrefix(line, " ") && inlist {
inlist = false
flushli(&html, &currli)
}

if inlist {
if strings.HasPrefix(line, " -") {
if len(currli) > 0 {
flushli(&html, &currli)
}
currli = strings.TrimPrefix(line, " -") + " "
} else if strings.HasPrefix(line, " ") {
currli += strings.TrimPrefix(line, " ") + " "
}
} else {
currp += line + " "
}
}

if len(currp) > 0 {
flushp(&html, &currp)
}
if len(currli) > 0 {
flushli(&html, &currli)
}

return html
}

func flushp(dst, src *string) {
if len(*src) == 0 {
return
}
*dst = *dst + "<p>" + *src + "</p>"
*src = ""
}

func flushli(dst, src *string) {
if len(*src) == 0 {
return
}
*dst = *dst + "<li>" + *src + "</li>"
*src = ""
}

func toSignature(v ast.Expr, pkg string) string {
switch vv := v.(type) {
case *ast.Ident:
return fmt.Sprintf("%s", vv.Name)
case *ast.StarExpr:
return fmt.Sprintf("*%s", toSignature(vv.X, pkg))
case *ast.ArrayType:
return fmt.Sprintf("[]%s", toSignature(vv.Elt, pkg))
case *ast.MapType:
return fmt.Sprintf("map[string]%s", toSignature(vv.Value, pkg))
case *ast.SelectorExpr:
spkg := vv.X.(*ast.Ident).Name
if spkg == "types" {
return fmt.Sprintf("[%s.%s](-aws-sdk-client-%s!%s:Struct)", spkg, vv.Sel.Name, pkg, vv.Sel.Name)
}
// FUTURE: handle links to runtime
return fmt.Sprintf("%s.%s", spkg, vv.Sel.Name)
case *ast.FuncType:
return toFuncSignature(vv, pkg)
default:
return ""
}
}

func toFuncSignature(v *ast.FuncType, pkg string) string {
xpr := "func ("
if v.Params != nil {
for i, param := range v.Params.List {
xpr += toSignature(param.Type, pkg)
if i < len(v.Params.List)-1 {
xpr += ", "
}
}
}
xpr += ")"
if v.Results != nil {
xpr += " "
for i, param := range v.Results.List {
xpr += toSignature(param.Type, pkg)
if i < len(v.Params.List)-1 {
xpr += ", "
}
}
}
return xpr
}

func extractFunctions(packageName string, types map[string]*ast.TypeSpec, functions map[string]*ast.FuncDecl, items map[string]jewelryItem) error {
for _, vf := range functions {

Expand All @@ -202,7 +300,7 @@ func extractFunctions(packageName string, types map[string]*ast.TypeSpec, functi
OtherBlocks: map[string]string{},
Params: []jewelryParam{},
Members: []jewelryItem{},
Summary: vf.Doc.Text(),
Summary: formatComment(vf.Doc),
BreadCrumbs: []breadCrumb{
{
Name: packageName,
Expand Down Expand Up @@ -270,7 +368,7 @@ func extractFunctions(packageName string, types map[string]*ast.TypeSpec, functi
OtherBlocks: map[string]string{},
Params: params,
Returns: returns,
Summary: vf.Doc.Text(),
Summary: formatComment(vf.Doc),
BreadCrumbs: []breadCrumb{
{
Name: packageName,
Expand Down
8 changes: 1 addition & 7 deletions internal/repotools/cmd/docextractor/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,20 @@ import (
)

func getPackageItem(packageName string, files map[string]*ast.File) (jewelryItem, error) {
packageDoc := ""
docRe := regexp.MustCompile(`.*/doc.go`)

for k, f := range files {
matched := docRe.Match([]byte(k))
if !matched {
continue
}
if f.Doc != nil && f.Doc.List != nil {
for _, line := range f.Doc.List {
packageDoc += line.Text
}
}
return jewelryItem{
Tags: []string{},
OtherBlocks: map[string]string{},
Members: []jewelryItem{},
Params: []jewelryParam{},
BreadCrumbs: []breadCrumb{},
Summary: packageDoc,
Summary: formatComment(f.Doc),
}, nil
}
return jewelryItem{}, fmt.Errorf("no doc.go")
Expand Down
Loading