@@ -13,6 +13,7 @@ import (
13
13
"go/constant"
14
14
"go/doc"
15
15
"go/format"
16
+ "go/parser"
16
17
"go/token"
17
18
"go/types"
18
19
"io/fs"
@@ -398,8 +399,15 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
398
399
// including those that require a pointer receiver,
399
400
// and those promoted from embedded struct fields or
400
401
// 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 {
403
411
if ! accessibleTo (m .Obj (), pkg .Types ()) {
404
412
continue // inaccessible
405
413
}
@@ -412,10 +420,38 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
412
420
413
421
// Use objectString for its prettier rendering of method receivers.
414
422
b .WriteString (objectString (m .Obj (), qf , token .NoPos , nil , nil ))
423
+ methodNameArray = append (methodNameArray , m .Obj ().Name ())
424
+ methodDocs [m .Obj ().Name ()] = ""
415
425
}
416
426
methods = b .String ()
417
427
418
428
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
+
419
455
} else {
420
456
// Non-types
421
457
if sizeOffset != "" {
@@ -1063,6 +1099,13 @@ func formatHover(h *hoverJSON, options *settings.Options) (string, error) {
1063
1099
return s
1064
1100
}
1065
1101
1102
+ fallback := func (s string ) string {
1103
+ if ! strings .Contains (s , "```go" ) {
1104
+ return maybeMarkdown (s )
1105
+ }
1106
+ return s
1107
+ }
1108
+
1066
1109
switch options .HoverKind {
1067
1110
case settings .SingleLine :
1068
1111
return h .SingleLine , nil
@@ -1089,7 +1132,7 @@ func formatHover(h *hoverJSON, options *settings.Options) (string, error) {
1089
1132
maybeMarkdown (h .typeDecl ),
1090
1133
formatDoc (h , options ),
1091
1134
maybeMarkdown (h .promotedFields ),
1092
- maybeMarkdown (h .methods ),
1135
+ fallback (h .methods ),
1093
1136
formatLink (h , options ),
1094
1137
}
1095
1138
if h .typeDecl != "" {
@@ -1409,3 +1452,31 @@ func computeSizeOffsetInfo(pkg *cache.Package, path []ast.Node, obj types.Object
1409
1452
1410
1453
return
1411
1454
}
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