Skip to content

Commit 2db5a34

Browse files
adonovangopherbot
authored andcommitted
gopls/internal/golang: RenderPkgDoc: navigational <select>
This CL adds a <select> element with an index of all sections in the page, following the design of pkg.go.dev. Change-Id: I41b6719e85d0ace7edb864c5b20ae1f5b8e39d1b Reviewed-on: https://go-review.googlesource.com/c/tools/+/576596 Reviewed-by: Robert Findley <[email protected]> Auto-Submit: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 11c692e commit 2db5a34

File tree

1 file changed

+112
-6
lines changed

1 file changed

+112
-6
lines changed

gopls/internal/golang/pkgdoc.go

+112-6
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"html"
3939
"log"
4040
"path/filepath"
41+
"strings"
4142

4243
"golang.org/x/tools/gopls/internal/cache"
4344
"golang.org/x/tools/gopls/internal/protocol"
@@ -181,6 +182,8 @@ func RenderPackageDoc(pkg *cache.Package, posURL func(filename string, line, col
181182
}
182183
}
183184

185+
scope := pkg.Types().Scope()
186+
184187
var buf bytes.Buffer
185188
buf.WriteString(`<!DOCTYPE html>
186189
<html>
@@ -196,6 +199,13 @@ function httpGET(url) {
196199
return false; // disable usual <a href=...> behavior
197200
}
198201
202+
window.onload = () => {
203+
// Hook up the navigation selector.
204+
document.getElementById('hdr-Selector').onchange = (e) => {
205+
window.location.href = e.target.value;
206+
};
207+
};
208+
199209
// Start a GET /hang request. If it ever completes, the server
200210
// has disconnected. Show a banner in that case.
201211
{
@@ -209,9 +219,85 @@ function httpGET(url) {
209219
</script>
210220
</head>
211221
<body>
222+
<header>
212223
<div id='disconnected'>Gopls server has terminated. Page is inactive.</div>
224+
<select id='hdr-Selector'>
225+
<optgroup label="Documentation">
226+
<option label="Overview" value="#hdr-Overview"/>
227+
<option label="Index" value="#hdr-Index"/>
228+
<option label="Constants" value="#hdr-Constants"/>
229+
<option label="Variables" value="#hdr-Variables"/>
230+
<option label="Functions" value="#hdr-Functions"/>
231+
<option label="Types" value="#hdr-Types"/>
232+
<option label="Source Files" value="#hdr-SourceFiles"/>
233+
</optgroup>
213234
`)
214235

236+
// -- header select element --
237+
238+
// option emits an <option> for the specified symbol.
239+
option := func(obj types.Object) {
240+
// Render functions/methods as "(recv) Method(p1, ..., pN)".
241+
fragment := obj.Name()
242+
243+
// format parameter names (p1, ..., pN)
244+
label := obj.Name() // for a type
245+
if fn, ok := obj.(*types.Func); ok {
246+
var buf strings.Builder
247+
sig := fn.Type().(*types.Signature)
248+
if sig.Recv() != nil {
249+
_, named := typesinternal.ReceiverNamed(sig.Recv())
250+
fmt.Fprintf(&buf, "(%s) ", sig.Recv().Name())
251+
fragment = named.Obj().Name() + "." + fn.Name()
252+
}
253+
fmt.Fprintf(&buf, "%s(", fn.Name())
254+
for i := 0; i < sig.Params().Len(); i++ {
255+
if i > 0 {
256+
buf.WriteString(", ")
257+
}
258+
buf.WriteString(sig.Params().At(i).Name())
259+
}
260+
buf.WriteByte(')')
261+
label = buf.String()
262+
}
263+
264+
fmt.Fprintf(&buf, " <option label='%s' value='#%s'/>\n", label, fragment)
265+
}
266+
267+
// index of functions
268+
fmt.Fprintf(&buf, "<optgroup label='Functions'>\n")
269+
for _, fn := range docpkg.Funcs {
270+
option(scope.Lookup(fn.Name))
271+
}
272+
fmt.Fprintf(&buf, "</optgroup>\n")
273+
274+
// index of types
275+
fmt.Fprintf(&buf, "<optgroup label='Types'>\n")
276+
for _, doctype := range docpkg.Types {
277+
option(scope.Lookup(doctype.Name))
278+
}
279+
fmt.Fprintf(&buf, "</optgroup>\n")
280+
281+
// index of constructors and methods of each type
282+
for _, doctype := range docpkg.Types {
283+
tname := scope.Lookup(doctype.Name).(*types.TypeName)
284+
if len(doctype.Funcs)+len(doctype.Methods) > 0 {
285+
fmt.Fprintf(&buf, "<optgroup label='type %s'>\n", doctype.Name)
286+
for _, docfn := range doctype.Funcs {
287+
option(scope.Lookup(docfn.Name))
288+
}
289+
for _, docmethod := range doctype.Methods {
290+
method, _, _ := types.LookupFieldOrMethod(tname.Type(), true, tname.Pkg(), docmethod.Name)
291+
option(method)
292+
}
293+
fmt.Fprintf(&buf, "</optgroup>\n")
294+
}
295+
}
296+
fmt.Fprintf(&buf, "</select>\n")
297+
fmt.Fprintf(&buf, "</header>\n")
298+
299+
// -- main element --
300+
215301
escape := html.EscapeString
216302

217303
// sourceLink returns HTML for a link to open a file in the client editor.
@@ -355,8 +441,10 @@ function httpGET(url) {
355441
return other.Name()
356442
}
357443

444+
fmt.Fprintf(&buf, "<main>\n")
445+
358446
// package name
359-
fmt.Fprintf(&buf, "<h1>Package %s</h1>\n", pkg.Types().Name())
447+
fmt.Fprintf(&buf, "<h1 id='hdr-Overview'>Package %s</h1>\n", pkg.Types().Name())
360448

361449
// import path
362450
fmt.Fprintf(&buf, "<pre class='code'>import %q</pre>\n", pkg.Types().Path())
@@ -369,15 +457,14 @@ function httpGET(url) {
369457
fmt.Fprintf(&buf, "<div class='comment'>%s</div>\n", docHTML(docpkg.Doc))
370458

371459
// symbol index
372-
fmt.Fprintf(&buf, "<h2>Index</h2>\n")
460+
fmt.Fprintf(&buf, "<h2 id='hdr-Index'>Index</h2>\n")
373461
fmt.Fprintf(&buf, "<ul>\n")
374462
if len(docpkg.Consts) > 0 {
375463
fmt.Fprintf(&buf, "<li><a href='#hdr-Constants'>Constants</a></li>\n")
376464
}
377465
if len(docpkg.Vars) > 0 {
378466
fmt.Fprintf(&buf, "<li><a href='#hdr-Variables'>Variables</a></li>\n")
379467
}
380-
scope := pkg.Types().Scope()
381468
for _, fn := range docpkg.Funcs {
382469
obj := scope.Lookup(fn.Name).(*types.Func)
383470
fmt.Fprintf(&buf, "<li><a href='#%s'>%s</a></li>\n",
@@ -447,7 +534,7 @@ function httpGET(url) {
447534
}
448535

449536
// package-level functions
450-
fmt.Fprintf(&buf, "<h2>Functions</h2>\n")
537+
fmt.Fprintf(&buf, "<h2 id='hdr-Functions'>Functions</h2>\n")
451538
// funcs emits a list of package-level functions,
452539
// possibly organized beneath the type they construct.
453540
funcs := func(funcs []*doc.Func) {
@@ -467,7 +554,7 @@ function httpGET(url) {
467554
funcs(docpkg.Funcs)
468555

469556
// types and their subelements
470-
fmt.Fprintf(&buf, "<h2>Types</h2>\n")
557+
fmt.Fprintf(&buf, "<h2 id='hdr-Types'>Types</h2>\n")
471558
for _, doctype := range docpkg.Types {
472559
tname := scope.Lookup(doctype.Name).(*types.TypeName)
473560

@@ -506,12 +593,16 @@ function httpGET(url) {
506593
}
507594

508595
// source files
509-
fmt.Fprintf(&buf, "<h2>Source files</h2>\n")
596+
fmt.Fprintf(&buf, "<h2 id='hdr-SourceFiles'>Source files</h2>\n")
510597
for _, filename := range docpkg.Filenames {
511598
fmt.Fprintf(&buf, "<div class='comment'>%s</div>\n",
512599
sourceLink(filepath.Base(filename), posURL(filename, 1, 1)))
513600
}
514601

602+
fmt.Fprintf(&buf, "</main>\n")
603+
fmt.Fprintf(&buf, "</body>\n")
604+
fmt.Fprintf(&buf, "</html>\n")
605+
515606
return buf.Bytes(), nil
516607
}
517608

@@ -621,6 +712,21 @@ a:hover > * {
621712
622713
#pkgsite { height: 1.5em; }
623714
715+
header {
716+
position: sticky;
717+
top: 0;
718+
left: 0;
719+
width: 100%;
720+
padding: 0.3em;
721+
}
722+
723+
#hdr-Selector {
724+
margin-right: 0.3em;
725+
float: right;
726+
min-width: 25em;
727+
padding: 0.3em;
728+
}
729+
624730
#disconnected {
625731
position: fixed;
626732
top: 1em;

0 commit comments

Comments
 (0)