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

Option and code to generate AsciiDoc friendly output #45

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/ahmetb/gen-crd-api-reference-docs
module github.com/djencks/gen-crd-api-reference-docs

go 1.15

Expand Down
65 changes: 48 additions & 17 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ type generatorConfig struct {
// MarkdownDisabled controls markdown rendering for comment lines.
MarkdownDisabled bool `json:"markdownDisabled"`

// AsciiDoc controls HTML/text output and some comment escaping.
AsciiDoc bool `json:"asciiDoc"`

// GitCommitDisabled causes the git commit information to be excluded from the output.
GitCommitDisabled bool `json:"gitCommitDisabled"`
}
Expand Down Expand Up @@ -150,9 +153,11 @@ func main() {
return "", errors.Wrap(err, "failed to render the result")
}

// remove trailing whitespace from each html line for markdown renderers
s := regexp.MustCompile(`(?m)^\s+`).ReplaceAllString(b.String(), "")
return s, nil
if !config.AsciiDoc {
// remove trailing whitespace from each html line for markdown renderers
return regexp.MustCompile(`(?m)^\s+`).ReplaceAllString(b.String(), ""), nil
}
return b.String(), nil
}

if *flOutFile != "" {
Expand Down Expand Up @@ -346,7 +351,7 @@ func isLocalType(t *types.Type, typePkgMap map[*types.Type]*apiPackage) bool {
return ok
}

func renderComments(s []string, markdown bool) string {
func renderComments(s []string, markdown bool, asciiDoc bool) string {
s = filterCommentTags(s)
doc := strings.Join(s, "\n")

Expand All @@ -355,7 +360,17 @@ func renderComments(s []string, markdown bool) string {
// we treat this as a HTML tag with markdown renderer below. solve this.
return string(blackfriday.Run([]byte(doc)))
}
return nl2br(doc)

if asciiDoc{
doc = strings.Replace(doc, "#", "\\#", strings.Count(doc, "#") - 1) // avoid highlighting
non_attribute_re := regexp.MustCompile("{([^} ]+})")
doc = non_attribute_re.ReplaceAllString(doc, "\\{$1") // avoid interpretation as attribute
doc = strings.ReplaceAll(doc, "|", "{vbar}") // avoid alternation interpreted as column separator
} else {
doc = nl2br(doc)
}

return doc
}

func safe(s string) template.HTML { return template.HTML(s) }
Expand Down Expand Up @@ -393,7 +408,7 @@ func apiGroupForType(t *types.Type, typePkgMap map[*types.Type]*apiPackage) stri

// anchorIDForLocalType returns the #anchor string for the local type
func anchorIDForLocalType(t *types.Type, typePkgMap map[*types.Type]*apiPackage) string {
return fmt.Sprintf("%s.%s", apiGroupForType(t, typePkgMap), t.Name.Name)
return sanitizeId(fmt.Sprintf("%s.%s", apiGroupForType(t, typePkgMap), t.Name.Name))
Copy link
Owner

@ahmetb ahmetb Jan 30, 2022

Choose a reason for hiding this comment

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

this is a breaking change as I understand it, hence not quite desirable

Copy link
Author

Choose a reason for hiding this comment

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

I wasn't sure if you'd consider this a breaking change: I have no problem changing it. I see three options:

  • introduce a sanitizeId function called from templates, similar to the asciiDocAttributeEscape method I introduced. This is certainly the least complex and most flexible, but increases the API exposed to templates.
  • sanitize ids based on the asciiDoc flag. This will probably end up with the most complex code, and will be the least flexible: perhaps users wanting html generation will want to have simplified IDs.
  • introduce a sanitizeId flag.

I lean towards the first solution, exposing a sanitizeId function to templates. What is your preference?

Copy link
Author

Choose a reason for hiding this comment

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

I went ahead and pushed a commit to expose a sanitizeId method. I had to add a tryDereference to anchorIDForLocalType: I think exposing this to templates without the dereference was a bug.

Travis was using Go 1.11: I updated to 1.12 to allow use of strings.ReplaceAll. I'm not sure if there are other more modern API usages. Is updating the Go version OK?

}

// linkForType returns an anchor to the type if it can be generated. returns
Expand Down Expand Up @@ -648,26 +663,30 @@ func constantsOfType(t *types.Type, pkg *apiPackage) []*types.Type {
return sortTypes(constants)
}

func sanitizeId(s string) string {
return "_" + regexp.MustCompile("[./ -]+").ReplaceAllString(s, "_")
}

func render(w io.Writer, pkgs []*apiPackage, config generatorConfig) error {
references := findTypeReferences(pkgs)
typePkgMap := extractTypeToPackageMap(pkgs)

t, err := template.New("").Funcs(map[string]interface{}{
funcMap := map[string]interface{}{
"isExportedType": isExportedType,
"fieldName": fieldName,
"fieldEmbedded": fieldEmbedded,
"typeIdentifier": func(t *types.Type) string { return typeIdentifier(t) },
"typeDisplayName": func(t *types.Type) string { return typeDisplayName(t, config, typePkgMap) },
"visibleTypes": func(t []*types.Type) []*types.Type { return visibleTypes(t, config) },
"renderComments": func(s []string) string { return renderComments(s, !config.MarkdownDisabled) },
"renderComments": func(s []string) string { return renderComments(s, !config.MarkdownDisabled, config.AsciiDoc) },
"packageDisplayName": func(p *apiPackage) string { return p.identifier() },
"apiGroup": func(t *types.Type) string { return apiGroupForType(t, typePkgMap) },
"packageAnchorID": func(p *apiPackage) string {
// TODO(ahmetb): currently this is the same as packageDisplayName
// func, and it's fine since it retuns valid DOM id strings like
// 'serving.knative.dev/v1alpha1' which is valid per HTML5, except
// spaces, so just trim those.
return strings.Replace(p.identifier(), " ", "", -1)
return sanitizeId(p.identifier())
},
"linkForType": func(t *types.Type) string {
v, err := linkForType(t, config, typePkgMap)
Expand All @@ -682,22 +701,34 @@ func render(w io.Writer, pkgs []*apiPackage, config generatorConfig) error {
"sortedTypes": sortTypes,
"typeReferences": func(t *types.Type) []*types.Type { return typeReferences(t, config, references) },
"hiddenMember": func(m types.Member) bool { return hiddenMember(m, config) },
"isLocalType": isLocalType,
"isLocalType": func(t *types.Type) bool { return isLocalType(t, typePkgMap) },
"isOptionalMember": isOptionalMember,
"constantsOfType": func(t *types.Type) []*types.Type { return constantsOfType(t, typePkgMap[t]) },
}).ParseGlob(filepath.Join(*flTemplateDir, "*.tpl"))
if err != nil {
return errors.Wrap(err, "parse error")
"asciiDocAttributeEscape": func(s string) string { return strings.ReplaceAll(s, "]", "\\]") },
}

var gitCommit []byte
if !config.GitCommitDisabled {
gitCommit, _ = exec.Command("git", "rev-parse", "--short", "HEAD").Output()
}

return errors.Wrap(t.ExecuteTemplate(w, "packages", map[string]interface{}{
data := map[string]interface{}{
"packages": pkgs,
"config": config,
"gitCommit": strings.TrimSpace(string(gitCommit)),
}), "template execution error")
}

if config.AsciiDoc {
t, err := texttemplate.New("").Funcs(funcMap).ParseGlob(filepath.Join(*flTemplateDir, "*.tpl"))
if err != nil {
return errors.Wrap(err, "parse error")
}

return errors.Wrap(t.ExecuteTemplate(w, "packages", data), "template execution error")
} else {
t, err := template.New("").Funcs(funcMap).ParseGlob(filepath.Join(*flTemplateDir, "*.tpl"))
if err != nil {
return errors.Wrap(err, "parse error")
}

return errors.Wrap(t.ExecuteTemplate(w, "packages", data), "template execution error")
}
}