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

automatically link type names found in the doc comments #30

Open
jpeach opened this issue Dec 22, 2020 · 2 comments
Open

automatically link type names found in the doc comments #30

jpeach opened this issue Dec 22, 2020 · 2 comments

Comments

@jpeach
Copy link
Contributor

jpeach commented Dec 22, 2020

It would be a great enhancement to automatically link any API type names that are found in the doc comments. Adding a markdown link works, but it doesn't render properly in godoc, so if we can add the link in the generator we can have best of both worlds.

@ahmetb
Copy link
Owner

ahmetb commented Dec 22, 2020

I can't think of a clean way of linking from parts of godoc comments without over-engineering.

Such a smart detection might also be susceptible to issues like:

  • generic type names such as Service could be linked unintentionally from all over the place

  • type A is in multiple pkgs (i.e. multiple apiversions) but we can't easily figure out which one to link to.

If we can live without, that'd be preferable. If you definitely want to do it, probably should be opt-in with an explicit syntax that doesn't break godoc too much.

@jpeach
Copy link
Contributor Author

jpeach commented Dec 23, 2020

Yeh, I think you are right; this is more problematic the more you think about it.

I poked at this using strings.Replacer which kinda works because all the types are in a single package (so the Name.Name field is unique). However, replacer does string replacements not word replacements, which is sometimes what you want. ListenerReasonDegradedRoutes gets the Listener part linked to the Listener type, which is totally unwanted, but Listeners also gets its Listener part linked, which could be what you want, but you'd want the whole word to be the link, not just the Listener prefix. There's a few variations of this.

If we switch to something like bufio.Scanwords we would have to deal with preserving the whitespace, which will be complicated. Still would need to have heuristics to get variants like "Listeners" and Gateway(s)" to all link to the same target.

There's no package context from renderComments which would result in the "easily figure out which one to link to" problem you point out above.

Here's a super-naive implementation:

diff --git main.go main.go
index 95193b8..6c7bb71 100644
--- main.go
+++ main.go
@@ -338,14 +338,25 @@ func isLocalType(t *types.Type, typePkgMap map[*types.Type]*apiPackage) bool {
        return ok
 }

-func renderComments(s []string, markdown bool) string {
+func renderComments(s []string, typePkgMap map[*types.Type]*apiPackage, markdown bool) string {
        s = filterCommentTags(s)
        doc := strings.Join(s, "\n")

        if markdown {
+               var replacements []string
+
+               for t := range typePkgMap {
+                       if lnk, err := linkForType(t, generatorConfig{}, typePkgMap); err == nil {
+                               klog.Infof("type %q, link %q", t.Name.Name, lnk)
+                               replacements = append(replacements, t.Name.Name, fmt.Sprintf("[%s](%s)", t.Name.Name, lnk))
+                       }
+               }
+
+               r := strings.NewReplacer(replacements...)
+
                // TODO(ahmetb): when a comment includes stuff like "http://<service>"
                // we treat this as a HTML tag with markdown renderer below. solve this.
-               return string(blackfriday.Run([]byte(doc)))
+               return string(blackfriday.Run([]byte(r.Replace(doc))))
        }
        return nl2br(doc)
 }
@@ -649,7 +660,7 @@ func render(w io.Writer, pkgs []*apiPackage, config generatorConfig) error {
                "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, typePkgMap, !config.MarkdownDisabled) },
                "packageDisplayName": func(p *apiPackage) string { return p.identifier() },
                "apiGroup":           func(t *types.Type) string { return apiGroupForType(t, typePkgMap) },
                "packageAnchorID": func(p *apiPackage) string {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants