Skip to content

Commit 049a7d9

Browse files
x/pkgsite: add sourcegraph redirection link for types, functions, and methods
The godoc.org has a Uses link that redirects to Sourcegraph website for showing the usage/callers of types, functions, and methods. This PR implements the same funcitonality for pkg.go.dev. Much inspired by Quinn's (sqs) PR on gddo (github.com/golang/gddo/pull/259). A short demo can be seen at https://youtu.be/OathLmQVM5g. Fixes #39703
1 parent aa97eaf commit 049a7d9

File tree

9 files changed

+78
-8
lines changed

9 files changed

+78
-8
lines changed

content/static/css/stylesheet.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,13 @@ code {
11951195
.Documentation h3 a.Documentation-source {
11961196
opacity: 1;
11971197
}
1198+
1199+
.Documentation h3 a.Documentation-uses {
1200+
color: #666;
1201+
font-size: 0.8em;
1202+
opacity: 0;
1203+
}
1204+
11981205
.Documentation h2:hover a,
11991206
.Documentation h3:hover a,
12001207
.Documentation summary:hover a,

internal/godoc/dochtml/dochtml.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ type RenderOptions struct {
5757
// string to indicate that a given file should not be linked.
5858
FileLinkFunc func(file string) (url string)
5959
SourceLinkFunc func(ast.Node) string
60+
UsesLinkFunc func(defParts []string) string
6061
// ModInfo optionally specifies information about the module the package
6162
// belongs to in order to render module-related documentation.
6263
ModInfo *ModuleInfo
@@ -118,6 +119,9 @@ func Render(ctx context.Context, fset *token.FileSet, p *doc.Package, opt Render
118119
sourceLink := func(name string, node ast.Node) safehtml.HTML {
119120
return linkHTML(name, opt.SourceLinkFunc(node), "Documentation-source")
120121
}
122+
usesLink := func(title string, defParts ...string) safehtml.HTML {
123+
return sourcegraphLinkHTML("Uses", opt.UsesLinkFunc(defParts), "Documentation-uses", title)
124+
}
121125

122126
if experiment.IsActive(ctx, internal.ExperimentUnitPage) {
123127
if p.Doc == "" &&
@@ -139,6 +143,7 @@ func Render(ctx context.Context, fset *token.FileSet, p *doc.Package, opt Render
139143
"render_code": r.CodeHTML,
140144
"file_link": fileLink,
141145
"source_link": sourceLink,
146+
"uses_link": usesLink,
142147
})
143148
data := struct {
144149
RootURL string
@@ -180,6 +185,15 @@ func linkHTML(name, url, class string) safehtml.HTML {
180185
return render.ExecuteToHTML(render.LinkTemplate, render.Link{Class: class, Href: url, Text: name})
181186
}
182187

188+
// sourcegraphLinkHTML returns an HTML-formatted name linked to the given Sourcegraph URL.
189+
// A title is needed to generate the tooltip to distinguish between different components of the code.
190+
func sourcegraphLinkHTML(name, url, class, title string) safehtml.HTML {
191+
if url == "" {
192+
return safehtml.HTMLEscaped(name)
193+
}
194+
return render.ExecuteToHTML(render.SourcegraphLinkTemplate, render.SourcegraphLink{Class: class, Href: url, Text: name, Title: title})
195+
}
196+
183197
// examples is an internal representation of all package examples.
184198
type examples struct {
185199
List []*example // sorted by ParentID

internal/godoc/dochtml/dochtml_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func TestRender(t *testing.T) {
2828
rawDoc, err := Render(context.Background(), fset, d, RenderOptions{
2929
FileLinkFunc: func(string) string { return "file" },
3030
SourceLinkFunc: func(ast.Node) string { return "src" },
31+
UsesLinkFunc: func([]string) string { return "uses" },
3132
})
3233
if err != nil {
3334
t.Fatal(err)

internal/godoc/dochtml/internal/render/idents.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,14 @@ type Link struct {
318318
var LinkTemplate = template.Must(template.New("link").Parse(
319319
`<a {{with .Class}}class="{{.}}" {{end}}href="{{.Href}}">{{.Text}}</a>`))
320320

321+
type SourcegraphLink struct {
322+
Href, Text, Class string
323+
Title string // title for tooltip when the user's cursor hovers
324+
}
325+
326+
var SourcegraphLinkTemplate = template.Must(template.New("sourcegraph_link").Parse(
327+
`<a class="{{.Class}}" title="{{.Title}}" href="{{.Href}}">{{.Text}}</a>`))
328+
321329
// lookup looks up a dot-separated identifier.
322330
// E.g., "pkg", "pkg.Var", "Recv.Method", "Struct.Field", "pkg.Struct.Field"
323331
func (r identifierResolver) lookup(id string) (pkgPath, name string, ok bool) {

internal/godoc/dochtml/legacy_body.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ const legacyTmplBody = `
9090
{{- range .Funcs -}}
9191
<div class="Documentation-function">
9292
{{- $id := safe_id .Name -}}
93-
<h3 tabindex="-1" id="{{$id}}" data-kind="function" class="Documentation-functionHeader">func {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
93+
<h3 tabindex="-1" id="{{$id}}" data-kind="function" class="Documentation-functionHeader">func {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a> {{uses_link "List Function Callers" .Name}}</h3>{{"\n"}}
9494
{{- $out := render_decl .Doc .Decl -}}
9595
{{- $out.Decl -}}
9696
{{- $out.Doc -}}
@@ -107,7 +107,7 @@ const legacyTmplBody = `
107107
<div class="Documentation-type">
108108
{{- $tname := .Name -}}
109109
{{- $id := safe_id .Name -}}
110-
<h3 tabindex="-1" id="{{$id}}" data-kind="type" class="Documentation-typeHeader">type {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
110+
<h3 tabindex="-1" id="{{$id}}" data-kind="type" class="Documentation-typeHeader">type {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a> {{uses_link "List Uses of This Type" .Name}}</h3>{{"\n"}}
111111
{{- $out := render_decl .Doc .Decl -}}
112112
{{- $out.Decl -}}
113113
{{- $out.Doc -}}
@@ -135,7 +135,7 @@ const legacyTmplBody = `
135135
{{- range .Funcs -}}
136136
<div class="Documentation-typeFunc">
137137
{{- $id := safe_id .Name -}}
138-
<h3 tabindex="-1" id="{{$id}}" data-kind="function" class="Documentation-typeFuncHeader">func {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
138+
<h3 tabindex="-1" id="{{$id}}" data-kind="function" class="Documentation-typeFuncHeader">func {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a> {{uses_link "List Function Callers" .Name}}</h3>{{"\n"}}
139139
{{- $out := render_decl .Doc .Decl -}}
140140
{{- $out.Decl -}}
141141
{{- $out.Doc -}}
@@ -148,7 +148,7 @@ const legacyTmplBody = `
148148
<div class="Documentation-typeMethod">
149149
{{- $name := (printf "%s.%s" $tname .Name) -}}
150150
{{- $id := (safe_id $name) -}}
151-
<h3 tabindex="-1" id="{{$id}}" data-kind="method" class="Documentation-typeMethodHeader">func ({{.Recv}}) {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
151+
<h3 tabindex="-1" id="{{$id}}" data-kind="method" class="Documentation-typeMethodHeader">func ({{.Recv}}) {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a> {{uses_link "List Method Callers" .Recv .Name}}</h3>{{"\n"}}
152152
{{- $out := render_decl .Doc .Decl -}}
153153
{{- $out.Decl -}}
154154
{{- $out.Doc -}}

internal/godoc/dochtml/legacy_template_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ const fullTemplate = `{{- "" -}}
254254
{{- range .Funcs -}}
255255
<div class="Documentation-function">
256256
{{- $id := safe_id .Name -}}
257-
<h3 tabindex="-1" id="{{$id}}" data-kind="function" class="Documentation-functionHeader">func {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
257+
<h3 tabindex="-1" id="{{$id}}" data-kind="function" class="Documentation-functionHeader">func {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a> {{uses_link "List Function Callers" .Name}}</h3>{{"\n"}}
258258
{{- $out := render_decl .Doc .Decl -}}
259259
{{- $out.Decl -}}
260260
{{- $out.Doc -}}
@@ -271,7 +271,7 @@ const fullTemplate = `{{- "" -}}
271271
<div class="Documentation-type">
272272
{{- $tname := .Name -}}
273273
{{- $id := safe_id .Name -}}
274-
<h3 tabindex="-1" id="{{$id}}" data-kind="type" class="Documentation-typeHeader">type {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
274+
<h3 tabindex="-1" id="{{$id}}" data-kind="type" class="Documentation-typeHeader">type {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a> {{uses_link "List Uses of This Type" .Name}}</h3>{{"\n"}}
275275
{{- $out := render_decl .Doc .Decl -}}
276276
{{- $out.Decl -}}
277277
{{- $out.Doc -}}
@@ -299,7 +299,7 @@ const fullTemplate = `{{- "" -}}
299299
{{- range .Funcs -}}
300300
<div class="Documentation-typeFunc">
301301
{{- $id := safe_id .Name -}}
302-
<h3 tabindex="-1" id="{{$id}}" data-kind="function" class="Documentation-typeFuncHeader">func {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
302+
<h3 tabindex="-1" id="{{$id}}" data-kind="function" class="Documentation-typeFuncHeader">func {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a> {{uses_link "List Function Callers" .Name}}</h3>{{"\n"}}
303303
{{- $out := render_decl .Doc .Decl -}}
304304
{{- $out.Decl -}}
305305
{{- $out.Doc -}}
@@ -312,7 +312,7 @@ const fullTemplate = `{{- "" -}}
312312
<div class="Documentation-typeMethod">
313313
{{- $name := (printf "%s.%s" $tname .Name) -}}
314314
{{- $id := (safe_id $name) -}}
315-
<h3 tabindex="-1" id="{{$id}}" data-kind="method" class="Documentation-typeMethodHeader">func ({{.Recv}}) {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
315+
<h3 tabindex="-1" id="{{$id}}" data-kind="method" class="Documentation-typeMethodHeader">func ({{.Recv}}) {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a> {{uses_link "List Method Callers" .Recv .Name}}</h3>{{"\n"}}
316316
{{- $out := render_decl .Doc .Decl -}}
317317
{{- $out.Decl -}}
318318
{{- $out.Doc -}}

internal/godoc/dochtml/template.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ var tmpl = map[string]interface{}{
4949
"render_code": (*render.Renderer)(nil).CodeHTML,
5050
"file_link": func() string { return "" },
5151
"source_link": func() string { return "" },
52+
"uses_link": func() string { return "" },
5253
"play_url": func(*doc.Example) string { return "" },
5354
"safe_id": render.SafeGoID,
5455
}

internal/godoc/render.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,17 @@ func (p *Package) Render(ctx context.Context, innerPath string, sourceInfo *sour
119119
}
120120
return sourceInfo.FileURL(path.Join(innerPath, filename))
121121
}
122+
usesLinkFunc := func(defParts []string) string {
123+
if sourceInfo == nil {
124+
return ""
125+
}
126+
return sourceInfo.UsesURL(modInfo.ModulePath, importPath, defParts)
127+
}
122128

123129
docHTML, err := dochtml.Render(ctx, p.Fset, d, dochtml.RenderOptions{
124130
FileLinkFunc: fileLinkFunc,
125131
SourceLinkFunc: sourceLinkFunc,
132+
UsesLinkFunc: usesLinkFunc,
126133
ModInfo: modInfo,
127134
Limit: int64(MaxDocumentationHTML),
128135
})

internal/source/source.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"encoding/json"
2626
"fmt"
2727
"net/http"
28+
"net/url"
2829
"path"
2930
"regexp"
3031
"strconv"
@@ -112,6 +113,37 @@ func (i *Info) LineURL(pathname string, line int) string {
112113
})
113114
}
114115

116+
// UsesURL returns a URL redirecting to Sourcegraph site showing the usage for a particular component of the code.
117+
func (i *Info) UsesURL(modulePath string, importPath string, defParts []string) string {
118+
sourcegraphBaseURL := "https://sourcegraph.com/-/godoc/refs?"
119+
120+
var def string
121+
switch len(defParts) {
122+
case 1:
123+
def = defParts[0]
124+
125+
case 2:
126+
typeName, methodName := defParts[0], defParts[1]
127+
typeName = strings.TrimPrefix(typeName, "*")
128+
def = typeName + "/" + methodName
129+
130+
default:
131+
panic(fmt.Errorf("%v defParts, want 1 or 2", len(defParts)))
132+
}
133+
134+
repo := strings.TrimPrefix(modulePath, "https://")
135+
pkg := strings.TrimPrefix(importPath, "https://")
136+
137+
q := url.Values{
138+
"repo": []string{repo},
139+
"pkg": []string{pkg},
140+
"def": []string{def},
141+
"source": []string{"pkgsite"},
142+
}
143+
144+
return sourcegraphBaseURL + q.Encode()
145+
}
146+
115147
// RawURL returns a URL referring to the raw contents of a file relative to the
116148
// module's home directory.
117149
func (i *Info) RawURL(pathname string) string {

0 commit comments

Comments
 (0)