Skip to content

Commit 53d35a5

Browse files
adonovangopherbot
authored andcommitted
gopls/internal/golang: RenderPackageDoc: fix doc links
Previously, a variety of links generated by the go/doc/comment package were not valid. This CL configures the parser and printer hooks to query the cache.Package representation, and now generates valid crosslinks for all references. Tested interactively on a wide variety of cross links. Change-Id: Iaf1fffe52ea96e3be5df78227cf8395006caa59a Reviewed-on: https://go-review.googlesource.com/c/tools/+/575377 Auto-Submit: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 222cdde commit 53d35a5

File tree

1 file changed

+75
-11
lines changed

1 file changed

+75
-11
lines changed

gopls/internal/golang/pkgdoc.go

+75-11
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ package golang
2222
// - add push notifications such as didChange -> reload.
2323
// - there appears to be a maximum file size beyond which the
2424
// "source.doc" code action is not offered. Remove that.
25+
// - modify JS httpGET function to give a transient visual indication
26+
// when clicking a source link that the editor is being navigated
27+
// (in case it doesn't raise itself, like VS Code).
2528

2629
import (
2730
"bytes"
@@ -33,6 +36,7 @@ import (
3336
"go/token"
3437
"go/types"
3538
"html"
39+
"log"
3640
"path/filepath"
3741

3842
"golang.org/x/tools/gopls/internal/cache"
@@ -41,6 +45,7 @@ import (
4145
"golang.org/x/tools/gopls/internal/util/bug"
4246
"golang.org/x/tools/gopls/internal/util/safetoken"
4347
"golang.org/x/tools/gopls/internal/util/slices"
48+
"golang.org/x/tools/gopls/internal/util/typesutil"
4449
"golang.org/x/tools/internal/typesinternal"
4550
)
4651

@@ -108,13 +113,72 @@ func RenderPackageDoc(pkg *cache.Package, posURL func(filename string, line, col
108113
})
109114
}
110115

111-
// Ensure doc links (e.g. "[fmt.Println]") become valid links.
112-
docpkg.Printer().DocLinkURL = func(link *comment.DocLink) string {
113-
fragment := link.Name
114-
if link.Recv == "" {
115-
fragment = link.Recv + "." + link.Name
116+
var docHTML func(comment string) []byte
117+
{
118+
// Adapt doc comment parser and printer
119+
// to our representation of Go packages
120+
// so that doc links (e.g. "[fmt.Println]")
121+
// become valid links.
122+
123+
printer := docpkg.Printer()
124+
printer.DocLinkURL = func(link *comment.DocLink) string {
125+
path := pkg.Metadata().PkgPath
126+
if link.ImportPath != "" {
127+
path = PackagePath(link.ImportPath)
128+
}
129+
fragment := link.Name
130+
if link.Recv != "" {
131+
fragment = link.Recv + "." + link.Name
132+
}
133+
return pkgURL(path, fragment)
134+
}
135+
parser := docpkg.Parser()
136+
parser.LookupPackage = func(name string) (importPath string, ok bool) {
137+
// Ambiguous: different files in the same
138+
// package may have different import mappings,
139+
// but the hook doesn't provide the file context.
140+
// TODO(adonovan): conspire with docHTML to
141+
// pass the doc comment's enclosing file through
142+
// a shared variable, so that we can compute
143+
// the correct per-file mapping.
144+
//
145+
// TODO(adonovan): check for PkgName.Name
146+
// matches, but also check for
147+
// PkgName.Imported.Namer matches, since some
148+
// packages are typically imported under a
149+
// non-default name (e.g. pathpkg "path") but
150+
// may be referred to in doc links using their
151+
// canonical name.
152+
for _, f := range pkg.Syntax() {
153+
for _, imp := range f.Imports {
154+
pkgName, ok := typesutil.ImportedPkgName(pkg.TypesInfo(), imp)
155+
if ok && pkgName.Name() == name {
156+
return pkgName.Imported().Path(), true
157+
}
158+
}
159+
}
160+
return "", false
161+
}
162+
parser.LookupSym = func(recv, name string) (ok bool) {
163+
defer func() {
164+
log.Printf("LookupSym %q %q = %t ", recv, name, ok)
165+
}()
166+
// package-level decl?
167+
if recv == "" {
168+
return pkg.Types().Scope().Lookup(name) != nil
169+
}
170+
171+
// method?
172+
tname, ok := pkg.Types().Scope().Lookup(recv).(*types.TypeName)
173+
if !ok {
174+
return false
175+
}
176+
m, _, _ := types.LookupFieldOrMethod(tname.Type(), true, pkg.Types(), name)
177+
return is[*types.Func](m)
178+
}
179+
docHTML = func(comment string) []byte {
180+
return printer.HTML(parser.Parse(comment))
116181
}
117-
return pkgURL(PackagePath(link.ImportPath), fragment)
118182
}
119183

120184
var buf bytes.Buffer
@@ -302,7 +366,7 @@ function httpGET(url) {
302366
"https://pkg.go.dev/"+string(pkg.Types().Path()))
303367

304368
// package doc
305-
fmt.Fprintf(&buf, "<div class='comment'>%s</div>\n", docpkg.HTML(docpkg.Doc))
369+
fmt.Fprintf(&buf, "<div class='comment'>%s</div>\n", docHTML(docpkg.Doc))
306370

307371
// symbol index
308372
fmt.Fprintf(&buf, "<h2>Index</h2>\n")
@@ -366,7 +430,7 @@ function httpGET(url) {
366430
fmt.Fprintf(&buf, "<pre class='code'>%s</pre>\n", nodeHTML(&decl2))
367431

368432
// comment (if any)
369-
fmt.Fprintf(&buf, "<div class='comment'>%s</div>\n", docpkg.HTML(v.Doc))
433+
fmt.Fprintf(&buf, "<div class='comment'>%s</div>\n", docHTML(v.Doc))
370434
}
371435
}
372436
fmt.Fprintf(&buf, "<h2 id='hdr-Constants'>Constants</h2>\n")
@@ -397,7 +461,7 @@ function httpGET(url) {
397461
nodeHTML(docfn.Decl.Type))
398462

399463
// comment (if any)
400-
fmt.Fprintf(&buf, "<div class='comment'>%s</div>\n", docpkg.HTML(docfn.Doc))
464+
fmt.Fprintf(&buf, "<div class='comment'>%s</div>\n", docHTML(docfn.Doc))
401465
}
402466
}
403467
funcs(docpkg.Funcs)
@@ -417,7 +481,7 @@ function httpGET(url) {
417481
fmt.Fprintf(&buf, "<pre class='code'>%s</pre>\n", nodeHTML(&decl2))
418482

419483
// comment (if any)
420-
fmt.Fprintf(&buf, "<div class='comment'>%s</div>\n", docpkg.HTML(doctype.Doc))
484+
fmt.Fprintf(&buf, "<div class='comment'>%s</div>\n", docHTML(doctype.Doc))
421485

422486
// subelements
423487
values(doctype.Consts) // constants of type T
@@ -437,7 +501,7 @@ function httpGET(url) {
437501

438502
// comment (if any)
439503
fmt.Fprintf(&buf, "<div class='comment'>%s</div>\n",
440-
docpkg.HTML(docmethod.Doc))
504+
docHTML(docmethod.Doc))
441505
}
442506
}
443507

0 commit comments

Comments
 (0)