diff --git a/content/static/css/stylesheet.css b/content/static/css/stylesheet.css
index 75fd9ffe5..ac00d0589 100644
--- a/content/static/css/stylesheet.css
+++ b/content/static/css/stylesheet.css
@@ -1195,6 +1195,13 @@ code {
.Documentation h3 a.Documentation-source {
opacity: 1;
}
+
+.Documentation h3 a.Documentation-uses {
+ color: #666;
+ font-size: 0.8em;
+ opacity: 0;
+}
+
.Documentation h2:hover a,
.Documentation h3:hover a,
.Documentation summary:hover a,
diff --git a/internal/godoc/dochtml/dochtml.go b/internal/godoc/dochtml/dochtml.go
index e2d8d5942..f1315d17e 100644
--- a/internal/godoc/dochtml/dochtml.go
+++ b/internal/godoc/dochtml/dochtml.go
@@ -57,6 +57,7 @@ type RenderOptions struct {
// string to indicate that a given file should not be linked.
FileLinkFunc func(file string) (url string)
SourceLinkFunc func(ast.Node) string
+ UsesLinkFunc func(defParts []string) string
// ModInfo optionally specifies information about the module the package
// belongs to in order to render module-related documentation.
ModInfo *ModuleInfo
@@ -118,6 +119,9 @@ func Render(ctx context.Context, fset *token.FileSet, p *doc.Package, opt Render
sourceLink := func(name string, node ast.Node) safehtml.HTML {
return linkHTML(name, opt.SourceLinkFunc(node), "Documentation-source")
}
+ usesLink := func(title string, defParts ...string) safehtml.HTML {
+ return sourcegraphLinkHTML("Uses", opt.UsesLinkFunc(defParts), "Documentation-uses", title)
+ }
if experiment.IsActive(ctx, internal.ExperimentUnitPage) {
if p.Doc == "" &&
@@ -139,6 +143,7 @@ func Render(ctx context.Context, fset *token.FileSet, p *doc.Package, opt Render
"render_code": r.CodeHTML,
"file_link": fileLink,
"source_link": sourceLink,
+ "uses_link": usesLink,
})
data := struct {
RootURL string
@@ -180,6 +185,15 @@ func linkHTML(name, url, class string) safehtml.HTML {
return render.ExecuteToHTML(render.LinkTemplate, render.Link{Class: class, Href: url, Text: name})
}
+// sourcegraphLinkHTML returns an HTML-formatted name linked to the given Sourcegraph URL.
+// A title is needed to generate the tooltip to distinguish between different components of the code.
+func sourcegraphLinkHTML(name, url, class, title string) safehtml.HTML {
+ if url == "" {
+ return safehtml.HTMLEscaped(name)
+ }
+ return render.ExecuteToHTML(render.SourcegraphLinkTemplate, render.SourcegraphLink{Class: class, Href: url, Text: name, Title: title})
+}
+
// examples is an internal representation of all package examples.
type examples struct {
List []*example // sorted by ParentID
diff --git a/internal/godoc/dochtml/dochtml_test.go b/internal/godoc/dochtml/dochtml_test.go
index c46537127..173ef05fc 100644
--- a/internal/godoc/dochtml/dochtml_test.go
+++ b/internal/godoc/dochtml/dochtml_test.go
@@ -28,6 +28,7 @@ func TestRender(t *testing.T) {
rawDoc, err := Render(context.Background(), fset, d, RenderOptions{
FileLinkFunc: func(string) string { return "file" },
SourceLinkFunc: func(ast.Node) string { return "src" },
+ UsesLinkFunc: func([]string) string { return "uses" },
})
if err != nil {
t.Fatal(err)
diff --git a/internal/godoc/dochtml/internal/render/idents.go b/internal/godoc/dochtml/internal/render/idents.go
index b7911f1b3..e52cd91e8 100644
--- a/internal/godoc/dochtml/internal/render/idents.go
+++ b/internal/godoc/dochtml/internal/render/idents.go
@@ -318,6 +318,14 @@ type Link struct {
var LinkTemplate = template.Must(template.New("link").Parse(
`{{.Text}}`))
+type SourcegraphLink struct {
+ Href, Text, Class string
+ Title string // title for tooltip when the user's cursor hovers
+}
+
+var SourcegraphLinkTemplate = template.Must(template.New("sourcegraph_link").Parse(
+ `{{.Text}}`))
+
// lookup looks up a dot-separated identifier.
// E.g., "pkg", "pkg.Var", "Recv.Method", "Struct.Field", "pkg.Struct.Field"
func (r identifierResolver) lookup(id string) (pkgPath, name string, ok bool) {
diff --git a/internal/godoc/dochtml/legacy_body.go b/internal/godoc/dochtml/legacy_body.go
index 4d5a9e90e..9bea060b3 100644
--- a/internal/godoc/dochtml/legacy_body.go
+++ b/internal/godoc/dochtml/legacy_body.go
@@ -90,7 +90,7 @@ const legacyTmplBody = `
{{- range .Funcs -}}
{{- $id := safe_id .Name -}}
- {{"\n"}}
+ {{"\n"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
@@ -107,7 +107,7 @@ const legacyTmplBody = `
{{- $tname := .Name -}}
{{- $id := safe_id .Name -}}
- {{"\n"}}
+ {{"\n"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
@@ -135,7 +135,7 @@ const legacyTmplBody = `
{{- range .Funcs -}}
{{- $id := safe_id .Name -}}
- {{"\n"}}
+ {{"\n"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
@@ -148,7 +148,7 @@ const legacyTmplBody = `
{{- $name := (printf "%s.%s" $tname .Name) -}}
{{- $id := (safe_id $name) -}}
- {{"\n"}}
+ {{"\n"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
diff --git a/internal/godoc/dochtml/legacy_template_test.go b/internal/godoc/dochtml/legacy_template_test.go
index 62443d2eb..312724fd0 100644
--- a/internal/godoc/dochtml/legacy_template_test.go
+++ b/internal/godoc/dochtml/legacy_template_test.go
@@ -254,7 +254,7 @@ const fullTemplate = `{{- "" -}}
{{- range .Funcs -}}
{{- $id := safe_id .Name -}}
- {{"\n"}}
+ {{"\n"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
@@ -271,7 +271,7 @@ const fullTemplate = `{{- "" -}}
{{- $tname := .Name -}}
{{- $id := safe_id .Name -}}
- {{"\n"}}
+ {{"\n"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
@@ -299,7 +299,7 @@ const fullTemplate = `{{- "" -}}
{{- range .Funcs -}}
{{- $id := safe_id .Name -}}
- {{"\n"}}
+ {{"\n"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
@@ -312,7 +312,7 @@ const fullTemplate = `{{- "" -}}
{{- $name := (printf "%s.%s" $tname .Name) -}}
{{- $id := (safe_id $name) -}}
- {{"\n"}}
+ {{"\n"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
diff --git a/internal/godoc/dochtml/template.go b/internal/godoc/dochtml/template.go
index 800caa914..2b2387594 100644
--- a/internal/godoc/dochtml/template.go
+++ b/internal/godoc/dochtml/template.go
@@ -49,6 +49,7 @@ var tmpl = map[string]interface{}{
"render_code": (*render.Renderer)(nil).CodeHTML,
"file_link": func() string { return "" },
"source_link": func() string { return "" },
+ "uses_link": func() string { return "" },
"play_url": func(*doc.Example) string { return "" },
"safe_id": render.SafeGoID,
}
diff --git a/internal/godoc/render.go b/internal/godoc/render.go
index 75f59c667..d042b8187 100644
--- a/internal/godoc/render.go
+++ b/internal/godoc/render.go
@@ -119,10 +119,17 @@ func (p *Package) Render(ctx context.Context, innerPath string, sourceInfo *sour
}
return sourceInfo.FileURL(path.Join(innerPath, filename))
}
+ usesLinkFunc := func(defParts []string) string {
+ if sourceInfo == nil {
+ return ""
+ }
+ return sourceInfo.UsesURL(modInfo.ModulePath, importPath, defParts)
+ }
docHTML, err := dochtml.Render(ctx, p.Fset, d, dochtml.RenderOptions{
FileLinkFunc: fileLinkFunc,
SourceLinkFunc: sourceLinkFunc,
+ UsesLinkFunc: usesLinkFunc,
ModInfo: modInfo,
Limit: int64(MaxDocumentationHTML),
})
diff --git a/internal/source/source.go b/internal/source/source.go
index c1981ec9a..629ecd934 100644
--- a/internal/source/source.go
+++ b/internal/source/source.go
@@ -25,6 +25,7 @@ import (
"encoding/json"
"fmt"
"net/http"
+ "net/url"
"path"
"regexp"
"strconv"
@@ -112,6 +113,37 @@ func (i *Info) LineURL(pathname string, line int) string {
})
}
+// UsesURL returns a URL redirecting to Sourcegraph site showing the usage for a particular component of the code.
+func (i *Info) UsesURL(modulePath string, importPath string, defParts []string) string {
+ sourcegraphBaseURL := "https://sourcegraph.com/-/godoc/refs?"
+
+ var def string
+ switch len(defParts) {
+ case 1:
+ def = defParts[0]
+
+ case 2:
+ typeName, methodName := defParts[0], defParts[1]
+ typeName = strings.TrimPrefix(typeName, "*")
+ def = typeName + "/" + methodName
+
+ default:
+ panic(fmt.Errorf("%v defParts, want 1 or 2", len(defParts)))
+ }
+
+ repo := strings.TrimPrefix(modulePath, "https://")
+ pkg := strings.TrimPrefix(importPath, "https://")
+
+ q := url.Values{
+ "repo": []string{repo},
+ "pkg": []string{pkg},
+ "def": []string{def},
+ "source": []string{"pkgsite"},
+ }
+
+ return sourcegraphBaseURL + q.Encode()
+}
+
// RawURL returns a URL referring to the raw contents of a file relative to the
// module's home directory.
func (i *Info) RawURL(pathname string) string {