Skip to content

Commit

Permalink
x/pkgsite: add sourcegraph redirection link for types, functions, and…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
joshuabezaleel committed Oct 9, 2020
1 parent aa97eaf commit 049a7d9
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 8 deletions.
7 changes: 7 additions & 0 deletions content/static/css/stylesheet.css
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 14 additions & 0 deletions internal/godoc/dochtml/dochtml.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 == "" &&
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions internal/godoc/dochtml/dochtml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions internal/godoc/dochtml/internal/render/idents.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,14 @@ type Link struct {
var LinkTemplate = template.Must(template.New("link").Parse(
`<a {{with .Class}}class="{{.}}" {{end}}href="{{.Href}}">{{.Text}}</a>`))

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(
`<a class="{{.Class}}" title="{{.Title}}" href="{{.Href}}">{{.Text}}</a>`))

// 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) {
Expand Down
8 changes: 4 additions & 4 deletions internal/godoc/dochtml/legacy_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const legacyTmplBody = `
{{- range .Funcs -}}
<div class="Documentation-function">
{{- $id := safe_id .Name -}}
<h3 tabindex="-1" id="{{$id}}" data-kind="function" class="Documentation-functionHeader">func {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
<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"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
Expand All @@ -107,7 +107,7 @@ const legacyTmplBody = `
<div class="Documentation-type">
{{- $tname := .Name -}}
{{- $id := safe_id .Name -}}
<h3 tabindex="-1" id="{{$id}}" data-kind="type" class="Documentation-typeHeader">type {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
<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"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
Expand Down Expand Up @@ -135,7 +135,7 @@ const legacyTmplBody = `
{{- range .Funcs -}}
<div class="Documentation-typeFunc">
{{- $id := safe_id .Name -}}
<h3 tabindex="-1" id="{{$id}}" data-kind="function" class="Documentation-typeFuncHeader">func {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
<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"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
Expand All @@ -148,7 +148,7 @@ const legacyTmplBody = `
<div class="Documentation-typeMethod">
{{- $name := (printf "%s.%s" $tname .Name) -}}
{{- $id := (safe_id $name) -}}
<h3 tabindex="-1" id="{{$id}}" data-kind="method" class="Documentation-typeMethodHeader">func ({{.Recv}}) {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
<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"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
Expand Down
8 changes: 4 additions & 4 deletions internal/godoc/dochtml/legacy_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ const fullTemplate = `{{- "" -}}
{{- range .Funcs -}}
<div class="Documentation-function">
{{- $id := safe_id .Name -}}
<h3 tabindex="-1" id="{{$id}}" data-kind="function" class="Documentation-functionHeader">func {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
<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"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
Expand All @@ -271,7 +271,7 @@ const fullTemplate = `{{- "" -}}
<div class="Documentation-type">
{{- $tname := .Name -}}
{{- $id := safe_id .Name -}}
<h3 tabindex="-1" id="{{$id}}" data-kind="type" class="Documentation-typeHeader">type {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
<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"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
Expand Down Expand Up @@ -299,7 +299,7 @@ const fullTemplate = `{{- "" -}}
{{- range .Funcs -}}
<div class="Documentation-typeFunc">
{{- $id := safe_id .Name -}}
<h3 tabindex="-1" id="{{$id}}" data-kind="function" class="Documentation-typeFuncHeader">func {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
<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"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
Expand All @@ -312,7 +312,7 @@ const fullTemplate = `{{- "" -}}
<div class="Documentation-typeMethod">
{{- $name := (printf "%s.%s" $tname .Name) -}}
{{- $id := (safe_id $name) -}}
<h3 tabindex="-1" id="{{$id}}" data-kind="method" class="Documentation-typeMethodHeader">func ({{.Recv}}) {{source_link .Name .Decl}} <a href="#{{$id}}">¶</a></h3>{{"\n"}}
<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"}}
{{- $out := render_decl .Doc .Decl -}}
{{- $out.Decl -}}
{{- $out.Doc -}}
Expand Down
1 change: 1 addition & 0 deletions internal/godoc/dochtml/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
7 changes: 7 additions & 0 deletions internal/godoc/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
})
Expand Down
32 changes: 32 additions & 0 deletions internal/source/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"path"
"regexp"
"strconv"
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 049a7d9

Please sign in to comment.