@@ -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
2629import (
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
120184var 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
308372fmt .Fprintf (& buf , "<h2>Index</h2>\n " )
@@ -366,7 +430,7 @@ function httpGET(url) {
366430fmt .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}
372436fmt .Fprintf (& buf , "<h2 id='hdr-Constants'>Constants</h2>\n " )
@@ -397,7 +461,7 @@ function httpGET(url) {
397461nodeHTML (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}
403467funcs (docpkg .Funcs )
@@ -417,7 +481,7 @@ function httpGET(url) {
417481fmt .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
423487values (doctype .Consts ) // constants of type T
@@ -437,7 +501,7 @@ function httpGET(url) {
437501
438502// comment (if any)
439503fmt .Fprintf (& buf , "<div class='comment'>%s</div>\n " ,
440- docpkg . HTML (docmethod .Doc ))
504+ docHTML (docmethod .Doc ))
441505}
442506}
443507
0 commit comments