Skip to content

Commit 27d26b0

Browse files
committed
gopls: added method docs to hover info
The change also includes a jump to definition feature for all methods.The change is quite fail-safe, such that if it fails in any way, it will still display the hover info without the method docs(as before). The change is localised to the the gopls/internal/golang/hover.go file, in function hover and formatHover. I should also mention that it does not alter the method displayed or it order, it just add the method docs (if any) and a jump to page source url to the hover info. Fixes golang/go#66721 Fixes golang/go#66721 Signed-off-by: tobigiwa <[email protected]>
1 parent c7b6b8d commit 27d26b0

File tree

1 file changed

+74
-3
lines changed

1 file changed

+74
-3
lines changed

gopls/internal/golang/hover.go

+74-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"go/constant"
1414
"go/doc"
1515
"go/format"
16+
"go/parser"
1617
"go/token"
1718
"go/types"
1819
"io/fs"
@@ -398,8 +399,15 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
398399
// including those that require a pointer receiver,
399400
// and those promoted from embedded struct fields or
400401
// embedded interfaces.
401-
var b strings.Builder
402-
for _, m := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
402+
var (
403+
b strings.Builder
404+
// methodDocs is a map of the method name and it associated documentation.
405+
methodDocs = make(map[string]string)
406+
)
407+
methodSetOfHoveredType := typeutil.IntuitiveMethodSet(obj.Type(), nil)
408+
lenOfMethodSet := len(methodSetOfHoveredType)
409+
methodNameArray := make([]string, 0, lenOfMethodSet)
410+
for _, m := range methodSetOfHoveredType {
403411
if !accessibleTo(m.Obj(), pkg.Types()) {
404412
continue // inaccessible
405413
}
@@ -412,10 +420,38 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
412420

413421
// Use objectString for its prettier rendering of method receivers.
414422
b.WriteString(objectString(m.Obj(), qf, token.NoPos, nil, nil))
423+
methodNameArray = append(methodNameArray, m.Obj().Name())
424+
methodDocs[m.Obj().Name()] = ""
415425
}
416426
methods = b.String()
417427

418428
signature = typeDecl + "\n" + methods
429+
430+
// -- method doc --
431+
{
432+
if len(methodNameArray) > 0 { // if we have methods at all from methodSet
433+
fs := token.NewFileSet()
434+
if allPkgsInDir, err := parser.ParseDir(fs, declPGF.URI.Dir().Path(), nil, parser.ParseComments); err == nil {
435+
if files, ok := allPkgsInDir[obj.Pkg().Name()]; ok {
436+
for _, file := range files.Files {
437+
getMethodDoc(file, fs, ident, methodDocs)
438+
}
439+
440+
methodArray := strings.Split(methods, "\n")
441+
if m := len(methodArray); m > 0 && m == len(methodNameArray) {
442+
var d strings.Builder
443+
for i := 0; i < m; i++ {
444+
if doc, ok := methodDocs[methodNameArray[i]]; ok {
445+
d.WriteString(fmt.Sprintf("```go\n%s\n```\n%s\n", methodArray[i], doc))
446+
}
447+
}
448+
methods = d.String()
449+
}
450+
}
451+
}
452+
}
453+
}
454+
419455
} else {
420456
// Non-types
421457
if sizeOffset != "" {
@@ -1063,6 +1099,13 @@ func formatHover(h *hoverJSON, options *settings.Options) (string, error) {
10631099
return s
10641100
}
10651101

1102+
fallback := func(s string) string {
1103+
if !strings.Contains(s, "```go") {
1104+
return maybeMarkdown(s)
1105+
}
1106+
return s
1107+
}
1108+
10661109
switch options.HoverKind {
10671110
case settings.SingleLine:
10681111
return h.SingleLine, nil
@@ -1089,7 +1132,7 @@ func formatHover(h *hoverJSON, options *settings.Options) (string, error) {
10891132
maybeMarkdown(h.typeDecl),
10901133
formatDoc(h, options),
10911134
maybeMarkdown(h.promotedFields),
1092-
maybeMarkdown(h.methods),
1135+
fallback(h.methods),
10931136
formatLink(h, options),
10941137
}
10951138
if h.typeDecl != "" {
@@ -1409,3 +1452,31 @@ func computeSizeOffsetInfo(pkg *cache.Package, path []ast.Node, obj types.Object
14091452

14101453
return
14111454
}
1455+
1456+
// getMethodDoc populates the map type parameter(methodDocs) with the respective docs,
1457+
// if the key (method name) is present in the map.
1458+
func getMethodDoc(file *ast.File, fs *token.FileSet, hoveredType *ast.Ident, methodDocs map[string]string) {
1459+
ast.Inspect(file, func(n ast.Node) bool {
1460+
if fn, ok := n.(*ast.FuncDecl); ok {
1461+
if recv := fn.Recv; recv != nil { // if it is a method
1462+
if recvList := recv.List; len(recvList) == 1 { // as it should always be
1463+
methodType := types.ExprString(recvList[0].Type)
1464+
rFmethodType, _ := strings.CutPrefix(methodType, "*") // if it a pointer
1465+
1466+
if rFmethodType == hoveredType.Name {
1467+
fnPos := fn.Pos()
1468+
f := fs.File(fnPos)
1469+
filname, lineNumber := f.Name(), f.Line(fnPos)
1470+
pageSourceURL := fmt.Sprintf("[jump to page source](%s#L%d)", filname, lineNumber)
1471+
1472+
methodDocs[fn.Name.Name] = strings.TrimSpace(fn.Doc.Text()) + "\n\n" + pageSourceURL
1473+
return false
1474+
}
1475+
}
1476+
return true
1477+
}
1478+
return true
1479+
}
1480+
return true
1481+
})
1482+
}

0 commit comments

Comments
 (0)